From b51a457e5a7231795708e1060c1557cc3809233a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 6 Mar 2019 14:36:30 +0900 Subject: [PATCH 0001/1117] Implement sorcerer's diffcalc changes --- .../Difficulty/CatchDifficultyCalculator.cs | 2 +- .../Preprocessing/CatchDifficultyHitObject.cs | 6 +-- .../Difficulty/Skills/Movement.cs | 46 ++++++++++++++----- 3 files changed, 38 insertions(+), 16 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 8cfda5d532..2cb71428b9 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty { public class CatchDifficultyCalculator : DifficultyCalculator { - private const double star_scaling_factor = 0.145; + private const double star_scaling_factor = 0.15; protected override int SectionLength => 750; diff --git a/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs b/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs index 24e526ed19..f0c68e4392 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing public readonly float LastNormalizedPosition; /// - /// Milliseconds elapsed since the start time of the previous , with a minimum of 25ms. + /// Milliseconds elapsed since the start time of the previous , with a minimum of 40ms. /// public readonly double StrainTime; @@ -34,8 +34,8 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing NormalizedPosition = BaseObject.X * CatchPlayfield.BASE_WIDTH * scalingFactor; LastNormalizedPosition = LastObject.X * CatchPlayfield.BASE_WIDTH * scalingFactor; - // Every strain interval is hard capped at the equivalent of 600 BPM streaming speed as a safety measure - StrainTime = Math.Max(25, DeltaTime); + // Every strain interval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure + StrainTime = Math.Max(40, DeltaTime); } } } diff --git a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs index d146153294..b1b5ba0312 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs @@ -14,7 +14,8 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills { private const float absolute_player_positioning_error = 16f; private const float normalized_hitobject_radius = 41.0f; - private const double direction_change_bonus = 12.5; + private const double direction_change_bonus = 9.5; + private const double antiflow_bonus = 25.0; protected override double SkillMultiplier => 850; protected override double StrainDecayBase => 0.2; @@ -23,6 +24,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills private float? lastPlayerPosition; private float lastDistanceMoved; + private double lastStrainTime; protected override double StrainValueOf(DifficultyHitObject current) { @@ -39,8 +41,11 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills float distanceMoved = playerPosition - lastPlayerPosition.Value; - double distanceAddition = Math.Pow(Math.Abs(distanceMoved), 1.3) / 500; - double sqrtStrain = Math.Sqrt(catchCurrent.StrainTime); + // Reduce speed scaling + double weightedStrainTime = catchCurrent.StrainTime + 20; + + double distanceAddition = Math.Pow(Math.Abs(distanceMoved), 1.3) / 600; + double sqrtStrain = Math.Sqrt(weightedStrainTime); double bonus = 0; @@ -53,33 +58,50 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills distanceAddition += direction_change_bonus / sqrtStrain * bonusFactor; - // Bonus for tougher direction switches and "almost" hyperdashes at this point - if (catchCurrent.LastObject.DistanceToHyperDash <= 10 / CatchPlayfield.BASE_WIDTH) - bonus = 0.3 * bonusFactor; + // Direction changes after jumps (antiflow) are harder + double antiflowBonusFactor = Math.Min(Math.Sqrt(Math.Abs(distanceMoved)) / 10, 1); + + distanceAddition += (antiflow_bonus / sqrtStrain) * (Math.Sqrt(Math.Abs(lastDistanceMoved)) / (lastStrainTime / 40 + 10.0)) * antiflowBonusFactor; } // Base bonus for every movement, giving some weight to streams. - distanceAddition += 7.5 * Math.Min(Math.Abs(distanceMoved), normalized_hitobject_radius * 2) / (normalized_hitobject_radius * 6) / sqrtStrain; + distanceAddition += 10.0 * Math.Min(Math.Abs(distanceMoved), normalized_hitobject_radius * 2) / (normalized_hitobject_radius * 6) / sqrtStrain; } - // Bonus for "almost" hyperdashes at corner points - if (catchCurrent.LastObject.DistanceToHyperDash <= 10.0f / CatchPlayfield.BASE_WIDTH) + // Big bonus for edge hyperdashes + if (catchCurrent.LastObject.DistanceToHyperDash <= 14.0f / CatchPlayfield.BASE_WIDTH) { if (!catchCurrent.LastObject.HyperDash) - bonus += 1.0; + bonus += 5.0; else { // After a hyperdash we ARE in the correct position. Always! playerPosition = catchCurrent.NormalizedPosition; } - distanceAddition *= 1.0 + bonus * ((10 - catchCurrent.LastObject.DistanceToHyperDash * CatchPlayfield.BASE_WIDTH) / 10); + distanceAddition *= 1.0 + bonus * (14 - catchCurrent.LastObject.DistanceToHyperDash * CatchPlayfield.BASE_WIDTH) / 14 * (Math.Min(catchCurrent.StrainTime, 180) / 180); // Edge dashes are easier at lower ms values + } + + // Prevent wide, dense stacks of notes which fit on the catcher from greatly increasing SR + if (Math.Abs(distanceMoved) > 0.1) + { + if (Math.Abs(lastDistanceMoved) > 0.1 && Math.Sign(distanceMoved) != Math.Sign(lastDistanceMoved)) + { + if (Math.Abs(distanceMoved) <= (CatcherArea.CATCHER_SIZE) && Math.Abs(lastDistanceMoved) == Math.Abs(distanceMoved)) + { + if (catchCurrent.StrainTime <= 80 && lastStrainTime == catchCurrent.StrainTime) + { + distanceAddition *= Math.Max(((catchCurrent.StrainTime / 80) - 0.75) * 4, 0); + } + } + } } lastPlayerPosition = playerPosition; lastDistanceMoved = distanceMoved; + lastStrainTime = catchCurrent.StrainTime; - return distanceAddition / catchCurrent.StrainTime; + return distanceAddition / weightedStrainTime; } } } From 24fb25f1cdc770ecb7c016a8fba472c00507484d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 14 Mar 2019 23:39:45 +0900 Subject: [PATCH 0002/1117] Use fresh mods for each difficulty calculation --- .../Difficulty/CatchDifficultyCalculator.cs | 10 +++--- .../Difficulty/ManiaDifficultyCalculator.cs | 33 ++++++++++--------- .../Difficulty/OsuDifficultyCalculator.cs | 10 +++--- .../Difficulty/TaikoDifficultyCalculator.cs | 11 ++++--- ...DifficultyAdjustmentModCombinationsTest.cs | 14 ++++---- .../Difficulty/DifficultyCalculator.cs | 10 +++--- 6 files changed, 46 insertions(+), 42 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index f3b88bd928..d73ee19d41 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -90,12 +90,12 @@ namespace osu.Game.Rulesets.Catch.Difficulty new Movement(), }; - protected override Mod[] DifficultyAdjustmentMods => new Mod[] + protected override Type[] DifficultyAdjustmentMods => new[] { - new CatchModDoubleTime(), - new CatchModHalfTime(), - new CatchModHardRock(), - new CatchModEasy(), + typeof(CatchModDoubleTime), + typeof(CatchModHalfTime), + typeof(CatchModHardRock), + typeof(CatchModEasy), }; } } diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs index 59fed1031f..bff3bfdb23 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.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.Game.Beatmaps; @@ -91,33 +92,33 @@ namespace osu.Game.Rulesets.Mania.Difficulty return skills.ToArray(); } - protected override Mod[] DifficultyAdjustmentMods + protected override Type[] DifficultyAdjustmentMods { get { - var mods = new Mod[] + var mods = new[] { - new ManiaModDoubleTime(), - new ManiaModHalfTime(), - new ManiaModEasy(), - new ManiaModHardRock(), + typeof(ManiaModDoubleTime), + typeof(ManiaModHalfTime), + typeof(ManiaModEasy), + typeof(ManiaModHardRock) }; if (isForCurrentRuleset) return mods; // if we are a convert, we can be played in any key mod. - return mods.Concat(new Mod[] + return mods.Concat(new[] { - new ManiaModKey1(), - new ManiaModKey2(), - new ManiaModKey3(), - new ManiaModKey4(), - new ManiaModKey5(), - new ManiaModKey6(), - new ManiaModKey7(), - new ManiaModKey8(), - new ManiaModKey9(), + typeof(ManiaModKey1), + typeof(ManiaModKey2), + typeof(ManiaModKey3), + typeof(ManiaModKey4), + typeof(ManiaModKey5), + typeof(ManiaModKey6), + typeof(ManiaModKey7), + typeof(ManiaModKey8), + typeof(ManiaModKey9), }).ToArray(); } } diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index e2a1542574..9c44eb6f97 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -74,12 +74,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty new Speed() }; - protected override Mod[] DifficultyAdjustmentMods => new Mod[] + protected override Type[] DifficultyAdjustmentMods => new[] { - new OsuModDoubleTime(), - new OsuModHalfTime(), - new OsuModEasy(), - new OsuModHardRock(), + typeof(OsuModDoubleTime), + typeof(OsuModHalfTime), + typeof(OsuModEasy), + typeof(OsuModHardRock), }; } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index 685ad9949b..ad1fb4c2e5 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.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.Game.Beatmaps; @@ -47,12 +48,12 @@ namespace osu.Game.Rulesets.Taiko.Difficulty protected override Skill[] CreateSkills(IBeatmap beatmap) => new Skill[] { new Strain() }; - protected override Mod[] DifficultyAdjustmentMods => new Mod[] + protected override Type[] DifficultyAdjustmentMods => new[] { - new TaikoModDoubleTime(), - new TaikoModHalfTime(), - new TaikoModEasy(), - new TaikoModHardRock(), + typeof(TaikoModDoubleTime), + typeof(TaikoModHalfTime), + typeof(TaikoModEasy), + typeof(TaikoModHardRock), }; } } diff --git a/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs b/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs index 760a033aff..3bce6fedbb 100644 --- a/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs +++ b/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs @@ -27,7 +27,7 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestSingleMod() { - var combinations = new TestLegacyDifficultyCalculator(new ModA()).CreateDifficultyAdjustmentModCombinations(); + var combinations = new TestLegacyDifficultyCalculator(typeof(ModA)).CreateDifficultyAdjustmentModCombinations(); Assert.AreEqual(2, combinations.Length); Assert.IsTrue(combinations[0] is ModNoMod); @@ -37,7 +37,7 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestDoubleMod() { - var combinations = new TestLegacyDifficultyCalculator(new ModA(), new ModB()).CreateDifficultyAdjustmentModCombinations(); + var combinations = new TestLegacyDifficultyCalculator(typeof(ModA), typeof(ModB)).CreateDifficultyAdjustmentModCombinations(); Assert.AreEqual(4, combinations.Length); Assert.IsTrue(combinations[0] is ModNoMod); @@ -52,7 +52,7 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestIncompatibleMods() { - var combinations = new TestLegacyDifficultyCalculator(new ModA(), new ModIncompatibleWithA()).CreateDifficultyAdjustmentModCombinations(); + var combinations = new TestLegacyDifficultyCalculator(typeof(ModA), typeof(ModIncompatibleWithA)).CreateDifficultyAdjustmentModCombinations(); Assert.AreEqual(3, combinations.Length); Assert.IsTrue(combinations[0] is ModNoMod); @@ -63,7 +63,7 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestDoubleIncompatibleMods() { - var combinations = new TestLegacyDifficultyCalculator(new ModA(), new ModB(), new ModIncompatibleWithA(), new ModIncompatibleWithAAndB()).CreateDifficultyAdjustmentModCombinations(); + var combinations = new TestLegacyDifficultyCalculator(typeof(ModA), typeof(ModB), typeof(ModIncompatibleWithA), typeof(ModIncompatibleWithAAndB)).CreateDifficultyAdjustmentModCombinations(); Assert.AreEqual(8, combinations.Length); Assert.IsTrue(combinations[0] is ModNoMod); @@ -86,7 +86,7 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestIncompatibleThroughBaseType() { - var combinations = new TestLegacyDifficultyCalculator(new ModAofA(), new ModIncompatibleWithAofA()).CreateDifficultyAdjustmentModCombinations(); + var combinations = new TestLegacyDifficultyCalculator(typeof(ModAofA), typeof(ModIncompatibleWithAofA)).CreateDifficultyAdjustmentModCombinations(); Assert.AreEqual(3, combinations.Length); Assert.IsTrue(combinations[0] is ModNoMod); @@ -141,13 +141,13 @@ namespace osu.Game.Tests.NonVisual private class TestLegacyDifficultyCalculator : DifficultyCalculator { - public TestLegacyDifficultyCalculator(params Mod[] mods) + public TestLegacyDifficultyCalculator(params Type[] mods) : base(null, null) { DifficultyAdjustmentMods = mods; } - protected override Mod[] DifficultyAdjustmentMods { get; } + protected override Type[] DifficultyAdjustmentMods { get; } protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) { diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index db8bdde6bb..47ffa48b91 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -106,7 +106,7 @@ namespace osu.Game.Rulesets.Difficulty { return createDifficultyAdjustmentModCombinations(Enumerable.Empty(), DifficultyAdjustmentMods).ToArray(); - IEnumerable createDifficultyAdjustmentModCombinations(IEnumerable currentSet, Mod[] adjustmentSet, int currentSetCount = 0, int adjustmentSetStart = 0) + IEnumerable createDifficultyAdjustmentModCombinations(IEnumerable currentSet, Type[] adjustmentSet, int currentSetCount = 0, int adjustmentSetStart = 0) { switch (currentSetCount) { @@ -129,12 +129,14 @@ namespace osu.Game.Rulesets.Difficulty // combinations in further recursions, so a moving subset is used to eliminate this effect for (int i = adjustmentSetStart; i < adjustmentSet.Length; i++) { - var adjustmentMod = adjustmentSet[i]; + var adjustmentMod = createMod(); if (currentSet.Any(c => c.IncompatibleMods.Any(m => m.IsInstanceOfType(adjustmentMod)))) continue; - foreach (var combo in createDifficultyAdjustmentModCombinations(currentSet.Append(adjustmentMod), adjustmentSet, currentSetCount + 1, i + 1)) + foreach (var combo in createDifficultyAdjustmentModCombinations(currentSet.Append(createMod()), adjustmentSet, currentSetCount + 1, i + 1)) yield return combo; + + Mod createMod() => (Mod)Activator.CreateInstance(adjustmentSet[i]); } } } @@ -142,7 +144,7 @@ namespace osu.Game.Rulesets.Difficulty /// /// Retrieves all s which adjust the difficulty. /// - protected virtual Mod[] DifficultyAdjustmentMods => Array.Empty(); + protected virtual Type[] DifficultyAdjustmentMods => Array.Empty(); /// /// Creates to describe beatmap's calculated difficulty. From f959a2ee373f13a998bb8e752bc42d614ae12cd3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 16 Mar 2019 10:12:05 +0900 Subject: [PATCH 0003/1117] Update antiflow bonus --- osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs index b1b5ba0312..d06813f160 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs @@ -59,9 +59,9 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills distanceAddition += direction_change_bonus / sqrtStrain * bonusFactor; // Direction changes after jumps (antiflow) are harder - double antiflowBonusFactor = Math.Min(Math.Sqrt(Math.Abs(distanceMoved)) / 10, 1); + double antiflowBonusFactor = Math.Min(Math.Abs(distanceMoved) / 70, 1); - distanceAddition += (antiflow_bonus / sqrtStrain) * (Math.Sqrt(Math.Abs(lastDistanceMoved)) / (lastStrainTime / 40 + 10.0)) * antiflowBonusFactor; + distanceAddition += (antiflow_bonus / (catchCurrent.StrainTime / 40 + 10)) * (Math.Sqrt(Math.Abs(lastDistanceMoved)) / Math.Sqrt(lastStrainTime + 20)) * antiflowBonusFactor; } // Base bonus for every movement, giving some weight to streams. From 9ae6cde837470d8e5708788bd224631f82af8243 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 20 Mar 2019 12:14:26 +0900 Subject: [PATCH 0004/1117] Nerf back-and-forth hyperdash chains --- .../Difficulty/Skills/Movement.cs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs index d06813f160..d8e359bb48 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs @@ -15,9 +15,9 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills private const float absolute_player_positioning_error = 16f; private const float normalized_hitobject_radius = 41.0f; private const double direction_change_bonus = 9.5; - private const double antiflow_bonus = 25.0; + private const double antiflow_bonus = 26.0; - protected override double SkillMultiplier => 850; + protected override double SkillMultiplier => 860; protected override double StrainDecayBase => 0.2; protected override double DecayWeight => 0.94; @@ -25,6 +25,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills private float? lastPlayerPosition; private float lastDistanceMoved; private double lastStrainTime; + private bool lastHyperdash; protected override double StrainValueOf(DifficultyHitObject current) { @@ -44,7 +45,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills // Reduce speed scaling double weightedStrainTime = catchCurrent.StrainTime + 20; - double distanceAddition = Math.Pow(Math.Abs(distanceMoved), 1.3) / 600; + double distanceAddition = Math.Pow(Math.Abs(distanceMoved), 1.2) / 340; double sqrtStrain = Math.Sqrt(weightedStrainTime); double bonus = 0; @@ -61,7 +62,14 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills // Direction changes after jumps (antiflow) are harder double antiflowBonusFactor = Math.Min(Math.Abs(distanceMoved) / 70, 1); - distanceAddition += (antiflow_bonus / (catchCurrent.StrainTime / 40 + 10)) * (Math.Sqrt(Math.Abs(lastDistanceMoved)) / Math.Sqrt(lastStrainTime + 20)) * antiflowBonusFactor; + distanceAddition += (antiflow_bonus / (catchCurrent.StrainTime / 17.5 + 10)) * (Math.Sqrt(Math.Abs(lastDistanceMoved)) / Math.Sqrt(lastStrainTime + 25)) * antiflowBonusFactor; + + // Reduce strain slightly for Hyperdash chains + if (catchCurrent.LastObject.HyperDash && lastHyperdash) + distanceAddition *= 0.95; + + if (catchCurrent.LastObject.DistanceToHyperDash <= 14.0f / CatchPlayfield.BASE_WIDTH && !catchCurrent.LastObject.HyperDash) + bonus += 3.0; } // Base bonus for every movement, giving some weight to streams. @@ -100,6 +108,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills lastPlayerPosition = playerPosition; lastDistanceMoved = distanceMoved; lastStrainTime = catchCurrent.StrainTime; + lastHyperdash = catchCurrent.LastObject.HyperDash; return distanceAddition / weightedStrainTime; } From 9f12a36598f504539644674a89a33f370f9ef3c5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 20 Mar 2019 12:14:53 +0900 Subject: [PATCH 0005/1117] Buff slower edge dashes, nerf faster ones --- osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs index d8e359bb48..d06ab2f928 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills { private const float absolute_player_positioning_error = 16f; private const float normalized_hitobject_radius = 41.0f; - private const double direction_change_bonus = 9.5; + private const double direction_change_bonus = 9.8; private const double antiflow_bonus = 26.0; protected override double SkillMultiplier => 860; @@ -76,18 +76,18 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills distanceAddition += 10.0 * Math.Min(Math.Abs(distanceMoved), normalized_hitobject_radius * 2) / (normalized_hitobject_radius * 6) / sqrtStrain; } - // Big bonus for edge hyperdashes + // Big bonus for edge dashes if (catchCurrent.LastObject.DistanceToHyperDash <= 14.0f / CatchPlayfield.BASE_WIDTH) { if (!catchCurrent.LastObject.HyperDash) - bonus += 5.0; + bonus += 4.5; else { // After a hyperdash we ARE in the correct position. Always! playerPosition = catchCurrent.NormalizedPosition; } - distanceAddition *= 1.0 + bonus * (14 - catchCurrent.LastObject.DistanceToHyperDash * CatchPlayfield.BASE_WIDTH) / 14 * (Math.Min(catchCurrent.StrainTime, 180) / 180); // Edge dashes are easier at lower ms values + distanceAddition *= 1.0 + bonus * (14.0f - catchCurrent.LastObject.DistanceToHyperDash * CatchPlayfield.BASE_WIDTH) / 14.0f * (Math.Min(catchCurrent.StrainTime, 250) / 250); // Edge dashes are easier at lower ms values } // Prevent wide, dense stacks of notes which fit on the catcher from greatly increasing SR From 839dd7343f1cc6411077616ce0b2965e29130584 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 23 Mar 2019 15:57:22 +0900 Subject: [PATCH 0006/1117] Revert "Use fresh mods for each difficulty calculation" This reverts commit 24fb25f1cdc770ecb7c016a8fba472c00507484d. --- .../Difficulty/CatchDifficultyCalculator.cs | 10 +++--- .../Difficulty/ManiaDifficultyCalculator.cs | 33 +++++++++---------- .../Difficulty/OsuDifficultyCalculator.cs | 10 +++--- .../Difficulty/TaikoDifficultyCalculator.cs | 11 +++---- ...DifficultyAdjustmentModCombinationsTest.cs | 14 ++++---- .../Difficulty/DifficultyCalculator.cs | 10 +++--- 6 files changed, 42 insertions(+), 46 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 289deab8cd..47e2bc5259 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -91,12 +91,12 @@ namespace osu.Game.Rulesets.Catch.Difficulty new Movement(), }; - protected override Type[] DifficultyAdjustmentMods => new[] + protected override Mod[] DifficultyAdjustmentMods => new Mod[] { - typeof(CatchModDoubleTime), - typeof(CatchModHalfTime), - typeof(CatchModHardRock), - typeof(CatchModEasy), + new CatchModDoubleTime(), + new CatchModHalfTime(), + new CatchModHardRock(), + new CatchModEasy(), }; } } diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs index bff3bfdb23..59fed1031f 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.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.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; @@ -92,33 +91,33 @@ namespace osu.Game.Rulesets.Mania.Difficulty return skills.ToArray(); } - protected override Type[] DifficultyAdjustmentMods + protected override Mod[] DifficultyAdjustmentMods { get { - var mods = new[] + var mods = new Mod[] { - typeof(ManiaModDoubleTime), - typeof(ManiaModHalfTime), - typeof(ManiaModEasy), - typeof(ManiaModHardRock) + new ManiaModDoubleTime(), + new ManiaModHalfTime(), + new ManiaModEasy(), + new ManiaModHardRock(), }; if (isForCurrentRuleset) return mods; // if we are a convert, we can be played in any key mod. - return mods.Concat(new[] + return mods.Concat(new Mod[] { - typeof(ManiaModKey1), - typeof(ManiaModKey2), - typeof(ManiaModKey3), - typeof(ManiaModKey4), - typeof(ManiaModKey5), - typeof(ManiaModKey6), - typeof(ManiaModKey7), - typeof(ManiaModKey8), - typeof(ManiaModKey9), + new ManiaModKey1(), + new ManiaModKey2(), + new ManiaModKey3(), + new ManiaModKey4(), + new ManiaModKey5(), + new ManiaModKey6(), + new ManiaModKey7(), + new ManiaModKey8(), + new ManiaModKey9(), }).ToArray(); } } diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 9c44eb6f97..e2a1542574 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -74,12 +74,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty new Speed() }; - protected override Type[] DifficultyAdjustmentMods => new[] + protected override Mod[] DifficultyAdjustmentMods => new Mod[] { - typeof(OsuModDoubleTime), - typeof(OsuModHalfTime), - typeof(OsuModEasy), - typeof(OsuModHardRock), + new OsuModDoubleTime(), + new OsuModHalfTime(), + new OsuModEasy(), + new OsuModHardRock(), }; } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index ad1fb4c2e5..685ad9949b 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.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.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; @@ -48,12 +47,12 @@ namespace osu.Game.Rulesets.Taiko.Difficulty protected override Skill[] CreateSkills(IBeatmap beatmap) => new Skill[] { new Strain() }; - protected override Type[] DifficultyAdjustmentMods => new[] + protected override Mod[] DifficultyAdjustmentMods => new Mod[] { - typeof(TaikoModDoubleTime), - typeof(TaikoModHalfTime), - typeof(TaikoModEasy), - typeof(TaikoModHardRock), + new TaikoModDoubleTime(), + new TaikoModHalfTime(), + new TaikoModEasy(), + new TaikoModHardRock(), }; } } diff --git a/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs b/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs index 3bce6fedbb..760a033aff 100644 --- a/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs +++ b/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs @@ -27,7 +27,7 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestSingleMod() { - var combinations = new TestLegacyDifficultyCalculator(typeof(ModA)).CreateDifficultyAdjustmentModCombinations(); + var combinations = new TestLegacyDifficultyCalculator(new ModA()).CreateDifficultyAdjustmentModCombinations(); Assert.AreEqual(2, combinations.Length); Assert.IsTrue(combinations[0] is ModNoMod); @@ -37,7 +37,7 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestDoubleMod() { - var combinations = new TestLegacyDifficultyCalculator(typeof(ModA), typeof(ModB)).CreateDifficultyAdjustmentModCombinations(); + var combinations = new TestLegacyDifficultyCalculator(new ModA(), new ModB()).CreateDifficultyAdjustmentModCombinations(); Assert.AreEqual(4, combinations.Length); Assert.IsTrue(combinations[0] is ModNoMod); @@ -52,7 +52,7 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestIncompatibleMods() { - var combinations = new TestLegacyDifficultyCalculator(typeof(ModA), typeof(ModIncompatibleWithA)).CreateDifficultyAdjustmentModCombinations(); + var combinations = new TestLegacyDifficultyCalculator(new ModA(), new ModIncompatibleWithA()).CreateDifficultyAdjustmentModCombinations(); Assert.AreEqual(3, combinations.Length); Assert.IsTrue(combinations[0] is ModNoMod); @@ -63,7 +63,7 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestDoubleIncompatibleMods() { - var combinations = new TestLegacyDifficultyCalculator(typeof(ModA), typeof(ModB), typeof(ModIncompatibleWithA), typeof(ModIncompatibleWithAAndB)).CreateDifficultyAdjustmentModCombinations(); + var combinations = new TestLegacyDifficultyCalculator(new ModA(), new ModB(), new ModIncompatibleWithA(), new ModIncompatibleWithAAndB()).CreateDifficultyAdjustmentModCombinations(); Assert.AreEqual(8, combinations.Length); Assert.IsTrue(combinations[0] is ModNoMod); @@ -86,7 +86,7 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestIncompatibleThroughBaseType() { - var combinations = new TestLegacyDifficultyCalculator(typeof(ModAofA), typeof(ModIncompatibleWithAofA)).CreateDifficultyAdjustmentModCombinations(); + var combinations = new TestLegacyDifficultyCalculator(new ModAofA(), new ModIncompatibleWithAofA()).CreateDifficultyAdjustmentModCombinations(); Assert.AreEqual(3, combinations.Length); Assert.IsTrue(combinations[0] is ModNoMod); @@ -141,13 +141,13 @@ namespace osu.Game.Tests.NonVisual private class TestLegacyDifficultyCalculator : DifficultyCalculator { - public TestLegacyDifficultyCalculator(params Type[] mods) + public TestLegacyDifficultyCalculator(params Mod[] mods) : base(null, null) { DifficultyAdjustmentMods = mods; } - protected override Type[] DifficultyAdjustmentMods { get; } + protected override Mod[] DifficultyAdjustmentMods { get; } protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) { diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index 9b630865c2..aad55f8a38 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -108,7 +108,7 @@ namespace osu.Game.Rulesets.Difficulty { return createDifficultyAdjustmentModCombinations(Enumerable.Empty(), DifficultyAdjustmentMods).ToArray(); - IEnumerable createDifficultyAdjustmentModCombinations(IEnumerable currentSet, Type[] adjustmentSet, int currentSetCount = 0, int adjustmentSetStart = 0) + IEnumerable createDifficultyAdjustmentModCombinations(IEnumerable currentSet, Mod[] adjustmentSet, int currentSetCount = 0, int adjustmentSetStart = 0) { switch (currentSetCount) { @@ -131,14 +131,12 @@ namespace osu.Game.Rulesets.Difficulty // combinations in further recursions, so a moving subset is used to eliminate this effect for (int i = adjustmentSetStart; i < adjustmentSet.Length; i++) { - var adjustmentMod = createMod(); + var adjustmentMod = adjustmentSet[i]; if (currentSet.Any(c => c.IncompatibleMods.Any(m => m.IsInstanceOfType(adjustmentMod)))) continue; - foreach (var combo in createDifficultyAdjustmentModCombinations(currentSet.Append(createMod()), adjustmentSet, currentSetCount + 1, i + 1)) + foreach (var combo in createDifficultyAdjustmentModCombinations(currentSet.Append(adjustmentMod), adjustmentSet, currentSetCount + 1, i + 1)) yield return combo; - - Mod createMod() => (Mod)Activator.CreateInstance(adjustmentSet[i]); } } } @@ -146,7 +144,7 @@ namespace osu.Game.Rulesets.Difficulty /// /// Retrieves all s which adjust the difficulty. /// - protected virtual Type[] DifficultyAdjustmentMods => Array.Empty(); + protected virtual Mod[] DifficultyAdjustmentMods => Array.Empty(); /// /// Creates to describe beatmap's calculated difficulty. From be5ffdbf22cd5a8dc5fca3c87306042741e2dafa Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 23 Mar 2019 16:01:14 +0900 Subject: [PATCH 0007/1117] Adjust edge bonuses to consider clock rate --- .../Preprocessing/CatchDifficultyHitObject.cs | 2 ++ .../Difficulty/Skills/Movement.cs | 21 +++++++------------ 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs b/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs index f0c68e4392..b2b4129c8a 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs @@ -24,6 +24,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing /// Milliseconds elapsed since the start time of the previous , with a minimum of 40ms. /// public readonly double StrainTime; + public readonly double ClockRate; public CatchDifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate, float halfCatcherWidth) : base(hitObject, lastObject, clockRate) @@ -36,6 +37,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing // Every strain interval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure StrainTime = Math.Max(40, DeltaTime); + ClockRate = clockRate; } } } diff --git a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs index d06ab2f928..227fb2820c 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills private const double direction_change_bonus = 9.8; private const double antiflow_bonus = 26.0; - protected override double SkillMultiplier => 860; + protected override double SkillMultiplier => 850; protected override double StrainDecayBase => 0.2; protected override double DecayWeight => 0.94; @@ -25,7 +25,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills private float? lastPlayerPosition; private float lastDistanceMoved; private double lastStrainTime; - private bool lastHyperdash; protected override double StrainValueOf(DifficultyHitObject current) { @@ -62,35 +61,32 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills // Direction changes after jumps (antiflow) are harder double antiflowBonusFactor = Math.Min(Math.Abs(distanceMoved) / 70, 1); - distanceAddition += (antiflow_bonus / (catchCurrent.StrainTime / 17.5 + 10)) * (Math.Sqrt(Math.Abs(lastDistanceMoved)) / Math.Sqrt(lastStrainTime + 25)) * antiflowBonusFactor; - - // Reduce strain slightly for Hyperdash chains - if (catchCurrent.LastObject.HyperDash && lastHyperdash) - distanceAddition *= 0.95; + distanceAddition += (antiflow_bonus / (catchCurrent.StrainTime / 17.5 + 10)) * (Math.Sqrt(Math.Abs(lastDistanceMoved)) / Math.Sqrt(lastStrainTime + 20)) * antiflowBonusFactor; + // Bonus for edge dashes on direction change if (catchCurrent.LastObject.DistanceToHyperDash <= 14.0f / CatchPlayfield.BASE_WIDTH && !catchCurrent.LastObject.HyperDash) - bonus += 3.0; + bonus += 1.0; } // Base bonus for every movement, giving some weight to streams. distanceAddition += 10.0 * Math.Min(Math.Abs(distanceMoved), normalized_hitobject_radius * 2) / (normalized_hitobject_radius * 6) / sqrtStrain; } - // Big bonus for edge dashes + // Bonus for edge dashes regardless of direction change if (catchCurrent.LastObject.DistanceToHyperDash <= 14.0f / CatchPlayfield.BASE_WIDTH) { if (!catchCurrent.LastObject.HyperDash) - bonus += 4.5; + bonus += 0.9; else { // After a hyperdash we ARE in the correct position. Always! playerPosition = catchCurrent.NormalizedPosition; } - distanceAddition *= 1.0 + bonus * (14.0f - catchCurrent.LastObject.DistanceToHyperDash * CatchPlayfield.BASE_WIDTH) / 14.0f * (Math.Min(catchCurrent.StrainTime, 250) / 250); // Edge dashes are easier at lower ms values + distanceAddition *= 1.0 + bonus * Math.Pow(14.0f - catchCurrent.LastObject.DistanceToHyperDash * CatchPlayfield.BASE_WIDTH, 1.6f) / 14.0f * (Math.Min(catchCurrent.StrainTime * catchCurrent.ClockRate, 250) / 250); // Edge dashes are easier at lower ms values } - // Prevent wide, dense stacks of notes which fit on the catcher from greatly increasing SR + // Prevent wide dense stacks of notes which fit on the catcher from greatly increasing SR if (Math.Abs(distanceMoved) > 0.1) { if (Math.Abs(lastDistanceMoved) > 0.1 && Math.Sign(distanceMoved) != Math.Sign(lastDistanceMoved)) @@ -108,7 +104,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills lastPlayerPosition = playerPosition; lastDistanceMoved = distanceMoved; lastStrainTime = catchCurrent.StrainTime; - lastHyperdash = catchCurrent.LastObject.HyperDash; return distanceAddition / weightedStrainTime; } From 2705263145b1dcae03bc5b58304eebe9aebfa7f3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 26 Mar 2019 13:25:52 +0900 Subject: [PATCH 0008/1117] Scale edge dash threshold with clock rate --- .../Difficulty/Skills/Movement.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs index 227fb2820c..27558838f4 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs @@ -62,28 +62,26 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills double antiflowBonusFactor = Math.Min(Math.Abs(distanceMoved) / 70, 1); distanceAddition += (antiflow_bonus / (catchCurrent.StrainTime / 17.5 + 10)) * (Math.Sqrt(Math.Abs(lastDistanceMoved)) / Math.Sqrt(lastStrainTime + 20)) * antiflowBonusFactor; - - // Bonus for edge dashes on direction change - if (catchCurrent.LastObject.DistanceToHyperDash <= 14.0f / CatchPlayfield.BASE_WIDTH && !catchCurrent.LastObject.HyperDash) - bonus += 1.0; } // Base bonus for every movement, giving some weight to streams. distanceAddition += 10.0 * Math.Min(Math.Abs(distanceMoved), normalized_hitobject_radius * 2) / (normalized_hitobject_radius * 6) / sqrtStrain; } - // Bonus for edge dashes regardless of direction change - if (catchCurrent.LastObject.DistanceToHyperDash <= 14.0f / CatchPlayfield.BASE_WIDTH) + // Bonus for edge dashes + double edgeDashThreshold = 15.5f * ((Math.Min(catchCurrent.StrainTime * catchCurrent.ClockRate, 250) * 0.9 + 25) / 250); + + if (catchCurrent.LastObject.DistanceToHyperDash <= edgeDashThreshold / CatchPlayfield.BASE_WIDTH) { if (!catchCurrent.LastObject.HyperDash) - bonus += 0.9; + bonus += 2.3; else { // After a hyperdash we ARE in the correct position. Always! playerPosition = catchCurrent.NormalizedPosition; } - distanceAddition *= 1.0 + bonus * Math.Pow(14.0f - catchCurrent.LastObject.DistanceToHyperDash * CatchPlayfield.BASE_WIDTH, 1.6f) / 14.0f * (Math.Min(catchCurrent.StrainTime * catchCurrent.ClockRate, 250) / 250); // Edge dashes are easier at lower ms values + distanceAddition *= 1.0 + bonus * Math.Pow(edgeDashThreshold - catchCurrent.LastObject.DistanceToHyperDash * CatchPlayfield.BASE_WIDTH, 1.6f) / edgeDashThreshold * (Math.Min(catchCurrent.StrainTime * catchCurrent.ClockRate, 265) / 265); // Edge dashes are easier at lower ms values } // Prevent wide dense stacks of notes which fit on the catcher from greatly increasing SR From 9d0d402336e6b7123ee715105d953979797cc44c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 27 Mar 2019 17:21:57 +0900 Subject: [PATCH 0009/1117] Apply pp calculator changes (Backported from https://github.com/ppy/osu-performance/compare/master...smoogipoo:sorcerer-catch-changes) --- .../Difficulty/CatchPerformanceCalculator.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs index 5a640f6d1a..28da047187 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs @@ -55,8 +55,8 @@ namespace osu.Game.Rulesets.Catch.Difficulty // Longer maps are worth more float lengthBonus = - 0.95f + 0.4f * Math.Min(1.0f, numTotalHits / 3000.0f) + - (numTotalHits > 3000 ? (float)Math.Log10(numTotalHits / 3000.0f) * 0.5f : 0.0f); + 0.95f + 0.3f * Math.Min(1.0f, numTotalHits / 2500.0f) + + (numTotalHits > 2500 ? (float)Math.Log10(numTotalHits / 2500.0f) * 0.475f : 0.0f); // Longer maps are worth more value *= lengthBonus; @@ -73,14 +73,22 @@ namespace osu.Game.Rulesets.Catch.Difficulty float approachRateFactor = 1.0f; if (approachRate > 9.0f) approachRateFactor += 0.1f * (approachRate - 9.0f); // 10% for each AR above 9 + if (approachRate > 10.0f) + approachRateFactor += 0.2f * (float)Math.Pow(approachRate - 10.0f, 1.5f); // Additional 20% at AR 11, 40% total else if (approachRate < 8.0f) approachRateFactor += 0.025f * (8.0f - approachRate); // 2.5% for each AR below 8 value *= approachRateFactor; if (mods.Any(m => m is ModHidden)) - // Hiddens gives nothing on max approach rate, and more the lower it is + { value *= 1.05f + 0.075f * (10.0f - Math.Min(10.0f, approachRate)); // 7.5% for each AR below 10 + // Hiddens gives almost nothing on max approach rate, and more the lower it is + if (approachRate <= 10.0f) + value *= 1.05f + 0.075f * (10.0f - approachRate); // 7.5% for each AR below 10 + else if (approachRate > 10.0f) + value *= 1.01f + 0.04f * (11.0f - Math.Min(11.0f, approachRate)); // 5% at AR 10, 1% at AR 11 + } if (mods.Any(m => m is ModFlashlight)) // Apply length bonus again if flashlight is on simply because it becomes a lot harder on longer maps. From b402981fc644c85422d52449c90cd3120a3bd44d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 1 Apr 2019 10:57:01 +0900 Subject: [PATCH 0010/1117] Buff CS > 5 --- .../Difficulty/CatchDifficultyCalculator.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 47e2bc5259..a475aefd71 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -52,7 +52,8 @@ namespace osu.Game.Rulesets.Catch.Difficulty using (var catcher = new CatcherArea.Catcher(beatmap.BeatmapInfo.BaseDifficulty)) { halfCatchWidth = catcher.CatchWidth * 0.5f; - halfCatchWidth *= 0.8f; // We're only using 80% of the catcher's width to simulate imperfect gameplay. + // We're only using 80% of the catcher's width to simulate imperfect gameplay. + halfCatchWidth *= Math.Min(1.05f - (0.05f * beatmap.BeatmapInfo.BaseDifficulty.CircleSize), 0.8f); // Reduce the catcher's width further at circle sizes above 5. } CatchHitObject lastObject = null; From b2396b82a5bf218ac61786f9e38ae8a6976d91c8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 1 Apr 2019 10:58:26 +0900 Subject: [PATCH 0011/1117] Change edge dashes to scale linearly once again --- .../Preprocessing/CatchDifficultyHitObject.cs | 3 +++ osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs | 10 ++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs b/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs index b2b4129c8a..9f6de35605 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs @@ -25,6 +25,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing /// public readonly double StrainTime; public readonly double ClockRate; + public readonly double HalfCatcherWidth; public CatchDifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate, float halfCatcherWidth) : base(hitObject, lastObject, clockRate) @@ -38,6 +39,8 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing // Every strain interval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure StrainTime = Math.Max(40, DeltaTime); ClockRate = clockRate; + HalfCatcherWidth = halfCatcherWidth; } } } + diff --git a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs index 27558838f4..473b254d5b 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs @@ -68,20 +68,18 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills distanceAddition += 10.0 * Math.Min(Math.Abs(distanceMoved), normalized_hitobject_radius * 2) / (normalized_hitobject_radius * 6) / sqrtStrain; } - // Bonus for edge dashes - double edgeDashThreshold = 15.5f * ((Math.Min(catchCurrent.StrainTime * catchCurrent.ClockRate, 250) * 0.9 + 25) / 250); - - if (catchCurrent.LastObject.DistanceToHyperDash <= edgeDashThreshold / CatchPlayfield.BASE_WIDTH) + // Bonus for "almost" hyperdashes at corner points + if (catchCurrent.LastObject.DistanceToHyperDash <= 20.0f / CatchPlayfield.BASE_WIDTH) { if (!catchCurrent.LastObject.HyperDash) - bonus += 2.3; + bonus += 5.7; else { // After a hyperdash we ARE in the correct position. Always! playerPosition = catchCurrent.NormalizedPosition; } - distanceAddition *= 1.0 + bonus * Math.Pow(edgeDashThreshold - catchCurrent.LastObject.DistanceToHyperDash * CatchPlayfield.BASE_WIDTH, 1.6f) / edgeDashThreshold * (Math.Min(catchCurrent.StrainTime * catchCurrent.ClockRate, 265) / 265); // Edge dashes are easier at lower ms values + distanceAddition *= 1.0 + bonus * ((20 - catchCurrent.LastObject.DistanceToHyperDash * CatchPlayfield.BASE_WIDTH) / 20) * Math.Pow((Math.Min(catchCurrent.StrainTime, 265) / 265), 1.5); } // Prevent wide dense stacks of notes which fit on the catcher from greatly increasing SR From efee2fb283903f00b4039330117836c271016eb0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 1 Apr 2019 11:00:26 +0900 Subject: [PATCH 0012/1117] Adjust antiflow calculations --- .../Difficulty/Skills/Movement.cs | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs index 473b254d5b..e78e5c0a58 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs @@ -14,10 +14,9 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills { private const float absolute_player_positioning_error = 16f; private const float normalized_hitobject_radius = 41.0f; - private const double direction_change_bonus = 9.8; - private const double antiflow_bonus = 26.0; + private const double direction_change_bonus = 21.0; - protected override double SkillMultiplier => 850; + protected override double SkillMultiplier => 900; protected override double StrainDecayBase => 0.2; protected override double DecayWeight => 0.94; @@ -29,6 +28,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills protected override double StrainValueOf(DifficultyHitObject current) { var catchCurrent = (CatchDifficultyHitObject)current; + double halfCatcherWidth = catchCurrent.HalfCatcherWidth; if (lastPlayerPosition == null) lastPlayerPosition = catchCurrent.LastNormalizedPosition; @@ -41,10 +41,9 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills float distanceMoved = playerPosition - lastPlayerPosition.Value; - // Reduce speed scaling - double weightedStrainTime = catchCurrent.StrainTime + 20; + double weightedStrainTime = catchCurrent.StrainTime + 18; - double distanceAddition = Math.Pow(Math.Abs(distanceMoved), 1.2) / 340; + double distanceAddition = (Math.Pow(Math.Abs(distanceMoved), 1.3) / 510); double sqrtStrain = Math.Sqrt(weightedStrainTime); double bonus = 0; @@ -54,18 +53,14 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills { if (Math.Abs(lastDistanceMoved) > 0.1 && Math.Sign(distanceMoved) != Math.Sign(lastDistanceMoved)) { - double bonusFactor = Math.Min(absolute_player_positioning_error, Math.Abs(distanceMoved)) / absolute_player_positioning_error; + double bonusFactor = Math.Min(halfCatcherWidth, Math.Abs(distanceMoved)) / halfCatcherWidth; + double antiflowFactor = Math.Max(Math.Min(halfCatcherWidth, Math.Abs(lastDistanceMoved)) / halfCatcherWidth, 0.3); - distanceAddition += direction_change_bonus / sqrtStrain * bonusFactor; - - // Direction changes after jumps (antiflow) are harder - double antiflowBonusFactor = Math.Min(Math.Abs(distanceMoved) / 70, 1); - - distanceAddition += (antiflow_bonus / (catchCurrent.StrainTime / 17.5 + 10)) * (Math.Sqrt(Math.Abs(lastDistanceMoved)) / Math.Sqrt(lastStrainTime + 20)) * antiflowBonusFactor; + distanceAddition += direction_change_bonus / Math.Sqrt(lastStrainTime + 18) * bonusFactor * antiflowFactor * Math.Max(1 - Math.Pow(weightedStrainTime / 1000, 2), 0); } // Base bonus for every movement, giving some weight to streams. - distanceAddition += 10.0 * Math.Min(Math.Abs(distanceMoved), normalized_hitobject_radius * 2) / (normalized_hitobject_radius * 6) / sqrtStrain; + distanceAddition += 12.5 * Math.Min(Math.Abs(distanceMoved), normalized_hitobject_radius * 2) / (normalized_hitobject_radius * 6) / sqrtStrain; } // Bonus for "almost" hyperdashes at corner points From 21e62c37d8d2fcacb49a1f475ae65d73f04b4abf Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Apr 2019 07:28:04 +0900 Subject: [PATCH 0013/1117] General fixes --- .../Difficulty/CatchDifficultyCalculator.cs | 2 +- .../Preprocessing/CatchDifficultyHitObject.cs | 3 --- .../Difficulty/Skills/Movement.cs | 13 ++++++------- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index a475aefd71..4cd297478a 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty { public class CatchDifficultyCalculator : DifficultyCalculator { - private const double star_scaling_factor = 0.15; + private const double star_scaling_factor = 0.153; protected override int SectionLength => 750; diff --git a/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs b/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs index 9f6de35605..b2b4129c8a 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs @@ -25,7 +25,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing /// public readonly double StrainTime; public readonly double ClockRate; - public readonly double HalfCatcherWidth; public CatchDifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate, float halfCatcherWidth) : base(hitObject, lastObject, clockRate) @@ -39,8 +38,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing // Every strain interval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure StrainTime = Math.Max(40, DeltaTime); ClockRate = clockRate; - HalfCatcherWidth = halfCatcherWidth; } } } - diff --git a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs index e78e5c0a58..4ea5135c4f 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs @@ -28,7 +28,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills protected override double StrainValueOf(DifficultyHitObject current) { var catchCurrent = (CatchDifficultyHitObject)current; - double halfCatcherWidth = catchCurrent.HalfCatcherWidth; if (lastPlayerPosition == null) lastPlayerPosition = catchCurrent.LastNormalizedPosition; @@ -46,17 +45,17 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills double distanceAddition = (Math.Pow(Math.Abs(distanceMoved), 1.3) / 510); double sqrtStrain = Math.Sqrt(weightedStrainTime); - double bonus = 0; + double edgeDashBonus = 0; // Direction changes give an extra point! if (Math.Abs(distanceMoved) > 0.1) { if (Math.Abs(lastDistanceMoved) > 0.1 && Math.Sign(distanceMoved) != Math.Sign(lastDistanceMoved)) { - double bonusFactor = Math.Min(halfCatcherWidth, Math.Abs(distanceMoved)) / halfCatcherWidth; - double antiflowFactor = Math.Max(Math.Min(halfCatcherWidth, Math.Abs(lastDistanceMoved)) / halfCatcherWidth, 0.3); + double bonusFactor = Math.Min(50, Math.Abs(distanceMoved)) / 50; + double antiflowFactor = Math.Max(Math.Min(70, Math.Abs(lastDistanceMoved)) / 70, 0.3); - distanceAddition += direction_change_bonus / Math.Sqrt(lastStrainTime + 18) * bonusFactor * antiflowFactor * Math.Max(1 - Math.Pow(weightedStrainTime / 1000, 2), 0); + distanceAddition += direction_change_bonus / Math.Sqrt(lastStrainTime + 18) * bonusFactor * antiflowFactor * Math.Max(1 - Math.Pow(weightedStrainTime / 1000, 3), 0); } // Base bonus for every movement, giving some weight to streams. @@ -67,14 +66,14 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills if (catchCurrent.LastObject.DistanceToHyperDash <= 20.0f / CatchPlayfield.BASE_WIDTH) { if (!catchCurrent.LastObject.HyperDash) - bonus += 5.7; + edgeDashBonus += 5.7; else { // After a hyperdash we ARE in the correct position. Always! playerPosition = catchCurrent.NormalizedPosition; } - distanceAddition *= 1.0 + bonus * ((20 - catchCurrent.LastObject.DistanceToHyperDash * CatchPlayfield.BASE_WIDTH) / 20) * Math.Pow((Math.Min(catchCurrent.StrainTime, 265) / 265), 1.5); + distanceAddition *= 1.0 + edgeDashBonus * ((20 - catchCurrent.LastObject.DistanceToHyperDash * CatchPlayfield.BASE_WIDTH) / 20) * Math.Pow((Math.Min(catchCurrent.StrainTime * catchCurrent.ClockRate, 265) / 265), 1.5); // Edge Dashes are easier at lower ms values } // Prevent wide dense stacks of notes which fit on the catcher from greatly increasing SR From 5566c4881a25af18e2b34407260966d148f51ff0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 3 Apr 2019 11:38:48 +0900 Subject: [PATCH 0014/1117] Buff DT --- osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs index 4ea5135c4f..7d5a7b4007 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs @@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills float distanceMoved = playerPosition - lastPlayerPosition.Value; - double weightedStrainTime = catchCurrent.StrainTime + 18; + double weightedStrainTime = catchCurrent.StrainTime + 10 + (8 / catchCurrent.ClockRate); double distanceAddition = (Math.Pow(Math.Abs(distanceMoved), 1.3) / 510); double sqrtStrain = Math.Sqrt(weightedStrainTime); From 2824a32db60b99222ac2f862e1f54c980af0aab7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 3 Apr 2019 11:39:13 +0900 Subject: [PATCH 0015/1117] Adjust circle-size bonus point --- .../Difficulty/CatchDifficultyCalculator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 4cd297478a..c56881ba51 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -52,8 +52,8 @@ namespace osu.Game.Rulesets.Catch.Difficulty using (var catcher = new CatcherArea.Catcher(beatmap.BeatmapInfo.BaseDifficulty)) { halfCatchWidth = catcher.CatchWidth * 0.5f; - // We're only using 80% of the catcher's width to simulate imperfect gameplay. - halfCatchWidth *= Math.Min(1.05f - (0.05f * beatmap.BeatmapInfo.BaseDifficulty.CircleSize), 0.8f); // Reduce the catcher's width further at circle sizes above 5. + // We're only using 80% of the catcher's width to simulate imperfect gameplay, reduced further at circle sizes above 5.5 + halfCatchWidth *= Math.Min(1.075f - (0.05f * beatmap.BeatmapInfo.BaseDifficulty.CircleSize), 0.8f); } CatchHitObject lastObject = null; From 9d116efdbd8731443e46195f666aa1c21752aec5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 10 Apr 2019 10:57:27 +0900 Subject: [PATCH 0016/1117] Limit to 10000 tiny ticks per slider --- osu.Game.Rulesets.Catch/Objects/JuiceStream.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs index 2adc156efd..9baa6a8531 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs @@ -55,6 +55,8 @@ namespace osu.Game.Rulesets.Catch.Objects SliderEventDescriptor? lastEvent = null; + int ticksGenerated = 0; + foreach (var e in SliderEventGenerator.Generate(StartTime, SpanDuration, Velocity, TickDistance, Path.Distance, this.SpanCount(), LegacyLastTickOffset)) { // generate tiny droplets since the last point @@ -70,6 +72,9 @@ namespace osu.Game.Rulesets.Catch.Objects for (double t = timeBetweenTiny; t < sinceLastTick; t += timeBetweenTiny) { + if (ticksGenerated++ >= 10000) + break; + AddNested(new TinyDroplet { Samples = tickSamples, From 5a0b93bdb2f2000cc7360319982cf652d0bdbbe1 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 20 Feb 2020 17:02:22 +0300 Subject: [PATCH 0017/1117] Add ShowTag method --- .../Online/TestSceneBeatmapListingOverlay.cs | 6 ++++++ osu.Game/Overlays/BeatmapListingOverlay.cs | 20 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs index 7c05d99c59..4aea29faa0 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs @@ -24,6 +24,12 @@ namespace osu.Game.Tests.Visual.Online Add(overlay = new BeatmapListingOverlay()); } + [Test] + public void TestShowTag() + { + AddStep("Show Rem tag", () => overlay.ShowTag("Rem")); + } + [Test] public void TestShow() { diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index 213e9a4244..e212d05442 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -158,6 +158,26 @@ namespace osu.Game.Overlays sortDirection.BindValueChanged(_ => queueUpdateSearch()); } + public void ShowTag(string tag) + { + var currentQuery = searchSection.Query.Value; + + if (currentQuery != tag) + { + setDefaultSearchValues(); + searchSection.Query.Value = tag; + } + + Show(); + } + + private void setDefaultSearchValues() + { + searchSection.Query.Value = string.Empty; + searchSection.Ruleset.Value = new RulesetInfo { Name = @"Any" }; + searchSection.Category.Value = BeatmapSearchCategory.Leaderboard; + } + private ScheduledDelegate queryChangedDebounce; private void queueUpdateSearch(bool queryTextChanged = false) From 6b2ae67eafda3bef8a2d720865f1ec2851e4fa86 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 20 Feb 2020 17:40:45 +0300 Subject: [PATCH 0018/1117] Implement Genre filter --- .../Online/TestSceneBeatmapListingOverlay.cs | 7 +++++ .../API/Requests/SearchBeatmapSetsRequest.cs | 26 ++++++++++++++++++- .../BeatmapListingSearchSection.cs | 4 +++ osu.Game/Overlays/BeatmapListingOverlay.cs | 18 ++++++++++++- 4 files changed, 53 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs index 4aea29faa0..9dec965818 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using osu.Game.Overlays; using NUnit.Framework; +using osu.Game.Online.API.Requests; namespace osu.Game.Tests.Visual.Online { @@ -30,6 +31,12 @@ namespace osu.Game.Tests.Visual.Online AddStep("Show Rem tag", () => overlay.ShowTag("Rem")); } + [Test] + public void TestShowGenre() + { + AddStep("Show Anime genre", () => overlay.ShowGenre(BeatmapSearchGenre.Anime)); + } + [Test] public void TestShow() { diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs index 930ca8fdf1..797a0a1015 100644 --- a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs +++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs @@ -16,15 +16,17 @@ namespace osu.Game.Online.API.Requests private readonly BeatmapSearchCategory searchCategory; private readonly DirectSortCriteria sortCriteria; private readonly SortDirection direction; + private readonly BeatmapSearchGenre genre; private string directionString => direction == SortDirection.Descending ? @"desc" : @"asc"; - public SearchBeatmapSetsRequest(string query, RulesetInfo ruleset, BeatmapSearchCategory searchCategory = BeatmapSearchCategory.Any, DirectSortCriteria sortCriteria = DirectSortCriteria.Ranked, SortDirection direction = SortDirection.Descending) + public SearchBeatmapSetsRequest(string query, RulesetInfo ruleset, BeatmapSearchCategory searchCategory = BeatmapSearchCategory.Any, DirectSortCriteria sortCriteria = DirectSortCriteria.Ranked, SortDirection direction = SortDirection.Descending, BeatmapSearchGenre genre = BeatmapSearchGenre.Any) { this.query = string.IsNullOrEmpty(query) ? string.Empty : System.Uri.EscapeDataString(query); this.ruleset = ruleset; this.searchCategory = searchCategory; this.sortCriteria = sortCriteria; this.direction = direction; + this.genre = genre; } protected override WebRequest CreateWebRequest() @@ -36,6 +38,10 @@ namespace osu.Game.Online.API.Requests req.AddParameter("m", ruleset.ID.Value.ToString()); req.AddParameter("s", searchCategory.ToString().ToLowerInvariant()); + + if (genre != BeatmapSearchGenre.Any) + req.AddParameter("g", ((int)genre).ToString()); + req.AddParameter("sort", $"{sortCriteria.ToString().ToLowerInvariant()}_{directionString}"); return req; @@ -62,4 +68,22 @@ namespace osu.Game.Online.API.Requests [Description("My Maps")] Mine, } + + public enum BeatmapSearchGenre + { + Any, + Unspecified, + + [Description("Video Game")] + Game, + Anime, + Rock, + Pop, + Other, + Novelty, + + [Description("Hip Hop")] + Hiphop = 9, + Electronic + } } diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchSection.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchSection.cs index f9799d8a6b..1e97720705 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchSection.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchSection.cs @@ -25,6 +25,8 @@ namespace osu.Game.Overlays.BeatmapListing public Bindable Category => categoryFilter.Current; + public Bindable Genre => genreFilter.Current; + public BeatmapSetInfo BeatmapSet { set @@ -43,6 +45,7 @@ namespace osu.Game.Overlays.BeatmapListing private readonly BeatmapSearchTextBox textBox; private readonly BeatmapSearchRulesetFilterRow modeFilter; private readonly BeatmapSearchFilterRow categoryFilter; + private readonly BeatmapSearchSmallFilterRow genreFilter; private readonly Box background; private readonly UpdateableBeatmapSetCover beatmapCover; @@ -98,6 +101,7 @@ namespace osu.Game.Overlays.BeatmapListing { modeFilter = new BeatmapSearchRulesetFilterRow(), categoryFilter = new BeatmapSearchFilterRow(@"Categories"), + genreFilter = new BeatmapSearchSmallFilterRow(@"Genre"), } } } diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index e212d05442..604af971f3 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -154,6 +154,7 @@ namespace osu.Game.Overlays searchSection.Ruleset.BindValueChanged(_ => queueUpdateSearch()); searchSection.Category.BindValueChanged(_ => queueUpdateSearch()); + searchSection.Genre.BindValueChanged(_ => queueUpdateSearch()); sortCriteria.BindValueChanged(_ => queueUpdateSearch()); sortDirection.BindValueChanged(_ => queueUpdateSearch()); } @@ -171,11 +172,25 @@ namespace osu.Game.Overlays Show(); } + public void ShowGenre(BeatmapSearchGenre genre) + { + var currentGenre = searchSection.Genre.Value; + + if (currentGenre != genre) + { + setDefaultSearchValues(); + searchSection.Genre.Value = genre; + } + + Show(); + } + private void setDefaultSearchValues() { searchSection.Query.Value = string.Empty; searchSection.Ruleset.Value = new RulesetInfo { Name = @"Any" }; searchSection.Category.Value = BeatmapSearchCategory.Leaderboard; + searchSection.Genre.Value = BeatmapSearchGenre.Any; } private ScheduledDelegate queryChangedDebounce; @@ -208,7 +223,8 @@ namespace osu.Game.Overlays searchSection.Ruleset.Value, searchSection.Category.Value, sortControl.Current.Value, - sortControl.SortDirection.Value); + sortControl.SortDirection.Value, + searchSection.Genre.Value); getSetsRequest.Success += response => Schedule(() => recreatePanels(response)); From 063a53017e82a4b883dd088bfb4ca36d72a9a6f5 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 20 Feb 2020 17:56:49 +0300 Subject: [PATCH 0019/1117] Implement Language filter --- .../Online/TestSceneBeatmapListingOverlay.cs | 6 +++++ .../API/Requests/SearchBeatmapSetsRequest.cs | 23 ++++++++++++++++++- .../BeatmapListingSearchSection.cs | 4 ++++ osu.Game/Overlays/BeatmapListingOverlay.cs | 18 ++++++++++++++- 4 files changed, 49 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs index 9dec965818..4dceb57129 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs @@ -37,6 +37,12 @@ namespace osu.Game.Tests.Visual.Online AddStep("Show Anime genre", () => overlay.ShowGenre(BeatmapSearchGenre.Anime)); } + [Test] + public void TestShowLanguage() + { + AddStep("Show Japanese language", () => overlay.ShowLanguage(BeatmapSearchLanguage.Japanese)); + } + [Test] public void TestShow() { diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs index 797a0a1015..c2679fcd5f 100644 --- a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs +++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs @@ -17,9 +17,10 @@ namespace osu.Game.Online.API.Requests private readonly DirectSortCriteria sortCriteria; private readonly SortDirection direction; private readonly BeatmapSearchGenre genre; + private readonly BeatmapSearchLanguage language; private string directionString => direction == SortDirection.Descending ? @"desc" : @"asc"; - public SearchBeatmapSetsRequest(string query, RulesetInfo ruleset, BeatmapSearchCategory searchCategory = BeatmapSearchCategory.Any, DirectSortCriteria sortCriteria = DirectSortCriteria.Ranked, SortDirection direction = SortDirection.Descending, BeatmapSearchGenre genre = BeatmapSearchGenre.Any) + public SearchBeatmapSetsRequest(string query, RulesetInfo ruleset, BeatmapSearchCategory searchCategory = BeatmapSearchCategory.Any, DirectSortCriteria sortCriteria = DirectSortCriteria.Ranked, SortDirection direction = SortDirection.Descending, BeatmapSearchGenre genre = BeatmapSearchGenre.Any, BeatmapSearchLanguage language = BeatmapSearchLanguage.Any) { this.query = string.IsNullOrEmpty(query) ? string.Empty : System.Uri.EscapeDataString(query); this.ruleset = ruleset; @@ -27,6 +28,7 @@ namespace osu.Game.Online.API.Requests this.sortCriteria = sortCriteria; this.direction = direction; this.genre = genre; + this.language = language; } protected override WebRequest CreateWebRequest() @@ -42,6 +44,9 @@ namespace osu.Game.Online.API.Requests if (genre != BeatmapSearchGenre.Any) req.AddParameter("g", ((int)genre).ToString()); + if (language != BeatmapSearchLanguage.Any) + req.AddParameter("l", ((int)language).ToString()); + req.AddParameter("sort", $"{sortCriteria.ToString().ToLowerInvariant()}_{directionString}"); return req; @@ -86,4 +91,20 @@ namespace osu.Game.Online.API.Requests Hiphop = 9, Electronic } + + public enum BeatmapSearchLanguage + { + Any, + English = 2, + Chilnese = 4, + French = 7, + German, + Italian = 11, + Japanese = 3, + Korean = 6, + Spanish = 10, + Swedish = 9, + Instrumantal = 5, + Other = 1 + } } diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchSection.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchSection.cs index 1e97720705..28619ea6fe 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchSection.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchSection.cs @@ -27,6 +27,8 @@ namespace osu.Game.Overlays.BeatmapListing public Bindable Genre => genreFilter.Current; + public Bindable Language => languageFilter.Current; + public BeatmapSetInfo BeatmapSet { set @@ -46,6 +48,7 @@ namespace osu.Game.Overlays.BeatmapListing private readonly BeatmapSearchRulesetFilterRow modeFilter; private readonly BeatmapSearchFilterRow categoryFilter; private readonly BeatmapSearchSmallFilterRow genreFilter; + private readonly BeatmapSearchSmallFilterRow languageFilter; private readonly Box background; private readonly UpdateableBeatmapSetCover beatmapCover; @@ -102,6 +105,7 @@ namespace osu.Game.Overlays.BeatmapListing modeFilter = new BeatmapSearchRulesetFilterRow(), categoryFilter = new BeatmapSearchFilterRow(@"Categories"), genreFilter = new BeatmapSearchSmallFilterRow(@"Genre"), + languageFilter = new BeatmapSearchSmallFilterRow(@"Language"), } } } diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index 604af971f3..414dabd7c1 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -155,6 +155,7 @@ namespace osu.Game.Overlays searchSection.Ruleset.BindValueChanged(_ => queueUpdateSearch()); searchSection.Category.BindValueChanged(_ => queueUpdateSearch()); searchSection.Genre.BindValueChanged(_ => queueUpdateSearch()); + searchSection.Language.BindValueChanged(_ => queueUpdateSearch()); sortCriteria.BindValueChanged(_ => queueUpdateSearch()); sortDirection.BindValueChanged(_ => queueUpdateSearch()); } @@ -185,12 +186,26 @@ namespace osu.Game.Overlays Show(); } + public void ShowLanguage(BeatmapSearchLanguage language) + { + var currentLanguage = searchSection.Language.Value; + + if (currentLanguage != language) + { + setDefaultSearchValues(); + searchSection.Language.Value = language; + } + + Show(); + } + private void setDefaultSearchValues() { searchSection.Query.Value = string.Empty; searchSection.Ruleset.Value = new RulesetInfo { Name = @"Any" }; searchSection.Category.Value = BeatmapSearchCategory.Leaderboard; searchSection.Genre.Value = BeatmapSearchGenre.Any; + searchSection.Language.Value = BeatmapSearchLanguage.Any; } private ScheduledDelegate queryChangedDebounce; @@ -224,7 +239,8 @@ namespace osu.Game.Overlays searchSection.Category.Value, sortControl.Current.Value, sortControl.SortDirection.Value, - searchSection.Genre.Value); + searchSection.Genre.Value, + searchSection.Language.Value); getSetsRequest.Success += response => Schedule(() => recreatePanels(response)); From eeae0a57746a4ed70199439499561b55f8311f2e Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 21 Feb 2020 00:56:33 +0300 Subject: [PATCH 0020/1117] Fix typos --- osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs index c2679fcd5f..1c1da33d8a 100644 --- a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs +++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs @@ -80,7 +80,7 @@ namespace osu.Game.Online.API.Requests Unspecified, [Description("Video Game")] - Game, + VideoGame, Anime, Rock, Pop, @@ -88,7 +88,7 @@ namespace osu.Game.Online.API.Requests Novelty, [Description("Hip Hop")] - Hiphop = 9, + HipHop = 9, Electronic } @@ -96,7 +96,7 @@ namespace osu.Game.Online.API.Requests { Any, English = 2, - Chilnese = 4, + Chinese = 4, French = 7, German, Italian = 11, @@ -104,7 +104,7 @@ namespace osu.Game.Online.API.Requests Korean = 6, Spanish = 10, Swedish = 9, - Instrumantal = 5, + Instrumental = 5, Other = 1 } } From d50cca626405266f86e7eae733b2af247cb2243d Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 21 Feb 2020 01:05:20 +0300 Subject: [PATCH 0021/1117] Minor enum adjustments for consistency --- .../API/Requests/SearchBeatmapSetsRequest.cs | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs index 1c1da33d8a..e329015f67 100644 --- a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs +++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs @@ -76,35 +76,35 @@ namespace osu.Game.Online.API.Requests public enum BeatmapSearchGenre { - Any, - Unspecified, + Any = 0, + Unspecified = 1, [Description("Video Game")] - VideoGame, - Anime, - Rock, - Pop, - Other, - Novelty, + VideoGame = 2, + Anime = 3, + Rock = 4, + Pop = 5, + Other = 6, + Novelty = 7, [Description("Hip Hop")] HipHop = 9, - Electronic + Electronic = 10 } public enum BeatmapSearchLanguage { Any, - English = 2, - Chinese = 4, - French = 7, + Other, + English, + Japanese, + Chinese, + Instrumental, + Korean, + French, German, - Italian = 11, - Japanese = 3, - Korean = 6, - Spanish = 10, - Swedish = 9, - Instrumental = 5, - Other = 1 + Swedish, + Spanish, + Italian } } From 58903759f13f307df17fd781780952a5d92f102e Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 21 Feb 2020 01:37:36 +0300 Subject: [PATCH 0022/1117] Implement enum attributes to set display order --- .../API/Requests/SearchBeatmapSetsRequest.cs | 41 +++++++++++++++++++ .../BeatmapListing/BeatmapSearchFilterRow.cs | 27 +++++++++++- 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs index e329015f67..58a41b6e08 100644 --- a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs +++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.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.ComponentModel; using osu.Framework.IO.Network; using osu.Game.Overlays; @@ -92,19 +93,59 @@ namespace osu.Game.Online.API.Requests Electronic = 10 } + [HasOrderedElements] public enum BeatmapSearchLanguage { + [Order(0)] Any, + + [Order(11)] Other, + + [Order(1)] English, + + [Order(6)] Japanese, + + [Order(2)] Chinese, + + [Order(10)] Instrumental, + + [Order(7)] Korean, + + [Order(3)] French, + + [Order(4)] German, + + [Order(9)] Swedish, + + [Order(8)] Spanish, + + [Order(5)] Italian } + + [AttributeUsage(AttributeTargets.Field)] + public class OrderAttribute : Attribute + { + public readonly int Order; + + public OrderAttribute(int order) + { + Order = order; + } + } + + [AttributeUsage(AttributeTargets.Enum)] + public class HasOrderedElementsAttribute : Attribute + { + } } diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapSearchFilterRow.cs b/osu.Game/Overlays/BeatmapListing/BeatmapSearchFilterRow.cs index 2c046a2bbf..467399dd20 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapSearchFilterRow.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapSearchFilterRow.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -13,6 +14,7 @@ using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API.Requests; using osuTK; using osuTK.Graphics; @@ -79,9 +81,30 @@ namespace osu.Game.Overlays.BeatmapListing TabContainer.Spacing = new Vector2(10, 0); - if (typeof(T).IsEnum) + var type = typeof(T); + + if (type.IsEnum) { - foreach (var val in (T[])Enum.GetValues(typeof(T))) + if (Attribute.GetCustomAttribute(type, typeof(HasOrderedElementsAttribute)) != null) + { + var enumValues = Enum.GetValues(type).Cast().ToArray(); + var enumNames = Enum.GetNames(type); + + int[] enumPositions = Array.ConvertAll(enumNames, n => + { + var orderAttr = (OrderAttribute)type.GetField(n).GetCustomAttributes(typeof(OrderAttribute), false)[0]; + return orderAttr.Order; + }); + + Array.Sort(enumPositions, enumValues); + + foreach (var val in enumValues) + AddItem(val); + + return; + } + + foreach (var val in (T[])Enum.GetValues(type)) AddItem(val); } } From 20b49bea4b516180bfcddfd40cbc57635c38c84a Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 21 Feb 2020 01:49:03 +0300 Subject: [PATCH 0023/1117] Refactor SearchBeatmapSetsRequest --- .../API/Requests/SearchBeatmapSetsRequest.cs | 43 +++++++++++-------- osu.Game/Overlays/BeatmapListingOverlay.cs | 16 +++---- osu.Game/Overlays/DirectOverlay.cs | 10 ++--- 3 files changed, 38 insertions(+), 31 deletions(-) diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs index 58a41b6e08..aef0788b49 100644 --- a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs +++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs @@ -12,24 +12,31 @@ namespace osu.Game.Online.API.Requests { public class SearchBeatmapSetsRequest : APIRequest { + public BeatmapSearchCategory SearchCategory { get; set; } + + public DirectSortCriteria SortCriteria { get; set; } + + public SortDirection SortDirection { get; set; } + + public BeatmapSearchGenre Genre { get; set; } + + public BeatmapSearchLanguage Language { get; set; } + private readonly string query; private readonly RulesetInfo ruleset; - private readonly BeatmapSearchCategory searchCategory; - private readonly DirectSortCriteria sortCriteria; - private readonly SortDirection direction; - private readonly BeatmapSearchGenre genre; - private readonly BeatmapSearchLanguage language; - private string directionString => direction == SortDirection.Descending ? @"desc" : @"asc"; - public SearchBeatmapSetsRequest(string query, RulesetInfo ruleset, BeatmapSearchCategory searchCategory = BeatmapSearchCategory.Any, DirectSortCriteria sortCriteria = DirectSortCriteria.Ranked, SortDirection direction = SortDirection.Descending, BeatmapSearchGenre genre = BeatmapSearchGenre.Any, BeatmapSearchLanguage language = BeatmapSearchLanguage.Any) + private string directionString => SortDirection == SortDirection.Descending ? @"desc" : @"asc"; + + public SearchBeatmapSetsRequest(string query, RulesetInfo ruleset) { this.query = string.IsNullOrEmpty(query) ? string.Empty : System.Uri.EscapeDataString(query); this.ruleset = ruleset; - this.searchCategory = searchCategory; - this.sortCriteria = sortCriteria; - this.direction = direction; - this.genre = genre; - this.language = language; + + SearchCategory = BeatmapSearchCategory.Any; + SortCriteria = DirectSortCriteria.Ranked; + SortDirection = SortDirection.Descending; + Genre = BeatmapSearchGenre.Any; + Language = BeatmapSearchLanguage.Any; } protected override WebRequest CreateWebRequest() @@ -40,15 +47,15 @@ namespace osu.Game.Online.API.Requests if (ruleset.ID.HasValue) req.AddParameter("m", ruleset.ID.Value.ToString()); - req.AddParameter("s", searchCategory.ToString().ToLowerInvariant()); + req.AddParameter("s", SearchCategory.ToString().ToLowerInvariant()); - if (genre != BeatmapSearchGenre.Any) - req.AddParameter("g", ((int)genre).ToString()); + if (Genre != BeatmapSearchGenre.Any) + req.AddParameter("g", ((int)Genre).ToString()); - if (language != BeatmapSearchLanguage.Any) - req.AddParameter("l", ((int)language).ToString()); + if (Language != BeatmapSearchLanguage.Any) + req.AddParameter("l", ((int)Language).ToString()); - req.AddParameter("sort", $"{sortCriteria.ToString().ToLowerInvariant()}_{directionString}"); + req.AddParameter("sort", $"{SortCriteria.ToString().ToLowerInvariant()}_{directionString}"); return req; } diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index 414dabd7c1..5b7466df0d 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -233,14 +233,14 @@ namespace osu.Game.Overlays currentContent?.FadeColour(Color4.DimGray, 400, Easing.OutQuint); - getSetsRequest = new SearchBeatmapSetsRequest( - searchSection.Query.Value, - searchSection.Ruleset.Value, - searchSection.Category.Value, - sortControl.Current.Value, - sortControl.SortDirection.Value, - searchSection.Genre.Value, - searchSection.Language.Value); + getSetsRequest = new SearchBeatmapSetsRequest(searchSection.Query.Value, searchSection.Ruleset.Value) + { + SearchCategory = searchSection.Category.Value, + SortCriteria = sortControl.Current.Value, + SortDirection = sortControl.SortDirection.Value, + Genre = searchSection.Genre.Value, + Language = searchSection.Language.Value, + }; getSetsRequest.Success += response => Schedule(() => recreatePanels(response)); diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs index a6f8b65a0d..0620e687e5 100644 --- a/osu.Game/Overlays/DirectOverlay.cs +++ b/osu.Game/Overlays/DirectOverlay.cs @@ -254,11 +254,11 @@ namespace osu.Game.Overlays previewTrackManager.StopAnyPlaying(this); - getSetsRequest = new SearchBeatmapSetsRequest( - currentQuery.Value, - ((FilterControl)Filter).Ruleset.Value, - Filter.DisplayStyleControl.Dropdown.Current.Value, - Filter.Tabs.Current.Value); //todo: sort direction (?) + getSetsRequest = new SearchBeatmapSetsRequest(currentQuery.Value, ((FilterControl)Filter).Ruleset.Value) + { + SearchCategory = Filter.DisplayStyleControl.Dropdown.Current.Value, + SortCriteria = Filter.Tabs.Current.Value + }; getSetsRequest.Success += response => { From 3c56118f45f184f8a0a274db99e346530f0893ad Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 21 Feb 2020 02:28:33 +0300 Subject: [PATCH 0024/1117] Implement BeatmapSearchParameters and refactor all the components --- .../TestSceneBeatmapListingSearchSection.cs | 27 +++++++- .../BeatmapListingSearchSection.cs | 35 +++++++---- .../BeatmapListing/BeatmapSearchParameters.cs | 30 +++++++++ osu.Game/Overlays/BeatmapListingOverlay.cs | 63 ++++++------------- 4 files changed, 96 insertions(+), 59 deletions(-) create mode 100644 osu.Game/Overlays/BeatmapListing/BeatmapSearchParameters.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchSection.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchSection.cs index 1d8db71527..f809c780f1 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchSection.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchSection.cs @@ -11,6 +11,7 @@ using osu.Game.Beatmaps; using osu.Game.Graphics.Sprites; using osu.Game.Overlays; using osu.Game.Overlays.BeatmapListing; +using osu.Game.Online.API.Requests; using osuTK; namespace osu.Game.Tests.Visual.UserInterface @@ -32,6 +33,8 @@ namespace osu.Game.Tests.Visual.UserInterface OsuSpriteText query; OsuSpriteText ruleset; OsuSpriteText category; + OsuSpriteText genre; + OsuSpriteText language; Add(section = new BeatmapListingSearchSection { @@ -49,12 +52,19 @@ namespace osu.Game.Tests.Visual.UserInterface query = new OsuSpriteText(), ruleset = new OsuSpriteText(), category = new OsuSpriteText(), + genre = new OsuSpriteText(), + language = new OsuSpriteText(), } }); - section.Query.BindValueChanged(q => query.Text = $"Query: {q.NewValue}", true); - section.Ruleset.BindValueChanged(r => ruleset.Text = $"Ruleset: {r.NewValue}", true); - section.Category.BindValueChanged(c => category.Text = $"Category: {c.NewValue}", true); + section.SearchParameters.BindValueChanged(parameters => + { + query.Text = $"Query: {parameters.NewValue.Query}"; + ruleset.Text = $"Ruleset: {parameters.NewValue.Ruleset}"; + category.Text = $"Category: {parameters.NewValue.Category}"; + genre.Text = $"Genre: {parameters.NewValue.Genre}"; + language.Text = $"Language: {parameters.NewValue.Language}"; + }, true); } [Test] @@ -65,6 +75,17 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("Set null beatmap", () => section.BeatmapSet = null); } + [Test] + public void TestParametersSet() + { + AddStep("Set big black tag", () => section.SetTag("big black")); + AddAssert("Check query is big black", () => section.SearchParameters.Value.Query == "big black"); + AddStep("Set anime genre", () => section.SetGenre(BeatmapSearchGenre.Anime)); + AddAssert("Check genre is anime", () => section.SearchParameters.Value.Genre == BeatmapSearchGenre.Anime); + AddStep("Set japanese language", () => section.SetLanguage(BeatmapSearchLanguage.Japanese)); + AddAssert("Check language is japanese", () => section.SearchParameters.Value.Language == BeatmapSearchLanguage.Japanese); + } + private static readonly BeatmapSetInfo beatmap_set = new BeatmapSetInfo { OnlineInfo = new BeatmapSetOnlineInfo diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchSection.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchSection.cs index 28619ea6fe..121b101861 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchSection.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchSection.cs @@ -6,7 +6,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Online.API.Requests; -using osu.Game.Rulesets; using osuTK; using osu.Framework.Bindables; using osu.Game.Beatmaps.Drawables; @@ -19,15 +18,7 @@ namespace osu.Game.Overlays.BeatmapListing { public class BeatmapListingSearchSection : CompositeDrawable { - public Bindable Query => textBox.Current; - - public Bindable Ruleset => modeFilter.Current; - - public Bindable Category => categoryFilter.Current; - - public Bindable Genre => genreFilter.Current; - - public Bindable Language => languageFilter.Current; + public Bindable SearchParameters = new Bindable(); public BeatmapSetInfo BeatmapSet { @@ -113,7 +104,13 @@ namespace osu.Game.Overlays.BeatmapListing } }); - Category.Value = BeatmapSearchCategory.Leaderboard; + categoryFilter.Current.Value = BeatmapSearchCategory.Leaderboard; + + textBox.Current.BindValueChanged(_ => changeSearchParameters()); + modeFilter.Current.BindValueChanged(_ => changeSearchParameters()); + categoryFilter.Current.BindValueChanged(_ => changeSearchParameters()); + genreFilter.Current.BindValueChanged(_ => changeSearchParameters()); + languageFilter.Current.BindValueChanged(_ => changeSearchParameters(), true); } [BackgroundDependencyLoader] @@ -122,6 +119,22 @@ namespace osu.Game.Overlays.BeatmapListing background.Colour = colourProvider.Dark6; } + public void SetTag(string tag) => textBox.Current.Value = tag; + + public void SetGenre(BeatmapSearchGenre genre) => genreFilter.Current.Value = genre; + + public void SetLanguage(BeatmapSearchLanguage language) => languageFilter.Current.Value = language; + + private void changeSearchParameters() + { + SearchParameters.Value = new BeatmapSearchParameters( + textBox.Current.Value, + modeFilter.Current.Value, + categoryFilter.Current.Value, + genreFilter.Current.Value, + languageFilter.Current.Value); + } + private class BeatmapSearchTextBox : SearchTextBox { protected override Color4 SelectionColour => Color4.Gray; diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapSearchParameters.cs b/osu.Game/Overlays/BeatmapListing/BeatmapSearchParameters.cs new file mode 100644 index 0000000000..6a681503f5 --- /dev/null +++ b/osu.Game/Overlays/BeatmapListing/BeatmapSearchParameters.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 osu.Game.Online.API.Requests; +using osu.Game.Rulesets; + +namespace osu.Game.Overlays.BeatmapListing +{ + public class BeatmapSearchParameters + { + public readonly string Query; + + public readonly RulesetInfo Ruleset; + + public readonly BeatmapSearchCategory Category; + + public readonly BeatmapSearchGenre Genre; + + public readonly BeatmapSearchLanguage Language; + + public BeatmapSearchParameters(string query, RulesetInfo ruleset, BeatmapSearchCategory category, BeatmapSearchGenre genre, BeatmapSearchLanguage language) + { + Query = query; + Ruleset = ruleset; + Category = category; + Genre = genre; + Language = language; + } + } +} diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index 5b7466df0d..2449f561c1 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -144,70 +144,43 @@ namespace osu.Game.Overlays var sortCriteria = sortControl.Current; var sortDirection = sortControl.SortDirection; - searchSection.Query.BindValueChanged(query => + searchSection.SearchParameters.BindValueChanged(parameters => { - sortCriteria.Value = string.IsNullOrEmpty(query.NewValue) ? DirectSortCriteria.Ranked : DirectSortCriteria.Relevance; - sortDirection.Value = SortDirection.Descending; + if (parameters.OldValue.Query != parameters.NewValue.Query) + { + sortCriteria.Value = string.IsNullOrEmpty(parameters.NewValue.Query) ? DirectSortCriteria.Ranked : DirectSortCriteria.Relevance; + sortDirection.Value = SortDirection.Descending; - queueUpdateSearch(true); + queueUpdateSearch(true); + } + else + { + queueUpdateSearch(); + } }); - searchSection.Ruleset.BindValueChanged(_ => queueUpdateSearch()); - searchSection.Category.BindValueChanged(_ => queueUpdateSearch()); - searchSection.Genre.BindValueChanged(_ => queueUpdateSearch()); - searchSection.Language.BindValueChanged(_ => queueUpdateSearch()); sortCriteria.BindValueChanged(_ => queueUpdateSearch()); sortDirection.BindValueChanged(_ => queueUpdateSearch()); } public void ShowTag(string tag) { - var currentQuery = searchSection.Query.Value; - - if (currentQuery != tag) - { - setDefaultSearchValues(); - searchSection.Query.Value = tag; - } - + searchSection.SetTag(tag); Show(); } public void ShowGenre(BeatmapSearchGenre genre) { - var currentGenre = searchSection.Genre.Value; - - if (currentGenre != genre) - { - setDefaultSearchValues(); - searchSection.Genre.Value = genre; - } - + searchSection.SetGenre(genre); Show(); } public void ShowLanguage(BeatmapSearchLanguage language) { - var currentLanguage = searchSection.Language.Value; - - if (currentLanguage != language) - { - setDefaultSearchValues(); - searchSection.Language.Value = language; - } - + searchSection.SetLanguage(language); Show(); } - private void setDefaultSearchValues() - { - searchSection.Query.Value = string.Empty; - searchSection.Ruleset.Value = new RulesetInfo { Name = @"Any" }; - searchSection.Category.Value = BeatmapSearchCategory.Leaderboard; - searchSection.Genre.Value = BeatmapSearchGenre.Any; - searchSection.Language.Value = BeatmapSearchLanguage.Any; - } - private ScheduledDelegate queryChangedDebounce; private void queueUpdateSearch(bool queryTextChanged = false) @@ -233,13 +206,13 @@ namespace osu.Game.Overlays currentContent?.FadeColour(Color4.DimGray, 400, Easing.OutQuint); - getSetsRequest = new SearchBeatmapSetsRequest(searchSection.Query.Value, searchSection.Ruleset.Value) + getSetsRequest = new SearchBeatmapSetsRequest(searchSection.SearchParameters.Value.Query, searchSection.SearchParameters.Value.Ruleset) { - SearchCategory = searchSection.Category.Value, + SearchCategory = searchSection.SearchParameters.Value.Category, SortCriteria = sortControl.Current.Value, SortDirection = sortControl.SortDirection.Value, - Genre = searchSection.Genre.Value, - Language = searchSection.Language.Value, + Genre = searchSection.SearchParameters.Value.Genre, + Language = searchSection.SearchParameters.Value.Language }; getSetsRequest.Success += response => Schedule(() => recreatePanels(response)); From 1318f242c1cdd710b52ace6de9ce881ec24cb1fb Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 6 Mar 2020 02:12:30 +0300 Subject: [PATCH 0025/1117] Revert changes to basic implementation and remove redundant stuff --- .../Online/TestSceneBeatmapListingOverlay.cs | 19 -------- .../TestSceneBeatmapListingSearchSection.cs | 25 ++-------- .../BeatmapListingSearchSection.cs | 33 ++++--------- .../BeatmapListing/BeatmapSearchParameters.cs | 30 ------------ osu.Game/Overlays/BeatmapListingOverlay.cs | 47 +++++-------------- 5 files changed, 28 insertions(+), 126 deletions(-) delete mode 100644 osu.Game/Overlays/BeatmapListing/BeatmapSearchParameters.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs index 4dceb57129..7c05d99c59 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using osu.Game.Overlays; using NUnit.Framework; -using osu.Game.Online.API.Requests; namespace osu.Game.Tests.Visual.Online { @@ -25,24 +24,6 @@ namespace osu.Game.Tests.Visual.Online Add(overlay = new BeatmapListingOverlay()); } - [Test] - public void TestShowTag() - { - AddStep("Show Rem tag", () => overlay.ShowTag("Rem")); - } - - [Test] - public void TestShowGenre() - { - AddStep("Show Anime genre", () => overlay.ShowGenre(BeatmapSearchGenre.Anime)); - } - - [Test] - public void TestShowLanguage() - { - AddStep("Show Japanese language", () => overlay.ShowLanguage(BeatmapSearchLanguage.Japanese)); - } - [Test] public void TestShow() { diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchSection.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchSection.cs index f809c780f1..69e3fbd75f 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchSection.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchSection.cs @@ -11,7 +11,6 @@ using osu.Game.Beatmaps; using osu.Game.Graphics.Sprites; using osu.Game.Overlays; using osu.Game.Overlays.BeatmapListing; -using osu.Game.Online.API.Requests; using osuTK; namespace osu.Game.Tests.Visual.UserInterface @@ -57,14 +56,11 @@ namespace osu.Game.Tests.Visual.UserInterface } }); - section.SearchParameters.BindValueChanged(parameters => - { - query.Text = $"Query: {parameters.NewValue.Query}"; - ruleset.Text = $"Ruleset: {parameters.NewValue.Ruleset}"; - category.Text = $"Category: {parameters.NewValue.Category}"; - genre.Text = $"Genre: {parameters.NewValue.Genre}"; - language.Text = $"Language: {parameters.NewValue.Language}"; - }, true); + section.Query.BindValueChanged(q => query.Text = $"Query: {q.NewValue}", true); + section.Ruleset.BindValueChanged(r => ruleset.Text = $"Ruleset: {r.NewValue}", true); + section.Category.BindValueChanged(c => category.Text = $"Category: {c.NewValue}", true); + section.Genre.BindValueChanged(g => genre.Text = $"Genre: {g.NewValue}", true); + section.Language.BindValueChanged(l => language.Text = $"Language: {l.NewValue}", true); } [Test] @@ -75,17 +71,6 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("Set null beatmap", () => section.BeatmapSet = null); } - [Test] - public void TestParametersSet() - { - AddStep("Set big black tag", () => section.SetTag("big black")); - AddAssert("Check query is big black", () => section.SearchParameters.Value.Query == "big black"); - AddStep("Set anime genre", () => section.SetGenre(BeatmapSearchGenre.Anime)); - AddAssert("Check genre is anime", () => section.SearchParameters.Value.Genre == BeatmapSearchGenre.Anime); - AddStep("Set japanese language", () => section.SetLanguage(BeatmapSearchLanguage.Japanese)); - AddAssert("Check language is japanese", () => section.SearchParameters.Value.Language == BeatmapSearchLanguage.Japanese); - } - private static readonly BeatmapSetInfo beatmap_set = new BeatmapSetInfo { OnlineInfo = new BeatmapSetOnlineInfo diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchSection.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchSection.cs index 121b101861..501abbf2c8 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchSection.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchSection.cs @@ -13,12 +13,21 @@ using osu.Game.Beatmaps; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osuTK.Graphics; +using osu.Game.Rulesets; namespace osu.Game.Overlays.BeatmapListing { public class BeatmapListingSearchSection : CompositeDrawable { - public Bindable SearchParameters = new Bindable(); + public Bindable Query => textBox.Current; + + public Bindable Ruleset => modeFilter.Current; + + public Bindable Category => categoryFilter.Current; + + public Bindable Genre => genreFilter.Current; + + public Bindable Language => languageFilter.Current; public BeatmapSetInfo BeatmapSet { @@ -105,12 +114,6 @@ namespace osu.Game.Overlays.BeatmapListing }); categoryFilter.Current.Value = BeatmapSearchCategory.Leaderboard; - - textBox.Current.BindValueChanged(_ => changeSearchParameters()); - modeFilter.Current.BindValueChanged(_ => changeSearchParameters()); - categoryFilter.Current.BindValueChanged(_ => changeSearchParameters()); - genreFilter.Current.BindValueChanged(_ => changeSearchParameters()); - languageFilter.Current.BindValueChanged(_ => changeSearchParameters(), true); } [BackgroundDependencyLoader] @@ -119,22 +122,6 @@ namespace osu.Game.Overlays.BeatmapListing background.Colour = colourProvider.Dark6; } - public void SetTag(string tag) => textBox.Current.Value = tag; - - public void SetGenre(BeatmapSearchGenre genre) => genreFilter.Current.Value = genre; - - public void SetLanguage(BeatmapSearchLanguage language) => languageFilter.Current.Value = language; - - private void changeSearchParameters() - { - SearchParameters.Value = new BeatmapSearchParameters( - textBox.Current.Value, - modeFilter.Current.Value, - categoryFilter.Current.Value, - genreFilter.Current.Value, - languageFilter.Current.Value); - } - private class BeatmapSearchTextBox : SearchTextBox { protected override Color4 SelectionColour => Color4.Gray; diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapSearchParameters.cs b/osu.Game/Overlays/BeatmapListing/BeatmapSearchParameters.cs deleted file mode 100644 index 6a681503f5..0000000000 --- a/osu.Game/Overlays/BeatmapListing/BeatmapSearchParameters.cs +++ /dev/null @@ -1,30 +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.Online.API.Requests; -using osu.Game.Rulesets; - -namespace osu.Game.Overlays.BeatmapListing -{ - public class BeatmapSearchParameters - { - public readonly string Query; - - public readonly RulesetInfo Ruleset; - - public readonly BeatmapSearchCategory Category; - - public readonly BeatmapSearchGenre Genre; - - public readonly BeatmapSearchLanguage Language; - - public BeatmapSearchParameters(string query, RulesetInfo ruleset, BeatmapSearchCategory category, BeatmapSearchGenre genre, BeatmapSearchLanguage language) - { - Query = query; - Ruleset = ruleset; - Category = category; - Genre = genre; - Language = language; - } - } -} diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index ebe4b7fe61..1a5257457f 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -153,43 +153,22 @@ namespace osu.Game.Overlays var sortCriteria = sortControl.Current; var sortDirection = sortControl.SortDirection; - searchSection.SearchParameters.BindValueChanged(parameters => + searchSection.Query.BindValueChanged(query => { - if (parameters.OldValue.Query != parameters.NewValue.Query) - { - sortCriteria.Value = string.IsNullOrEmpty(parameters.NewValue.Query) ? DirectSortCriteria.Ranked : DirectSortCriteria.Relevance; - sortDirection.Value = SortDirection.Descending; - - queueUpdateSearch(true); - } - else - { - queueUpdateSearch(); - } + sortCriteria.Value = string.IsNullOrEmpty(query.NewValue) ? DirectSortCriteria.Ranked : DirectSortCriteria.Relevance; + sortDirection.Value = SortDirection.Descending; + queueUpdateSearch(true); }); + searchSection.Ruleset.BindValueChanged(_ => queueUpdateSearch()); + searchSection.Category.BindValueChanged(_ => queueUpdateSearch()); + searchSection.Genre.BindValueChanged(_ => queueUpdateSearch()); + searchSection.Language.BindValueChanged(_ => queueUpdateSearch()); + sortCriteria.BindValueChanged(_ => queueUpdateSearch()); sortDirection.BindValueChanged(_ => queueUpdateSearch()); } - public void ShowTag(string tag) - { - searchSection.SetTag(tag); - Show(); - } - - public void ShowGenre(BeatmapSearchGenre genre) - { - searchSection.SetGenre(genre); - Show(); - } - - public void ShowLanguage(BeatmapSearchLanguage language) - { - searchSection.SetLanguage(language); - Show(); - } - private ScheduledDelegate queryChangedDebounce; private LoadingLayer loadingLayer; @@ -218,13 +197,13 @@ namespace osu.Game.Overlays loadingLayer.Show(); - getSetsRequest = new SearchBeatmapSetsRequest(searchSection.SearchParameters.Value.Query, searchSection.SearchParameters.Value.Ruleset) + getSetsRequest = new SearchBeatmapSetsRequest(searchSection.Query.Value, searchSection.Ruleset.Value) { - SearchCategory = searchSection.SearchParameters.Value.Category, + SearchCategory = searchSection.Category.Value, SortCriteria = sortControl.Current.Value, SortDirection = sortControl.SortDirection.Value, - Genre = searchSection.SearchParameters.Value.Genre, - Language = searchSection.SearchParameters.Value.Language + Genre = searchSection.Genre.Value, + Language = searchSection.Language.Value }; getSetsRequest.Success += response => Schedule(() => recreatePanels(response)); From c7384b9717a7cf9d063a502a8b219c04f98cc991 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 6 Mar 2020 03:09:43 +0300 Subject: [PATCH 0026/1117] Implement BeatmapListingSearchHandler component --- .../Online/TestSceneBeatmapListingOverlay.cs | 2 + .../BeatmapListingSearchHandler.cs | 163 +++++++++++++++++ osu.Game/Overlays/BeatmapListingOverlay.cs | 172 +++--------------- 3 files changed, 191 insertions(+), 146 deletions(-) create mode 100644 osu.Game/Overlays/BeatmapListing/BeatmapListingSearchHandler.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs index 7c05d99c59..f80687e142 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using osu.Game.Overlays; using NUnit.Framework; +using osu.Game.Overlays.BeatmapListing; namespace osu.Game.Tests.Visual.Online { @@ -13,6 +14,7 @@ namespace osu.Game.Tests.Visual.Online public override IReadOnlyList RequiredTypes => new[] { typeof(BeatmapListingOverlay), + typeof(BeatmapListingSearchHandler) }; protected override bool UseOnlineAPI => true; diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchHandler.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchHandler.cs new file mode 100644 index 0000000000..ce3d37fb98 --- /dev/null +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchHandler.cs @@ -0,0 +1,163 @@ +// 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; +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.Framework.Threading; +using osu.Game.Beatmaps; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Overlays.Direct; +using osu.Game.Rulesets; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Overlays.BeatmapListing +{ + public class BeatmapListingSearchHandler : CompositeDrawable + { + public Action> SearchFinished; + public Action SearchStarted; + + [Resolved] + private IAPIProvider api { get; set; } + + [Resolved] + private RulesetStore rulesets { get; set; } + + private readonly BeatmapListingSearchSection searchSection; + private readonly BeatmapListingSortTabControl sortControl; + private readonly Box sortControlBackground; + + private SearchBeatmapSetsRequest getSetsRequest; + + public BeatmapListingSearchHandler() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + InternalChild = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 10), + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Masking = true, + EdgeEffect = new EdgeEffectParameters + { + Colour = Color4.Black.Opacity(0.25f), + Type = EdgeEffectType.Shadow, + Radius = 3, + Offset = new Vector2(0f, 1f), + }, + Child = searchSection = new BeatmapListingSearchSection(), + }, + new Container + { + RelativeSizeAxes = Axes.X, + Height = 40, + Children = new Drawable[] + { + sortControlBackground = new Box + { + RelativeSizeAxes = Axes.Both + }, + sortControl = new BeatmapListingSortTabControl + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Margin = new MarginPadding { Left = 20 } + } + } + } + } + }; + } + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + sortControlBackground.Colour = colourProvider.Background5; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + var sortCriteria = sortControl.Current; + var sortDirection = sortControl.SortDirection; + + searchSection.Query.BindValueChanged(query => + { + sortCriteria.Value = string.IsNullOrEmpty(query.NewValue) ? DirectSortCriteria.Ranked : DirectSortCriteria.Relevance; + sortDirection.Value = SortDirection.Descending; + queueUpdateSearch(true); + }); + + searchSection.Ruleset.BindValueChanged(_ => queueUpdateSearch()); + searchSection.Category.BindValueChanged(_ => queueUpdateSearch()); + searchSection.Genre.BindValueChanged(_ => queueUpdateSearch()); + searchSection.Language.BindValueChanged(_ => queueUpdateSearch()); + + sortCriteria.BindValueChanged(_ => queueUpdateSearch()); + sortDirection.BindValueChanged(_ => queueUpdateSearch()); + } + + private ScheduledDelegate queryChangedDebounce; + + private void queueUpdateSearch(bool queryTextChanged = false) + { + SearchStarted?.Invoke(); + + getSetsRequest?.Cancel(); + + queryChangedDebounce?.Cancel(); + queryChangedDebounce = Scheduler.AddDelayed(updateSearch, queryTextChanged ? 500 : 100); + } + + private void updateSearch() + { + getSetsRequest = new SearchBeatmapSetsRequest(searchSection.Query.Value, searchSection.Ruleset.Value) + { + SearchCategory = searchSection.Category.Value, + SortCriteria = sortControl.Current.Value, + SortDirection = sortControl.SortDirection.Value, + Genre = searchSection.Genre.Value, + Language = searchSection.Language.Value + }; + + getSetsRequest.Success += response => Schedule(() => onSearchFinished(response)); + + api.Queue(getSetsRequest); + } + + private void onSearchFinished(SearchBeatmapSetsResponse response) + { + var beatmaps = response.BeatmapSets.Select(r => r.ToBeatmapSet(rulesets)).ToList(); + + searchSection.BeatmapSet = response.Total == 0 ? null : beatmaps.First(); + + SearchFinished?.Invoke(beatmaps); + } + + protected override void Dispose(bool isDisposing) + { + getSetsRequest?.Cancel(); + queryChangedDebounce?.Cancel(); + + base.Dispose(isDisposing); + } + } +} diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index 1a5257457f..dd8dc4a79d 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -1,27 +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.Collections.Generic; using System.Linq; +using System.Threading; 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.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; -using osu.Framework.Threading; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; -using osu.Game.Online.API.Requests; using osu.Game.Overlays.BeatmapListing; using osu.Game.Overlays.Direct; -using osu.Game.Rulesets; using osuTK; -using osuTK.Graphics; namespace osu.Game.Overlays { @@ -30,14 +26,9 @@ namespace osu.Game.Overlays [Resolved] private PreviewTrackManager previewTrackManager { get; set; } - [Resolved] - private RulesetStore rulesets { get; set; } - - private SearchBeatmapSetsRequest getSetsRequest; - private Drawable currentContent; - private BeatmapListingSearchSection searchSection; - private BeatmapListingSortTabControl sortControl; + private LoadingLayer loadingLayer; + private Container panelTarget; public BeatmapListingOverlay() : base(OverlayColourScheme.Blue) @@ -63,27 +54,13 @@ namespace osu.Game.Overlays AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 10), Children = new Drawable[] { - new FillFlowContainer + new BeatmapListingHeader(), + new BeatmapListingSearchHandler { - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - Direction = FillDirection.Vertical, - Masking = true, - EdgeEffect = new EdgeEffectParameters - { - Colour = Color4.Black.Opacity(0.25f), - Type = EdgeEffectType.Shadow, - Radius = 3, - Offset = new Vector2(0f, 1f), - }, - Children = new Drawable[] - { - new BeatmapListingHeader(), - searchSection = new BeatmapListingSearchSection(), - } + SearchStarted = onSearchStarted, + SearchFinished = onSearchFinished, }, new Container { @@ -96,132 +73,41 @@ namespace osu.Game.Overlays RelativeSizeAxes = Axes.Both, Colour = ColourProvider.Background4, }, - new FillFlowContainer + panelTarget = new Container { - RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Children = new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.X, - Height = 40, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = ColourProvider.Background5 - }, - sortControl = new BeatmapListingSortTabControl - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Margin = new MarginPadding { Left = 20 } - } - } - }, - new Container - { - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - Padding = new MarginPadding { Horizontal = 20 }, - Children = new Drawable[] - { - panelTarget = new Container - { - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - }, - loadingLayer = new LoadingLayer(panelTarget), - } - }, - } - } + RelativeSizeAxes = Axes.X, + Padding = new MarginPadding { Horizontal = 20 } + }, + loadingLayer = new LoadingLayer(panelTarget) } - } + }, } } } }; } - protected override void LoadComplete() + private CancellationTokenSource cancellationToken; + + private void onSearchStarted() { - base.LoadComplete(); - - var sortCriteria = sortControl.Current; - var sortDirection = sortControl.SortDirection; - - searchSection.Query.BindValueChanged(query => - { - sortCriteria.Value = string.IsNullOrEmpty(query.NewValue) ? DirectSortCriteria.Ranked : DirectSortCriteria.Relevance; - sortDirection.Value = SortDirection.Descending; - queueUpdateSearch(true); - }); - - searchSection.Ruleset.BindValueChanged(_ => queueUpdateSearch()); - searchSection.Category.BindValueChanged(_ => queueUpdateSearch()); - searchSection.Genre.BindValueChanged(_ => queueUpdateSearch()); - searchSection.Language.BindValueChanged(_ => queueUpdateSearch()); - - sortCriteria.BindValueChanged(_ => queueUpdateSearch()); - sortDirection.BindValueChanged(_ => queueUpdateSearch()); - } - - private ScheduledDelegate queryChangedDebounce; - - private LoadingLayer loadingLayer; - private Container panelTarget; - - private void queueUpdateSearch(bool queryTextChanged = false) - { - getSetsRequest?.Cancel(); - - queryChangedDebounce?.Cancel(); - queryChangedDebounce = Scheduler.AddDelayed(updateSearch, queryTextChanged ? 500 : 100); - } - - private void updateSearch() - { - if (!IsLoaded) - return; - - if (State.Value == Visibility.Hidden) - return; - - if (API == null) - return; + cancellationToken?.Cancel(); previewTrackManager.StopAnyPlaying(this); - loadingLayer.Show(); - - getSetsRequest = new SearchBeatmapSetsRequest(searchSection.Query.Value, searchSection.Ruleset.Value) - { - SearchCategory = searchSection.Category.Value, - SortCriteria = sortControl.Current.Value, - SortDirection = sortControl.SortDirection.Value, - Genre = searchSection.Genre.Value, - Language = searchSection.Language.Value - }; - - getSetsRequest.Success += response => Schedule(() => recreatePanels(response)); - - API.Queue(getSetsRequest); + if (panelTarget.Any()) + loadingLayer.Show(); } - private void recreatePanels(SearchBeatmapSetsResponse response) + private void onSearchFinished(List beatmaps) { - if (response.Total == 0) + if (!beatmaps.Any()) { - searchSection.BeatmapSet = null; - LoadComponentAsync(new NotFoundDrawable(), addContentToPlaceholder); + LoadComponentAsync(new NotFoundDrawable(), addContentToPlaceholder, (cancellationToken = new CancellationTokenSource()).Token); return; } - var beatmaps = response.BeatmapSets.Select(r => r.ToBeatmapSet(rulesets)).ToList(); - var newPanels = new FillFlowContainer { RelativeSizeAxes = Axes.X, @@ -236,18 +122,14 @@ namespace osu.Game.Overlays }) }; - LoadComponentAsync(newPanels, loaded => - { - addContentToPlaceholder(loaded); - searchSection.BeatmapSet = beatmaps.First(); - }); + LoadComponentAsync(newPanels, addContentToPlaceholder, (cancellationToken = new CancellationTokenSource()).Token); } private void addContentToPlaceholder(Drawable content) { loadingLayer.Hide(); - Drawable lastContent = currentContent; + var lastContent = currentContent; if (lastContent != null) { @@ -266,9 +148,7 @@ namespace osu.Game.Overlays protected override void Dispose(bool isDisposing) { - getSetsRequest?.Cancel(); - queryChangedDebounce?.Cancel(); - + cancellationToken?.Cancel(); base.Dispose(isDisposing); } From 1aacd1aaa2568eecb1f00ecc1b77cb0ae406082f Mon Sep 17 00:00:00 2001 From: Lucas A Date: Mon, 16 Mar 2020 20:43:02 +0100 Subject: [PATCH 0027/1117] Initial implementation of LowHealthLayer --- osu.Game/Configuration/OsuConfigManager.cs | 2 + .../Sections/Gameplay/GeneralSettings.cs | 6 +++ osu.Game/Screens/Play/HUD/LowHealthLayer.cs | 47 +++++++++++++++++++ 3 files changed, 55 insertions(+) create mode 100644 osu.Game/Screens/Play/HUD/LowHealthLayer.cs diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 21de654670..895bacafc4 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -88,6 +88,7 @@ namespace osu.Game.Configuration Set(OsuSetting.ShowInterface, true); Set(OsuSetting.ShowProgressGraph, true); Set(OsuSetting.ShowHealthDisplayWhenCantFail, true); + Set(OsuSetting.FadePlayfieldWhenLowHealth, true); Set(OsuSetting.KeyOverlay, false); Set(OsuSetting.ScoreMeter, ScoreMeterType.HitErrorBoth); @@ -183,6 +184,7 @@ namespace osu.Game.Configuration ShowInterface, ShowProgressGraph, ShowHealthDisplayWhenCantFail, + FadePlayfieldWhenLowHealth, MouseDisableButtons, MouseDisableWheel, AudioOffset, diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs index 2d2cd42213..6b6b3e8fa4 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs @@ -53,6 +53,12 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay Keywords = new[] { "hp", "bar" } }, new SettingsCheckbox + { + LabelText = "Fade playfield to red when health is low", + Bindable = config.GetBindable(OsuSetting.FadePlayfieldWhenLowHealth), + Keywords = new[] { "hp", "playfield", "health" } + }, + new SettingsCheckbox { LabelText = "Always show key overlay", Bindable = config.GetBindable(OsuSetting.KeyOverlay) diff --git a/osu.Game/Screens/Play/HUD/LowHealthLayer.cs b/osu.Game/Screens/Play/HUD/LowHealthLayer.cs new file mode 100644 index 0000000000..8f03a95877 --- /dev/null +++ b/osu.Game/Screens/Play/HUD/LowHealthLayer.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 osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Game.Configuration; +using osu.Game.Graphics; + +namespace osu.Game.Screens.Play.HUD +{ + public class LowHealthLayer : HealthDisplay + { + private const float max_alpha = 0.4f; + + private const double fade_time = 300; + + private readonly Box box; + + private Bindable configFadeRedWhenLowHealth; + + public LowHealthLayer() + { + Child = box = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0 + }; + } + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config, OsuColour color) + { + configFadeRedWhenLowHealth = config.GetBindable(OsuSetting.FadePlayfieldWhenLowHealth); + box.Colour = color.Red; + + configFadeRedWhenLowHealth.BindValueChanged(value => + { + if (value.NewValue) + this.FadeIn(fade_time, Easing.OutQuint); + else + this.FadeOut(fade_time, Easing.OutQuint); + }, true); + } + } +} From 8c611a981f0fc35ab89fb8012157dc7c62cecb00 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Mon, 16 Mar 2020 21:48:28 +0100 Subject: [PATCH 0028/1117] Update visual tests --- .../Visual/Gameplay/TestSceneHUDOverlay.cs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs index fc03dc6ed3..579f6ff9b6 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs @@ -103,6 +103,38 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("return value", () => config.Set(OsuSetting.KeyOverlay, keyCounterVisibleValue)); } + [Test] + public void TestChangeHealthValue() + { + void applyToHealthDisplays(double value) + { + if (hudOverlay == null) return; + + hudOverlay.LowHealthDisplay.Current.Value = value; + hudOverlay.HealthDisplay.Current.Value = value; + } + + createNew(); + AddSliderStep("health value", 0, 1, 0.5, applyToHealthDisplays); + + AddStep("enable low health display", () => + { + config.Set(OsuSetting.FadePlayfieldWhenLowHealth, true); + hudOverlay.LowHealthDisplay.FinishTransforms(true); + }); + AddAssert("low health display is visible", () => hudOverlay.LowHealthDisplay.IsPresent); + AddStep("set health to 30%", () => applyToHealthDisplays(0.3)); + AddAssert("hud is not faded to red", () => !hudOverlay.LowHealthDisplay.Child.IsPresent); + AddStep("set health to < 10%", () => applyToHealthDisplays(0.1f)); + AddAssert("hud is faded to red", () => hudOverlay.LowHealthDisplay.Child.IsPresent); + AddStep("disable low health display", () => + { + config.Set(OsuSetting.FadePlayfieldWhenLowHealth, false); + hudOverlay.LowHealthDisplay.FinishTransforms(true); + }); + AddAssert("low health display is not visible", () => !hudOverlay.LowHealthDisplay.IsPresent); + } + private void createNew(Action action = null) { AddStep("create overlay", () => From 6b0c5bc65d1f6aa80fd98abd2261613ca971fbbc Mon Sep 17 00:00:00 2001 From: Lucas A Date: Tue, 17 Mar 2020 22:32:07 +0100 Subject: [PATCH 0029/1117] Rename to LowHealthLayer to FaillingLayer. --- .../Visual/Gameplay/TestSceneHUDOverlay.cs | 32 ------------- osu.Game/Screens/Play/HUD/FaillingLayer.cs | 47 +++++++++++++++++++ osu.Game/Screens/Play/HUD/LowHealthLayer.cs | 47 ------------------- 3 files changed, 47 insertions(+), 79 deletions(-) create mode 100644 osu.Game/Screens/Play/HUD/FaillingLayer.cs delete mode 100644 osu.Game/Screens/Play/HUD/LowHealthLayer.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs index 579f6ff9b6..fc03dc6ed3 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs @@ -103,38 +103,6 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("return value", () => config.Set(OsuSetting.KeyOverlay, keyCounterVisibleValue)); } - [Test] - public void TestChangeHealthValue() - { - void applyToHealthDisplays(double value) - { - if (hudOverlay == null) return; - - hudOverlay.LowHealthDisplay.Current.Value = value; - hudOverlay.HealthDisplay.Current.Value = value; - } - - createNew(); - AddSliderStep("health value", 0, 1, 0.5, applyToHealthDisplays); - - AddStep("enable low health display", () => - { - config.Set(OsuSetting.FadePlayfieldWhenLowHealth, true); - hudOverlay.LowHealthDisplay.FinishTransforms(true); - }); - AddAssert("low health display is visible", () => hudOverlay.LowHealthDisplay.IsPresent); - AddStep("set health to 30%", () => applyToHealthDisplays(0.3)); - AddAssert("hud is not faded to red", () => !hudOverlay.LowHealthDisplay.Child.IsPresent); - AddStep("set health to < 10%", () => applyToHealthDisplays(0.1f)); - AddAssert("hud is faded to red", () => hudOverlay.LowHealthDisplay.Child.IsPresent); - AddStep("disable low health display", () => - { - config.Set(OsuSetting.FadePlayfieldWhenLowHealth, false); - hudOverlay.LowHealthDisplay.FinishTransforms(true); - }); - AddAssert("low health display is not visible", () => !hudOverlay.LowHealthDisplay.IsPresent); - } - private void createNew(Action action = null) { AddStep("create overlay", () => diff --git a/osu.Game/Screens/Play/HUD/FaillingLayer.cs b/osu.Game/Screens/Play/HUD/FaillingLayer.cs new file mode 100644 index 0000000000..3dc18cefec --- /dev/null +++ b/osu.Game/Screens/Play/HUD/FaillingLayer.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; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; + +namespace osu.Game.Screens.Play.HUD +{ + /// + /// An overlay layer on top of the player HUD which fades to red when the current player health falls a certain threshold defined by . + /// + public class FaillingLayer : HealthDisplay + { + private const float max_alpha = 0.4f; + + private readonly Box box; + + /// + /// The threshold under which the current player life should be considered low and the layer should start fading in. + /// + protected virtual double LowHealthThreshold => 0.20f; + + public FaillingLayer() + { + Child = box = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0 + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour color) + { + box.Colour = color.Red; + } + + protected override void Update() + { + box.Alpha = (float)Math.Clamp(max_alpha * (1 - Current.Value / LowHealthThreshold), 0, max_alpha); + base.Update(); + } + } +} diff --git a/osu.Game/Screens/Play/HUD/LowHealthLayer.cs b/osu.Game/Screens/Play/HUD/LowHealthLayer.cs deleted file mode 100644 index 8f03a95877..0000000000 --- a/osu.Game/Screens/Play/HUD/LowHealthLayer.cs +++ /dev/null @@ -1,47 +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.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Shapes; -using osu.Game.Configuration; -using osu.Game.Graphics; - -namespace osu.Game.Screens.Play.HUD -{ - public class LowHealthLayer : HealthDisplay - { - private const float max_alpha = 0.4f; - - private const double fade_time = 300; - - private readonly Box box; - - private Bindable configFadeRedWhenLowHealth; - - public LowHealthLayer() - { - Child = box = new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0 - }; - } - - [BackgroundDependencyLoader] - private void load(OsuConfigManager config, OsuColour color) - { - configFadeRedWhenLowHealth = config.GetBindable(OsuSetting.FadePlayfieldWhenLowHealth); - box.Colour = color.Red; - - configFadeRedWhenLowHealth.BindValueChanged(value => - { - if (value.NewValue) - this.FadeIn(fade_time, Easing.OutQuint); - else - this.FadeOut(fade_time, Easing.OutQuint); - }, true); - } - } -} From ed4f9f8ba9959c142dc1282ef37e735a4a162b7e Mon Sep 17 00:00:00 2001 From: Lucas A Date: Tue, 17 Mar 2020 22:57:47 +0100 Subject: [PATCH 0030/1117] Bind every HealthDisplay on Player load --- osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs | 8 ++++++++ osu.Game/Screens/Play/HUD/FaillingLayer.cs | 1 + osu.Game/Screens/Play/HUD/HealthDisplay.cs | 6 ++++++ osu.Game/Screens/Play/Player.cs | 4 ++++ 4 files changed, 19 insertions(+) diff --git a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs index a37ef8d9a0..50bff4fe3a 100644 --- a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using osu.Framework.Allocation; using osu.Framework.Input; using osu.Game.Beatmaps; using osu.Game.Input.Handlers; @@ -16,6 +17,7 @@ using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Replays; using osu.Game.Rulesets.UI; using osu.Game.Screens.Play; +using osu.Game.Screens.Play.HUD; using osuTK; namespace osu.Game.Rulesets.Osu.UI @@ -29,6 +31,12 @@ namespace osu.Game.Rulesets.Osu.UI { } + [BackgroundDependencyLoader] + private void load() + { + Overlays.Add(new FaillingLayer()); + } + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; // always show the gameplay cursor protected override Playfield CreatePlayfield() => new OsuPlayfield(); diff --git a/osu.Game/Screens/Play/HUD/FaillingLayer.cs b/osu.Game/Screens/Play/HUD/FaillingLayer.cs index 3dc18cefec..6651ad6c88 100644 --- a/osu.Game/Screens/Play/HUD/FaillingLayer.cs +++ b/osu.Game/Screens/Play/HUD/FaillingLayer.cs @@ -25,6 +25,7 @@ namespace osu.Game.Screens.Play.HUD public FaillingLayer() { + RelativeSizeAxes = Axes.Both; Child = box = new Box { RelativeSizeAxes = Axes.Both, diff --git a/osu.Game/Screens/Play/HUD/HealthDisplay.cs b/osu.Game/Screens/Play/HUD/HealthDisplay.cs index 37038ad58c..6a5b77a64b 100644 --- a/osu.Game/Screens/Play/HUD/HealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/HealthDisplay.cs @@ -3,6 +3,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Screens.Play.HUD { @@ -13,5 +14,10 @@ namespace osu.Game.Screens.Play.HUD MinValue = 0, MaxValue = 1 }; + + public virtual void BindHealthProcessor(HealthProcessor processor) + { + Current.BindTo(processor.Health); + } } } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index bcadba14af..0df4aacb7a 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -24,6 +24,7 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Scoring; +using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Ranking; using osu.Game.Skinning; using osu.Game.Users; @@ -184,6 +185,9 @@ namespace osu.Game.Screens.Play foreach (var mod in Mods.Value.OfType()) mod.ApplyToHealthProcessor(HealthProcessor); + foreach (var overlay in DrawableRuleset.Overlays.OfType()) + overlay.BindHealthProcessor(HealthProcessor); + BreakOverlay.IsBreakTime.BindValueChanged(onBreakTimeChanged, true); } From 44c13b081c4167685ace193a5a6fadae95072fcf Mon Sep 17 00:00:00 2001 From: Lucas A Date: Tue, 17 Mar 2020 22:58:20 +0100 Subject: [PATCH 0031/1117] Remove old configuration variants. --- osu.Game/Configuration/OsuConfigManager.cs | 2 -- .../Overlays/Settings/Sections/Gameplay/GeneralSettings.cs | 6 ------ 2 files changed, 8 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 895bacafc4..21de654670 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -88,7 +88,6 @@ namespace osu.Game.Configuration Set(OsuSetting.ShowInterface, true); Set(OsuSetting.ShowProgressGraph, true); Set(OsuSetting.ShowHealthDisplayWhenCantFail, true); - Set(OsuSetting.FadePlayfieldWhenLowHealth, true); Set(OsuSetting.KeyOverlay, false); Set(OsuSetting.ScoreMeter, ScoreMeterType.HitErrorBoth); @@ -184,7 +183,6 @@ namespace osu.Game.Configuration ShowInterface, ShowProgressGraph, ShowHealthDisplayWhenCantFail, - FadePlayfieldWhenLowHealth, MouseDisableButtons, MouseDisableWheel, AudioOffset, diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs index 6b6b3e8fa4..2d2cd42213 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs @@ -53,12 +53,6 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay Keywords = new[] { "hp", "bar" } }, new SettingsCheckbox - { - LabelText = "Fade playfield to red when health is low", - Bindable = config.GetBindable(OsuSetting.FadePlayfieldWhenLowHealth), - Keywords = new[] { "hp", "playfield", "health" } - }, - new SettingsCheckbox { LabelText = "Always show key overlay", Bindable = config.GetBindable(OsuSetting.KeyOverlay) From 17bae532bd91e782ac9be727843b4e8f57456df9 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Tue, 17 Mar 2020 23:09:50 +0100 Subject: [PATCH 0032/1117] Add failling layer to others rulesets. --- osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs | 8 ++++++++ osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs | 3 +++ 2 files changed, 11 insertions(+) diff --git a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs index fd8a1d175d..705c2d756c 100644 --- a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using osu.Framework.Allocation; using osu.Framework.Input; using osu.Game.Beatmaps; using osu.Game.Configuration; @@ -14,6 +15,7 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; +using osu.Game.Screens.Play.HUD; namespace osu.Game.Rulesets.Catch.UI { @@ -30,6 +32,12 @@ namespace osu.Game.Rulesets.Catch.UI TimeRange.Value = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450); } + [BackgroundDependencyLoader] + private void load() + { + Overlays.Add(new FaillingLayer()); + } + protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay); protected override Playfield CreatePlayfield() => new CatchPlayfield(Beatmap.BeatmapInfo.BaseDifficulty, CreateDrawableRepresentation); diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs index 2c497541a8..b8b6ff3c3c 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs @@ -19,6 +19,7 @@ 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.Screens.Play.HUD; using osuTK; namespace osu.Game.Rulesets.Mania.UI @@ -52,6 +53,8 @@ namespace osu.Game.Rulesets.Mania.UI configDirection.BindValueChanged(direction => Direction.Value = (ScrollingDirection)direction.NewValue, true); Config.BindWith(ManiaRulesetSetting.ScrollTime, TimeRange); + + Overlays.Add(new FaillingLayer()); } /// From a1274a9eb0ca927c4d07cb94aaba0fa101745a4a Mon Sep 17 00:00:00 2001 From: Lucas A Date: Wed, 18 Mar 2020 08:17:41 +0100 Subject: [PATCH 0033/1117] Fix and add missing XMLDoc --- osu.Game/Screens/Play/HUD/FaillingLayer.cs | 2 +- osu.Game/Screens/Play/HUD/HealthDisplay.cs | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/FaillingLayer.cs b/osu.Game/Screens/Play/HUD/FaillingLayer.cs index 6651ad6c88..55cc4476b0 100644 --- a/osu.Game/Screens/Play/HUD/FaillingLayer.cs +++ b/osu.Game/Screens/Play/HUD/FaillingLayer.cs @@ -10,7 +10,7 @@ using osu.Game.Graphics; namespace osu.Game.Screens.Play.HUD { /// - /// An overlay layer on top of the player HUD which fades to red when the current player health falls a certain threshold defined by . + /// An overlay layer on top of the playfield which fades to red when the current player health falls a certain threshold defined by . /// public class FaillingLayer : HealthDisplay { diff --git a/osu.Game/Screens/Play/HUD/HealthDisplay.cs b/osu.Game/Screens/Play/HUD/HealthDisplay.cs index 6a5b77a64b..4094b3de69 100644 --- a/osu.Game/Screens/Play/HUD/HealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/HealthDisplay.cs @@ -4,9 +4,14 @@ using osu.Framework.Bindables; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI; namespace osu.Game.Screens.Play.HUD { + /// + /// A container for components displaying the current player health. + /// Gets bound automatically to the when inserted to hierarchy. + /// public abstract class HealthDisplay : Container { public readonly BindableDouble Current = new BindableDouble @@ -14,7 +19,11 @@ namespace osu.Game.Screens.Play.HUD MinValue = 0, MaxValue = 1 }; - + + /// + /// Bind the tracked fields of to this health display. + /// + /// public virtual void BindHealthProcessor(HealthProcessor processor) { Current.BindTo(processor.Health); From e9f224b5e8c3ae93098848ee3ee2146d47e7146e Mon Sep 17 00:00:00 2001 From: Lucas A Date: Wed, 18 Mar 2020 21:16:54 +0100 Subject: [PATCH 0034/1117] Apply review suggestions --- osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs | 2 +- osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs | 2 +- osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs | 2 +- .../Play/HUD/{FaillingLayer.cs => FailingLayer.cs} | 8 ++++---- osu.Game/Screens/Play/HUD/HealthDisplay.cs | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) rename osu.Game/Screens/Play/HUD/{FaillingLayer.cs => FailingLayer.cs} (82%) diff --git a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs index 705c2d756c..50c4154c61 100644 --- a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs @@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Catch.UI [BackgroundDependencyLoader] private void load() { - Overlays.Add(new FaillingLayer()); + Overlays.Add(new FailingLayer()); } protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay); diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs index b8b6ff3c3c..8e56144752 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs @@ -54,7 +54,7 @@ namespace osu.Game.Rulesets.Mania.UI Config.BindWith(ManiaRulesetSetting.ScrollTime, TimeRange); - Overlays.Add(new FaillingLayer()); + Overlays.Add(new FailingLayer()); } /// diff --git a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs index 50bff4fe3a..ed75d47bbe 100644 --- a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs @@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.UI [BackgroundDependencyLoader] private void load() { - Overlays.Add(new FaillingLayer()); + Overlays.Add(new FailingLayer()); } public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; // always show the gameplay cursor diff --git a/osu.Game/Screens/Play/HUD/FaillingLayer.cs b/osu.Game/Screens/Play/HUD/FailingLayer.cs similarity index 82% rename from osu.Game/Screens/Play/HUD/FaillingLayer.cs rename to osu.Game/Screens/Play/HUD/FailingLayer.cs index 55cc4476b0..5f7dc77928 100644 --- a/osu.Game/Screens/Play/HUD/FaillingLayer.cs +++ b/osu.Game/Screens/Play/HUD/FailingLayer.cs @@ -10,9 +10,9 @@ using osu.Game.Graphics; namespace osu.Game.Screens.Play.HUD { /// - /// An overlay layer on top of the playfield which fades to red when the current player health falls a certain threshold defined by . + /// An overlay layer on top of the playfield which fades to red when the current player health falls below a certain threshold defined by . /// - public class FaillingLayer : HealthDisplay + public class FailingLayer : HealthDisplay { private const float max_alpha = 0.4f; @@ -21,9 +21,9 @@ namespace osu.Game.Screens.Play.HUD /// /// The threshold under which the current player life should be considered low and the layer should start fading in. /// - protected virtual double LowHealthThreshold => 0.20f; + protected double LowHealthThreshold { get; set; } = 0.20f; - public FaillingLayer() + public FailingLayer() { RelativeSizeAxes = Axes.Both; Child = box = new Box diff --git a/osu.Game/Screens/Play/HUD/HealthDisplay.cs b/osu.Game/Screens/Play/HUD/HealthDisplay.cs index 4094b3de69..4ea08626ad 100644 --- a/osu.Game/Screens/Play/HUD/HealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/HealthDisplay.cs @@ -24,7 +24,7 @@ namespace osu.Game.Screens.Play.HUD /// Bind the tracked fields of to this health display. /// /// - public virtual void BindHealthProcessor(HealthProcessor processor) + public void BindHealthProcessor(HealthProcessor processor) { Current.BindTo(processor.Health); } From a4171253a38f2d09eedea9f38eb7d3eca0afebff Mon Sep 17 00:00:00 2001 From: Lucas A Date: Wed, 18 Mar 2020 21:41:43 +0100 Subject: [PATCH 0035/1117] Make LowHealthThreshold a field. --- osu.Game/Screens/Play/HUD/FailingLayer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/FailingLayer.cs b/osu.Game/Screens/Play/HUD/FailingLayer.cs index 5f7dc77928..5f4037c14d 100644 --- a/osu.Game/Screens/Play/HUD/FailingLayer.cs +++ b/osu.Game/Screens/Play/HUD/FailingLayer.cs @@ -21,7 +21,7 @@ namespace osu.Game.Screens.Play.HUD /// /// The threshold under which the current player life should be considered low and the layer should start fading in. /// - protected double LowHealthThreshold { get; set; } = 0.20f; + public double LowHealthThreshold = 0.20f; public FailingLayer() { From 2f5dc93d6119428654b0fa40e4e5e9439a074d64 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Thu, 26 Mar 2020 00:19:54 +0200 Subject: [PATCH 0036/1117] Select recommended difficulty --- osu.Game/Screens/Select/BeatmapCarousel.cs | 10 +++++-- .../Select/Carousel/CarouselBeatmapSet.cs | 28 ++++++++++++++++++- .../Carousel/CarouselGroupEagerSelect.cs | 10 +++++-- 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index fa8974f55a..2c45b3642d 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -23,6 +23,8 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Cursor; using osu.Game.Input.Bindings; using osu.Game.Screens.Select.Carousel; +using osu.Game.Online.API; +using osu.Game.Users; namespace osu.Game.Screens.Select { @@ -31,6 +33,8 @@ namespace osu.Game.Screens.Select private const float bleed_top = FilterControl.HEIGHT; private const float bleed_bottom = Footer.HEIGHT; + private readonly Bindable localUser = new Bindable(); + /// /// Triggered when the loaded change and are completely loaded. /// @@ -140,7 +144,7 @@ namespace osu.Game.Screens.Select private BeatmapManager beatmaps { get; set; } [BackgroundDependencyLoader(permitNulls: true)] - private void load(OsuConfigManager config) + private void load(OsuConfigManager config, IAPIProvider api) { config.BindWith(OsuSetting.RandomSelectAlgorithm, RandomAlgorithm); config.BindWith(OsuSetting.SongSelectRightMouseScroll, RightClickScrollingEnabled); @@ -154,6 +158,8 @@ namespace osu.Game.Screens.Select beatmaps.BeatmapRestored += beatmapRestored; loadBeatmapSets(GetLoadableBeatmaps()); + + localUser.BindTo(api.LocalUser); } protected virtual IEnumerable GetLoadableBeatmaps() => beatmaps.GetAllUsableBeatmapSetsEnumerable(); @@ -588,7 +594,7 @@ namespace osu.Game.Screens.Select b.Metadata = beatmapSet.Metadata; } - var set = new CarouselBeatmapSet(beatmapSet); + var set = new CarouselBeatmapSet(beatmapSet, localUser); foreach (var c in set.Beatmaps) { diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs index 8e323c66e2..9f1c39c578 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs @@ -4,19 +4,23 @@ using System; using System.Collections.Generic; using System.Linq; +using osu.Framework.Bindables; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Game.Beatmaps; using osu.Game.Screens.Select.Filter; +using osu.Game.Users; namespace osu.Game.Screens.Select.Carousel { public class CarouselBeatmapSet : CarouselGroupEagerSelect { + private readonly Bindable localUser; + public IEnumerable Beatmaps => InternalChildren.OfType(); public BeatmapSetInfo BeatmapSet; - public CarouselBeatmapSet(BeatmapSetInfo beatmapSet) + public CarouselBeatmapSet(BeatmapSetInfo beatmapSet, Bindable localUser) { BeatmapSet = beatmapSet ?? throw new ArgumentNullException(nameof(beatmapSet)); @@ -24,10 +28,32 @@ namespace osu.Game.Screens.Select.Carousel .Where(b => !b.Hidden) .Select(b => new CarouselBeatmap(b)) .ForEach(AddChild); + + this.localUser = localUser; } protected override DrawableCarouselItem CreateDrawableRepresentation() => new DrawableCarouselBeatmapSet(this); + protected override CarouselItem GetNextToSelect() + { + if (LastSelected == null) + { + decimal? pp = localUser.Value?.Statistics?.PP ?? 60; // TODO: This needs to get ruleset specific statistics + + var recommendedDifficulty = Math.Pow((double)pp, 0.4) * 0.195; + return Children.OfType() + .Where(b => !b.Filtered.Value) + .OrderBy(b => + { + var difference = b.Beatmap.StarDifficulty - recommendedDifficulty; + return difference >= 0 ? difference * 2 : difference * -1; // prefer easier over harder + }) + .FirstOrDefault(); + } + + return base.GetNextToSelect(); + } + public override int CompareTo(FilterCriteria criteria, CarouselItem other) { if (!(other is CarouselBeatmapSet otherSet)) diff --git a/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs b/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs index 6ce12f7b89..262bea9c71 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs @@ -90,11 +90,15 @@ namespace osu.Game.Screens.Select.Carousel PerformSelection(); } + protected virtual CarouselItem GetNextToSelect() + { + return Children.Skip(lastSelectedIndex).FirstOrDefault(i => !i.Filtered.Value) ?? + Children.Reverse().Skip(InternalChildren.Count - lastSelectedIndex).FirstOrDefault(i => !i.Filtered.Value); + } + protected virtual void PerformSelection() { - CarouselItem nextToSelect = - Children.Skip(lastSelectedIndex).FirstOrDefault(i => !i.Filtered.Value) ?? - Children.Reverse().Skip(InternalChildren.Count - lastSelectedIndex).FirstOrDefault(i => !i.Filtered.Value); + CarouselItem nextToSelect = GetNextToSelect(); if (nextToSelect != null) nextToSelect.State.Value = CarouselItemState.Selected; From e6b2e3b0ed1f4059a9fef74053b7ed5d6ec39d9d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 26 Mar 2020 05:18:12 +0300 Subject: [PATCH 0037/1117] Add osu!catch skin configurations --- .../Skinning/CatchSkinConfiguration.cs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 osu.Game.Rulesets.Catch/Skinning/CatchSkinConfiguration.cs diff --git a/osu.Game.Rulesets.Catch/Skinning/CatchSkinConfiguration.cs b/osu.Game.Rulesets.Catch/Skinning/CatchSkinConfiguration.cs new file mode 100644 index 0000000000..aea5beaa6b --- /dev/null +++ b/osu.Game.Rulesets.Catch/Skinning/CatchSkinConfiguration.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. + +namespace osu.Game.Rulesets.Catch.Skinning +{ + public enum CatchSkinConfiguration + { + /// + /// The colour to be used for the catcher while on hyper-dashing state. + /// + HyperDash, + + /// + /// The colour to be used for hyper-dash fruits. + /// + HyperDashFruit, + + /// + /// The colour to be used for the "exploding" catcher sprite on beginning of hyper-dashing. + /// + HyperDashAfterImage, + } +} From aa162b1033caff83366debb191f58366561b6555 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 26 Mar 2020 05:30:59 +0300 Subject: [PATCH 0038/1117] Setup hyper-dash colouring test scene --- .../TestSceneHyperDashColouring.cs | 112 ++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs new file mode 100644 index 0000000000..2041e365ea --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.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.Linq; +using NUnit.Framework; +using osu.Framework.Audio.Sample; +using osu.Framework.Bindables; +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.Testing; +using osu.Game.Audio; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.Objects.Drawables; +using osu.Game.Rulesets.Catch.Skinning; +using osu.Game.Rulesets.Catch.UI; +using osu.Game.Skinning; +using osu.Game.Tests.Visual; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Catch.Tests +{ + public class TestSceneHyperDashColouring : OsuTestScene + { + private Drawable setupSkinHierarchy(Func getChild, bool customHyperDashCatcherColour = false, bool customHyperDashFruitColour = false, bool customHyperDashAfterColour = false) + { + var testSkinProvider = new SkinProvidingContainer(new TestLegacySkin(customHyperDashCatcherColour, customHyperDashFruitColour, customHyperDashAfterColour)); + + var legacySkinTransformer = new SkinProvidingContainer(new CatchLegacySkinTransformer(testSkinProvider)); + + return testSkinProvider + .WithChild(legacySkinTransformer + .WithChild(getChild.Invoke())); + } + + private bool checkFruitHyperDashColour(DrawableFruit fruit, Color4 expectedColour, bool isLegacyFruit) => + isLegacyFruit + ? fruit.ChildrenOfType().First().Drawable.ChildrenOfType().Any(c => c.Colour == expectedColour) + : fruit.ChildrenOfType().First().Drawable.ChildrenOfType().Single(c => c.BorderColour == expectedColour).Any(d => d.Colour == expectedColour); + + private class TestLegacySkin : ISkin + { + public static Color4 CustomHyperDashColour { get; } = Color4.Goldenrod; + public static Color4 CustomHyperDashFruitColour { get; } = Color4.Cyan; + public static Color4 CustomHyperDashAfterColour { get; } = Color4.Lime; + + private readonly bool customHyperDashCatcherColour; + private readonly bool customHyperDashFruitColour; + private readonly bool customHyperDashAfterColour; + + public TestLegacySkin(bool customHyperDashCatcherColour = false, bool customHyperDashFruitColour = false, bool customHyperDashAfterColour = false) + { + this.customHyperDashCatcherColour = customHyperDashCatcherColour; + this.customHyperDashFruitColour = customHyperDashFruitColour; + this.customHyperDashAfterColour = customHyperDashAfterColour; + } + + public Drawable GetDrawableComponent(ISkinComponent component) => null; + + public Texture GetTexture(string componentName) + { + if (componentName == "fruit-pear") + { + // convince CatchLegacySkinTransformer to use the LegacyFruitPiece for pear fruit. + var texture = new Texture(Texture.WhitePixel.TextureGL) + { + Width = 1, + Height = 1, + ScaleAdjust = 1 / 96f + }; + return texture; + } + + return null; + } + + public SampleChannel GetSample(ISampleInfo sampleInfo) => null; + + public IBindable GetConfig(TLookup lookup) + { + switch (lookup) + { + case CatchSkinConfiguration config when config == CatchSkinConfiguration.HyperDash: + if (customHyperDashCatcherColour) + return SkinUtils.As(new Bindable(CustomHyperDashColour)); + + return null; + + case CatchSkinConfiguration config when config == CatchSkinConfiguration.HyperDashFruit: + if (customHyperDashFruitColour) + return SkinUtils.As(new Bindable(CustomHyperDashFruitColour)); + + return null; + + case CatchSkinConfiguration config when config == CatchSkinConfiguration.HyperDashAfterImage: + if (customHyperDashAfterColour) + return SkinUtils.As(new Bindable(CustomHyperDashAfterColour)); + + return null; + } + + return null; + } + } + } +} From 0a368f13d99421d17c34fa48a75db4001115c95a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 26 Mar 2020 05:37:26 +0300 Subject: [PATCH 0039/1117] Add default hyper-dash colour constant on Catcher --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index e361b29a9d..f53e14a8c7 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -21,6 +21,8 @@ namespace osu.Game.Rulesets.Catch.UI { public class Catcher : Container, IKeyBindingHandler { + public static Color4 DefaultHyperDashColour { get; } = Color4.Red; + /// /// Whether we are hyper-dashing or not. /// From 6f2cc5471adabc4392fcf1f63a5de32266016c10 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 26 Mar 2020 05:38:41 +0300 Subject: [PATCH 0040/1117] Add support for custom hyper-dash fruit colouring --- .../Objects/Drawables/FruitPiece.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/FruitPiece.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/FruitPiece.cs index 5797588ded..c8f7c4912e 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/FruitPiece.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/FruitPiece.cs @@ -7,7 +7,10 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Game.Rulesets.Catch.Skinning; +using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Skinning; using osuTK.Graphics; namespace osu.Game.Rulesets.Catch.Objects.Drawables @@ -31,7 +34,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables } [BackgroundDependencyLoader] - private void load(DrawableHitObject drawableObject) + private void load(DrawableHitObject drawableObject, ISkinSource skin) { DrawableCatchHitObject drawableCatchObject = (DrawableCatchHitObject)drawableObject; hitObject = drawableCatchObject.HitObject; @@ -60,6 +63,11 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables }, }); + var hyperDashColour = + skin.GetConfig(CatchSkinConfiguration.HyperDashFruit)?.Value ?? + skin.GetConfig(CatchSkinConfiguration.HyperDash)?.Value ?? + Catcher.DefaultHyperDashColour; + if (hitObject.HyperDash) { AddInternal(new Circle @@ -67,7 +75,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables RelativeSizeAxes = Axes.Both, Anchor = Anchor.Centre, Origin = Anchor.Centre, - BorderColour = Color4.Red, + BorderColour = hyperDashColour, BorderThickness = 12f * RADIUS_ADJUST, Children = new Drawable[] { @@ -77,7 +85,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables Alpha = 0.3f, Blending = BlendingParameters.Additive, RelativeSizeAxes = Axes.Both, - Colour = Color4.Red, + Colour = hyperDashColour, } } }); From d995f3e1cc7eff1604d4fa06ed4f85de7152f020 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 26 Mar 2020 05:39:32 +0300 Subject: [PATCH 0041/1117] Add support for custom hyper-dash legacy fruit colouring --- osu.Game.Rulesets.Catch/Skinning/LegacyFruitPiece.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Skinning/LegacyFruitPiece.cs b/osu.Game.Rulesets.Catch/Skinning/LegacyFruitPiece.cs index 25ee0811d0..99ecf12fd3 100644 --- a/osu.Game.Rulesets.Catch/Skinning/LegacyFruitPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/LegacyFruitPiece.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Game.Rulesets.Catch.Objects.Drawables; +using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Skinning; using osuTK; @@ -53,10 +54,15 @@ namespace osu.Game.Rulesets.Catch.Skinning if (drawableCatchObject.HitObject.HyperDash) { + var hyperDashColour = + skin.GetConfig(CatchSkinConfiguration.HyperDashFruit)?.Value ?? + skin.GetConfig(CatchSkinConfiguration.HyperDash)?.Value ?? + Catcher.DefaultHyperDashColour; + var hyperDash = new Sprite { Texture = skin.GetTexture(lookupName), - Colour = Color4.Red, + Colour = hyperDashColour, Anchor = Anchor.Centre, Origin = Anchor.Centre, Blending = BlendingParameters.Additive, From 29274b004cfa1141d3a4c85ec97e8960ccdeca48 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 26 Mar 2020 05:40:38 +0300 Subject: [PATCH 0042/1117] Add hyper-dash fruit colouring test cases --- .../TestSceneHyperDashColouring.cs | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs index 2041e365ea..7fab961aa7 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs @@ -28,6 +28,77 @@ namespace osu.Game.Rulesets.Catch.Tests { public class TestSceneHyperDashColouring : OsuTestScene { + [TestCase(false)] + [TestCase(true)] + public void TestHyperDashFruitColour(bool legacyFruit) + { + DrawableFruit drawableFruit = null; + + AddStep("setup fruit", () => + { + var fruit = new Fruit { IndexInBeatmap = legacyFruit ? 0 : 1, HyperDashTarget = new Banana() }; + fruit.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); + + Child = setupSkinHierarchy(() => + drawableFruit = new DrawableFruit(fruit) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(4f), + }, false, false); + }); + + AddAssert("fruit colour default-hyperdash", () => checkFruitHyperDashColour(drawableFruit, Catcher.DefaultHyperDashColour, legacyFruit)); + } + + [TestCase(false, true)] + [TestCase(false, false)] + [TestCase(true, true)] + [TestCase(true, false)] + public void TestCustomHyperDashFruitColour(bool legacyFruit, bool customCatcherHyperDashColour) + { + DrawableFruit drawableFruit = null; + + AddStep("setup fruit", () => + { + var fruit = new Fruit { IndexInBeatmap = legacyFruit ? 0 : 1, HyperDashTarget = new Banana() }; + fruit.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); + + Child = setupSkinHierarchy(() => + drawableFruit = new DrawableFruit(fruit) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(4f), + }, customCatcherHyperDashColour, true); + }); + + AddAssert("fruit colour custom-hyperdash", () => checkFruitHyperDashColour(drawableFruit, TestLegacySkin.CustomHyperDashFruitColour, legacyFruit)); + } + + [TestCase(false)] + [TestCase(true)] + public void TestCustomHyperDashFruitColourFallback(bool legacyFruit) + { + DrawableFruit drawableFruit = null; + + AddStep("setup fruit", () => + { + var fruit = new Fruit { IndexInBeatmap = legacyFruit ? 0 : 1, HyperDashTarget = new Banana() }; + fruit.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); + + Child = setupSkinHierarchy(() => + drawableFruit = new DrawableFruit(fruit) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(4f), + }, true, false); + }); + + AddAssert("fruit colour catcher-custom-hyperdash", () => checkFruitHyperDashColour(drawableFruit, TestLegacySkin.CustomHyperDashColour, legacyFruit)); + } + private Drawable setupSkinHierarchy(Func getChild, bool customHyperDashCatcherColour = false, bool customHyperDashFruitColour = false, bool customHyperDashAfterColour = false) { var testSkinProvider = new SkinProvidingContainer(new TestLegacySkin(customHyperDashCatcherColour, customHyperDashFruitColour, customHyperDashAfterColour)); From e3a7c8a124b46fc6979c2cbc96565560a91228cb Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 26 Mar 2020 09:11:31 +0300 Subject: [PATCH 0043/1117] Make catcher trails colouring per container --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 73 +++++++++++++++++++++------ 1 file changed, 57 insertions(+), 16 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index f53e14a8c7..68280ab111 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -35,7 +35,30 @@ namespace osu.Game.Rulesets.Catch.UI public Container ExplodingFruitTarget; - public Container AdditiveTarget; + private Container additiveTarget; + private Container dashTrails; + private Container hyperDashTrails; + private Container endGlowSprites; + + public Container AdditiveTarget + { + get => additiveTarget; + set + { + if (additiveTarget == value) + return; + + additiveTarget?.RemoveRange(new[] { dashTrails, hyperDashTrails, endGlowSprites }); + + additiveTarget = value; + additiveTarget?.AddRange(new[] + { + dashTrails ??= new Container { RelativeSizeAxes = Axes.Both, Colour = Color4.White }, + hyperDashTrails ??= new Container { RelativeSizeAxes = Axes.Both, Colour = hyperDashColour }, + endGlowSprites ??= new Container { RelativeSizeAxes = Axes.Both, Colour = hyperDashEndGlowColour }, + }); + } + } public CatcherAnimationState CurrentState { get; private set; } @@ -65,7 +88,7 @@ namespace osu.Game.Rulesets.Catch.UI get => trail; set { - if (value == trail || AdditiveTarget == null) return; + if (value == trail || additiveTarget == null) return; trail = value; @@ -82,6 +105,9 @@ namespace osu.Game.Rulesets.Catch.UI private CatcherSprite currentCatcher; + private Color4 hyperDashColour = DefaultHyperDashColour; + private Color4 hyperDashEndGlowColour = DefaultHyperDashColour; + private int currentDirection; private bool dashing; @@ -213,8 +239,6 @@ namespace osu.Game.Rulesets.Catch.UI /// When this catcher crosses this position, this catcher ends hyper-dashing. public void SetHyperDashState(double modifier = 1, float targetPosition = -1) { - const float hyper_dash_transition_length = 180; - var wasHyperDashing = HyperDashing; if (modifier <= 1 || X == targetPosition) @@ -223,11 +247,7 @@ namespace osu.Game.Rulesets.Catch.UI hyperDashDirection = 0; if (wasHyperDashing) - { - this.FadeColour(Color4.White, hyper_dash_transition_length, Easing.OutQuint); - this.FadeTo(1, hyper_dash_transition_length, Easing.OutQuint); Trail &= Dashing; - } } else { @@ -237,18 +257,37 @@ namespace osu.Game.Rulesets.Catch.UI if (!wasHyperDashing) { - this.FadeColour(Color4.OrangeRed, hyper_dash_transition_length, Easing.OutQuint); - this.FadeTo(0.2f, hyper_dash_transition_length, Easing.OutQuint); Trail = true; - var hyperDashEndGlow = createAdditiveSprite(); - + var hyperDashEndGlow = createAdditiveSprite(endGlowSprites); hyperDashEndGlow.MoveToOffset(new Vector2(0, -10), 1200, Easing.In); hyperDashEndGlow.ScaleTo(hyperDashEndGlow.Scale * 0.95f).ScaleTo(hyperDashEndGlow.Scale * 1.2f, 1200, Easing.In); hyperDashEndGlow.FadeOut(1200); hyperDashEndGlow.Expire(true); } } + + updateCatcherColour(); + } + + private void updateCatcherColour() + { + const float hyper_dash_transition_length = 180; + + if (HyperDashing) + { + this.FadeColour(hyperDashColour == DefaultHyperDashColour ? Color4.OrangeRed : hyperDashColour, hyper_dash_transition_length, Easing.OutQuint); + this.FadeTo(0.2f, hyper_dash_transition_length, Easing.OutQuint); + } + else + { + this.FadeColour(Color4.White, hyper_dash_transition_length, Easing.OutQuint); + this.FadeTo(1f, hyper_dash_transition_length, Easing.OutQuint); + } + + // update hyper-dash colour of the hyper-dashing catcher sprites containers. + hyperDashTrails?.FadeColour(hyperDashColour, hyper_dash_transition_length, Easing.OutQuint); + endGlowSprites?.FadeColour(hyperDashEndGlowColour, hyper_dash_transition_length, Easing.OutQuint); } public bool OnPressed(CatchAction action) @@ -392,7 +431,7 @@ namespace osu.Game.Rulesets.Catch.UI return; } - var additive = createAdditiveSprite(); + var additive = createAdditiveSprite(HyperDashing ? hyperDashTrails : dashTrails); additive.FadeTo(0.4f).FadeOut(800, Easing.OutQuint); additive.Expire(true); @@ -409,21 +448,23 @@ namespace osu.Game.Rulesets.Catch.UI updateCatcher(); } - private CatcherTrailSprite createAdditiveSprite() + private CatcherTrailSprite createAdditiveSprite(Container target) { + if (target == null) + return null; + var tex = (currentCatcher.Drawable as TextureAnimation)?.CurrentFrame ?? ((Sprite)currentCatcher.Drawable).Texture; var sprite = new CatcherTrailSprite(tex) { Anchor = Anchor, Scale = Scale, - Colour = HyperDashing ? Color4.Red : Color4.White, Blending = BlendingParameters.Additive, RelativePositionAxes = RelativePositionAxes, Position = Position }; - AdditiveTarget?.Add(sprite); + target.Add(sprite); return sprite; } From 302fdd834a305697536ac4093b00f88d72751d80 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 26 Mar 2020 09:11:59 +0300 Subject: [PATCH 0044/1117] Add support for custom hyper-dash catcher colouring --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 68280ab111..b3742aa1ad 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -13,13 +13,15 @@ using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects.Drawables; +using osu.Game.Rulesets.Catch.Skinning; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Skinning; using osuTK; using osuTK.Graphics; namespace osu.Game.Rulesets.Catch.UI { - public class Catcher : Container, IKeyBindingHandler + public class Catcher : SkinReloadableDrawable, IKeyBindingHandler { public static Color4 DefaultHyperDashColour { get; } = Color4.Red; @@ -133,7 +135,7 @@ namespace osu.Game.Rulesets.Catch.UI [BackgroundDependencyLoader] private void load() { - Children = new Drawable[] + InternalChildren = new Drawable[] { caughtFruit = new Container { @@ -184,7 +186,7 @@ namespace osu.Game.Rulesets.Catch.UI caughtFruit.Add(fruit); - Add(new HitExplosion(fruit) + AddInternal(new HitExplosion(fruit) { X = fruit.X, Scale = new Vector2(fruit.HitObject.Scale) @@ -378,6 +380,15 @@ namespace osu.Game.Rulesets.Catch.UI }); } + protected override void SkinChanged(ISkinSource skin, bool allowFallback) + { + base.SkinChanged(skin, allowFallback); + + hyperDashColour = skin.GetConfig(CatchSkinConfiguration.HyperDash)?.Value ?? DefaultHyperDashColour; + hyperDashEndGlowColour = skin.GetConfig(CatchSkinConfiguration.HyperDashAfterImage)?.Value ?? hyperDashColour; + updateCatcherColour(); + } + protected override void Update() { base.Update(); From fecafc2e48b9e45fa5a70c630832b55e7db6f396 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 26 Mar 2020 09:14:44 +0300 Subject: [PATCH 0045/1117] Fix additive target accidentally clears all of the added containers It sets the AdditiveTarget on the object initializer but then the catcher is set to Child which wipes up all of the existing children (containers added by Catcher through AdditiveTarget setter) --- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index e0d9ff759d..37501736ff 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -33,10 +33,10 @@ namespace osu.Game.Rulesets.Catch.UI { RelativeSizeAxes = Axes.X; Height = CATCHER_SIZE; - Child = MovableCatcher = new Catcher(difficulty) - { - AdditiveTarget = this, - }; + Child = MovableCatcher = new Catcher(difficulty); + + // this property adds containers to 'this' so it must not be set in the object initializer. + MovableCatcher.AdditiveTarget = this; } public static float GetCatcherSize(BeatmapDifficulty difficulty) From 77b3011394ffdc1afb2517943a025bce713177c2 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 26 Mar 2020 09:19:00 +0300 Subject: [PATCH 0046/1117] Add hyper-dash catcher & trails colouring test cases --- .../TestSceneHyperDashColouring.cs | 134 ++++++++++++++++++ 1 file changed, 134 insertions(+) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs index 7fab961aa7..6bad45f7ba 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs @@ -28,6 +28,140 @@ namespace osu.Game.Rulesets.Catch.Tests { public class TestSceneHyperDashColouring : OsuTestScene { + [Test] + public void TestHyperDashCatcherColour() + { + CatcherArea catcherArea = null; + + AddStep("setup catcher", () => + { + Child = setupSkinHierarchy(() => + catcherArea = new CatcherArea + { + RelativePositionAxes = Axes.None, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(4f), + }, false); + }); + + AddStep("set hyper-dash", () => + { + catcherArea.MovableCatcher.SetHyperDashState(2); + catcherArea.MovableCatcher.FinishTransforms(); + }); + + AddAssert("catcher colour default-hyperdash", () => catcherArea.MovableCatcher.Colour == Color4.OrangeRed); + AddAssert("catcher trails colour default-hyperdash", () => catcherArea.OfType>().Any(c => c.Colour == Catcher.DefaultHyperDashColour)); + + AddStep("clear hyper-dash", () => + { + catcherArea.MovableCatcher.SetHyperDashState(1); + catcherArea.MovableCatcher.FinishTransforms(); + }); + + AddAssert("catcher colour white", () => catcherArea.MovableCatcher.Colour == Color4.White); + } + + [Test] + public void TestCustomHyperDashCatcherColour() + { + CatcherArea catcherArea = null; + + AddStep("setup catcher", () => + { + Child = setupSkinHierarchy(() => + catcherArea = new CatcherArea + { + RelativePositionAxes = Axes.None, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(4f), + }, true); + }); + + AddStep("set hyper-dash", () => + { + catcherArea.MovableCatcher.SetHyperDashState(2); + catcherArea.MovableCatcher.FinishTransforms(); + }); + + AddAssert("catcher colour custom-hyperdash", () => catcherArea.MovableCatcher.Colour == TestLegacySkin.CustomHyperDashColour); + AddAssert("catcher trails colour custom-hyperdash", () => catcherArea.OfType>().Any(c => c.Colour == TestLegacySkin.CustomHyperDashColour)); + + AddStep("clear hyper-dash", () => + { + catcherArea.MovableCatcher.SetHyperDashState(1); + catcherArea.MovableCatcher.FinishTransforms(); + }); + + AddAssert("catcher colour white", () => catcherArea.MovableCatcher.Colour == Color4.White); + } + + [Test] + public void TestHyperDashCatcherEndGlowColour() + { + CatcherArea catcherArea = null; + + AddStep("setup catcher", () => + { + Child = setupSkinHierarchy(() => + catcherArea = new CatcherArea + { + RelativePositionAxes = Axes.None, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(4f), + }, false, false, false); + }); + + AddStep("set hyper-dash", () => catcherArea.MovableCatcher.SetHyperDashState(2)); + AddAssert("end-glow sprite colour default-hyperdash", () => catcherArea.OfType>().Any(c => c.Colour == Catcher.DefaultHyperDashColour)); + } + + [TestCase(true)] + [TestCase(false)] + public void TestCustomHyperDashCatcherEndGlowColour(bool customHyperDashCatcherColour) + { + CatcherArea catcherArea = null; + + AddStep("setup catcher", () => + { + Child = setupSkinHierarchy(() => + catcherArea = new CatcherArea + { + RelativePositionAxes = Axes.None, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(4f), + }, customHyperDashCatcherColour, false, true); + }); + + AddStep("set hyper-dash", () => catcherArea.MovableCatcher.SetHyperDashState(2)); + AddAssert("end-glow sprite colour custom-hyperdash", () => catcherArea.OfType>().Any(c => c.Colour == TestLegacySkin.CustomHyperDashAfterColour)); + } + + [Test] + public void TestCustomHyperDashCatcherEndGlowColourFallback() + { + CatcherArea catcherArea = null; + + AddStep("setup catcher", () => + { + Child = setupSkinHierarchy(() => + catcherArea = new CatcherArea + { + RelativePositionAxes = Axes.None, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(4f), + }, true, false, false); + }); + + AddStep("set hyper-dash", () => catcherArea.MovableCatcher.SetHyperDashState(2)); + AddAssert("end-glow sprite colour catcher-custom-hyperdash", () => catcherArea.OfType>().Any(c => c.Colour == TestLegacySkin.CustomHyperDashColour)); + } + [TestCase(false)] [TestCase(true)] public void TestHyperDashFruitColour(bool legacyFruit) From c1ac57e70fc05e11e6d085f2829eef31d524328e Mon Sep 17 00:00:00 2001 From: Lucas A Date: Thu, 26 Mar 2020 12:14:44 +0100 Subject: [PATCH 0047/1117] Add back visual tests and add easing to alpha fade. --- .../Visual/Gameplay/TestSceneFailingLayer.cs | 31 +++++++++++++++++++ osu.Game/Screens/Play/HUD/FailingLayer.cs | 7 ++++- 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs new file mode 100644 index 0000000000..3016890ade --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.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.Screens.Play.HUD; + +namespace osu.Game.Tests.Visual.Gameplay +{ + public class TestSceneFailingLayer : OsuTestScene + { + private readonly FailingLayer layer; + + public TestSceneFailingLayer() + { + Child = layer = new FailingLayer(); + } + + [Test] + public void TestLayerFading() + { + AddSliderStep("current health", 0.0, 1.0, 1.0, val => + { + layer.Current.Value = val; + }); + + AddStep("set health to 0.10", () => layer.Current.Value = 0.10); + AddWaitStep("wait for fade to finish", 5); + AddStep("set health to 1", () => layer.Current.Value = 1f); + } + } +} diff --git a/osu.Game/Screens/Play/HUD/FailingLayer.cs b/osu.Game/Screens/Play/HUD/FailingLayer.cs index 5f4037c14d..97d2458674 100644 --- a/osu.Game/Screens/Play/HUD/FailingLayer.cs +++ b/osu.Game/Screens/Play/HUD/FailingLayer.cs @@ -5,6 +5,7 @@ using System; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; +using osu.Framework.Utils; using osu.Game.Graphics; namespace osu.Game.Screens.Play.HUD @@ -16,6 +17,8 @@ namespace osu.Game.Screens.Play.HUD { private const float max_alpha = 0.4f; + private const int fade_time = 400; + private readonly Box box; /// @@ -41,7 +44,9 @@ namespace osu.Game.Screens.Play.HUD protected override void Update() { - box.Alpha = (float)Math.Clamp(max_alpha * (1 - Current.Value / LowHealthThreshold), 0, max_alpha); + box.Alpha = (float)Interpolation.ValueAt(Math.Clamp(Clock.ElapsedFrameTime, 0, fade_time), box.Alpha, + Math.Clamp(max_alpha * (1 - Current.Value / LowHealthThreshold), 0, max_alpha), 0, fade_time, Easing.Out); + base.Update(); } } From ee112c6f507e295a414721e4049f679583b9ab24 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Thu, 26 Mar 2020 18:42:08 +0200 Subject: [PATCH 0048/1117] Move and change logic --- osu.Game/Screens/Select/BeatmapCarousel.cs | 33 ++++++++++++++++--- .../Select/Carousel/CarouselBeatmapSet.cs | 12 +++---- 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 2c45b3642d..65472f8a0e 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -24,7 +24,8 @@ using osu.Game.Graphics.Cursor; using osu.Game.Input.Bindings; using osu.Game.Screens.Select.Carousel; using osu.Game.Online.API; -using osu.Game.Users; +using osu.Game.Rulesets; +using osu.Game.Online.API.Requests; namespace osu.Game.Screens.Select { @@ -33,7 +34,7 @@ namespace osu.Game.Screens.Select private const float bleed_top = FilterControl.HEIGHT; private const float bleed_bottom = Footer.HEIGHT; - private readonly Bindable localUser = new Bindable(); + private readonly Bindable recommendedStarDifficulty = new Bindable(); /// /// Triggered when the loaded change and are completely loaded. @@ -143,8 +144,11 @@ namespace osu.Game.Screens.Select [Resolved] private BeatmapManager beatmaps { get; set; } + [Resolved] + private IAPIProvider api { get; set; } + [BackgroundDependencyLoader(permitNulls: true)] - private void load(OsuConfigManager config, IAPIProvider api) + private void load(OsuConfigManager config, Bindable decoupledRuleset) { config.BindWith(OsuSetting.RandomSelectAlgorithm, RandomAlgorithm); config.BindWith(OsuSetting.SongSelectRightMouseScroll, RightClickScrollingEnabled); @@ -159,7 +163,26 @@ namespace osu.Game.Screens.Select loadBeatmapSets(GetLoadableBeatmaps()); - localUser.BindTo(api.LocalUser); + decoupledRuleset.BindValueChanged(UpdateRecommendedStarDifficulty, true); + } + + protected void UpdateRecommendedStarDifficulty(ValueChangedEvent ruleset) + { + if (api.LocalUser.Value is GuestUser) + { + recommendedStarDifficulty.Value = 0; + return; + } + + var req = new GetUserRequest(api.LocalUser.Value.Id, ruleset.NewValue); + + req.Success += result => + { + // algorithm taken from https://github.com/ppy/osu-web/blob/e6e2825516449e3d0f3f5e1852c6bdd3428c3437/app/Models/User.php#L1505 + recommendedStarDifficulty.Value = Math.Pow((double)(result.Statistics.PP ?? 0), 0.4) * 0.195; + }; + + api.PerformAsync(req); } protected virtual IEnumerable GetLoadableBeatmaps() => beatmaps.GetAllUsableBeatmapSetsEnumerable(); @@ -594,7 +617,7 @@ namespace osu.Game.Screens.Select b.Metadata = beatmapSet.Metadata; } - var set = new CarouselBeatmapSet(beatmapSet, localUser); + var set = new CarouselBeatmapSet(beatmapSet, recommendedStarDifficulty); foreach (var c in set.Beatmaps) { diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs index 9f1c39c578..064840d99a 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs @@ -8,19 +8,18 @@ using osu.Framework.Bindables; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Game.Beatmaps; using osu.Game.Screens.Select.Filter; -using osu.Game.Users; namespace osu.Game.Screens.Select.Carousel { public class CarouselBeatmapSet : CarouselGroupEagerSelect { - private readonly Bindable localUser; + private readonly Bindable recommendedStarDifficulty = new Bindable(); public IEnumerable Beatmaps => InternalChildren.OfType(); public BeatmapSetInfo BeatmapSet; - public CarouselBeatmapSet(BeatmapSetInfo beatmapSet, Bindable localUser) + public CarouselBeatmapSet(BeatmapSetInfo beatmapSet, Bindable recommendedStarDifficulty) { BeatmapSet = beatmapSet ?? throw new ArgumentNullException(nameof(beatmapSet)); @@ -29,7 +28,7 @@ namespace osu.Game.Screens.Select.Carousel .Select(b => new CarouselBeatmap(b)) .ForEach(AddChild); - this.localUser = localUser; + this.recommendedStarDifficulty.BindTo(recommendedStarDifficulty); } protected override DrawableCarouselItem CreateDrawableRepresentation() => new DrawableCarouselBeatmapSet(this); @@ -38,14 +37,11 @@ namespace osu.Game.Screens.Select.Carousel { if (LastSelected == null) { - decimal? pp = localUser.Value?.Statistics?.PP ?? 60; // TODO: This needs to get ruleset specific statistics - - var recommendedDifficulty = Math.Pow((double)pp, 0.4) * 0.195; return Children.OfType() .Where(b => !b.Filtered.Value) .OrderBy(b => { - var difference = b.Beatmap.StarDifficulty - recommendedDifficulty; + var difference = b.Beatmap.StarDifficulty - recommendedStarDifficulty.Value; return difference >= 0 ? difference * 2 : difference * -1; // prefer easier over harder }) .FirstOrDefault(); From bbbaaae3ee8bbf6d48498deef378ca1974b2ff17 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Thu, 26 Mar 2020 19:18:16 +0200 Subject: [PATCH 0049/1117] Write tests --- .../SongSelect/TestSceneBeatmapCarousel.cs | 31 +++++++++++++++++++ osu.Game/Screens/Select/BeatmapCarousel.cs | 8 ++--- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs index 0cc37bbd57..b9b52a28cb 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Text; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -579,6 +580,34 @@ namespace osu.Game.Tests.Visual.SongSelect checkVisibleItemCount(true, 15); } + [Test] + public void TestSelectRecommendedDifficulty() + { + void setRecommendedAndExpect(double recommended, int expectedSet, int expectedDiff) + { + AddStep($"Recommend SR {recommended}", () => carousel.RecommendedStarDifficulty.Value = recommended); + advanceSelection(direction: 1, diff: false); + waitForSelection(expectedSet, expectedDiff); + } + + createCarousel(); + AddStep("Add beatmaps", () => + { + for (int i = 1; i <= 7; i++) + { + var set = createTestBeatmapSet(i); + carousel.UpdateBeatmapSet(set); + } + }); + waitForSelection(1, 1); + setRecommendedAndExpect(1, 2, 1); + setRecommendedAndExpect(3.9, 3, 1); + setRecommendedAndExpect(4.1, 4, 2); + setRecommendedAndExpect(5.6, 5, 2); + setRecommendedAndExpect(5.7, 6, 3); + setRecommendedAndExpect(10, 7, 3); + } + private void loadBeatmaps(List beatmapSets = null, Func initialCriteria = null) { createCarousel(); @@ -781,6 +810,8 @@ namespace osu.Game.Tests.Visual.SongSelect { public new List Items => base.Items; + public new Bindable RecommendedStarDifficulty => base.RecommendedStarDifficulty; + public bool PendingFilterTask => PendingFilter != null; protected override IEnumerable GetLoadableBeatmaps() => Enumerable.Empty(); diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 65472f8a0e..9aa4938886 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -34,7 +34,7 @@ namespace osu.Game.Screens.Select private const float bleed_top = FilterControl.HEIGHT; private const float bleed_bottom = Footer.HEIGHT; - private readonly Bindable recommendedStarDifficulty = new Bindable(); + protected readonly Bindable RecommendedStarDifficulty = new Bindable(); /// /// Triggered when the loaded change and are completely loaded. @@ -170,7 +170,7 @@ namespace osu.Game.Screens.Select { if (api.LocalUser.Value is GuestUser) { - recommendedStarDifficulty.Value = 0; + RecommendedStarDifficulty.Value = 0; return; } @@ -179,7 +179,7 @@ namespace osu.Game.Screens.Select req.Success += result => { // algorithm taken from https://github.com/ppy/osu-web/blob/e6e2825516449e3d0f3f5e1852c6bdd3428c3437/app/Models/User.php#L1505 - recommendedStarDifficulty.Value = Math.Pow((double)(result.Statistics.PP ?? 0), 0.4) * 0.195; + RecommendedStarDifficulty.Value = Math.Pow((double)(result.Statistics.PP ?? 0), 0.4) * 0.195; }; api.PerformAsync(req); @@ -617,7 +617,7 @@ namespace osu.Game.Screens.Select b.Metadata = beatmapSet.Metadata; } - var set = new CarouselBeatmapSet(beatmapSet, recommendedStarDifficulty); + var set = new CarouselBeatmapSet(beatmapSet, RecommendedStarDifficulty); foreach (var c in set.Beatmaps) { From 7b24cc325f0ed61490766307ac0a681d5a9bb766 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 27 Mar 2020 20:57:57 +0300 Subject: [PATCH 0050/1117] Implement OverlayScrollContainer component --- .../TestSceneOverlayScrollContainer.cs | 90 ++++++++++ osu.Game/Overlays/OverlayScrollContainer.cs | 169 ++++++++++++++++++ 2 files changed, 259 insertions(+) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneOverlayScrollContainer.cs create mode 100644 osu.Game/Overlays/OverlayScrollContainer.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayScrollContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayScrollContainer.cs new file mode 100644 index 0000000000..1fc85c3c04 --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayScrollContainer.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. + +using osu.Framework.Graphics.Containers; +using osu.Game.Overlays; +using System; +using System.Collections.Generic; +using osu.Framework.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Graphics.Shapes; +using osuTK.Graphics; +using NUnit.Framework; +using osu.Framework.Utils; +using osuTK.Input; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public class TestSceneOverlayScrollContainer : OsuManualInputManagerTestScene + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(OverlayScrollContainer) + }; + + [Cached] + private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); + + private OverlayScrollContainer scroll; + + private int invocationCount; + + [SetUp] + public void SetUp() => Schedule(() => + { + Add(scroll = new OverlayScrollContainer + { + RelativeSizeAxes = Axes.Both, + Child = new Container + { + Height = 3000, + RelativeSizeAxes = Axes.X, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Gray + } + } + }); + + invocationCount = 0; + + scroll.Button.Action += () => invocationCount++; + }); + + [Test] + public void TestButtonVisibility() + { + AddAssert("button is hidden", () => scroll.Button.State.Value == Visibility.Hidden); + + AddStep("scroll to end", () => scroll.ScrollToEnd(false)); + AddAssert("button is visible", () => scroll.Button.State.Value == Visibility.Visible); + + AddStep("scroll to start", () => scroll.ScrollToStart(false)); + AddAssert("button is hidden", () => scroll.Button.State.Value == Visibility.Hidden); + } + + [Test] + public void TestButtonAction() + { + AddStep("scroll to end", () => scroll.ScrollToEnd(false)); + + AddStep("invoke action", () => scroll.Button.Action.Invoke()); + + AddUntilStep("scrolled back to start", () => Precision.AlmostEquals(scroll.Current, 0, 0.1f)); + } + + [Test] + public void TestMultipleClicks() + { + AddStep("scroll to end", () => scroll.ScrollToEnd(false)); + + AddAssert("invocation count is 0", () => invocationCount == 0); + + AddStep("hover button", () => InputManager.MoveMouseTo(scroll.Button)); + AddRepeatStep("click button", () => InputManager.Click(MouseButton.Left), 3); + + AddAssert("invocation count is 1", () => invocationCount == 1); + } + } +} diff --git a/osu.Game/Overlays/OverlayScrollContainer.cs b/osu.Game/Overlays/OverlayScrollContainer.cs new file mode 100644 index 0000000000..1a875ded95 --- /dev/null +++ b/osu.Game/Overlays/OverlayScrollContainer.cs @@ -0,0 +1,169 @@ +// 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.Extensions.Color4Extensions; +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.Events; +using osu.Game.Graphics.Containers; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Overlays +{ + /// + /// which provides . Mostly used in . + /// + public class OverlayScrollContainer : OsuScrollContainer + { + /// + /// Scroll position at which the will be shown. + /// + private const int button_scroll_position = 200; + + public ScrollToTopButton Button { get; } + + private float currentTarget; + + public OverlayScrollContainer() + { + AddInternal(Button = new ScrollToTopButton + { + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + Margin = new MarginPadding(20), + Action = () => + { + ScrollToStart(); + currentTarget = Target; + Button.State.Value = Visibility.Hidden; + } + }); + } + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + if (ScrollContent.DrawHeight + button_scroll_position < DrawHeight) + { + Button.State.Value = Visibility.Hidden; + return; + } + + if (Target == currentTarget) + return; + + currentTarget = Target; + Button.State.Value = Current > button_scroll_position ? Visibility.Visible : Visibility.Hidden; + } + + public class ScrollToTopButton : VisibilityContainer + { + private const int fade_duration = 500; + + public Action Action + { + get => button.Action; + set => button.Action = value; + } + + public override bool PropagatePositionalInputSubTree => true; + + protected override bool StartHidden => true; + + private readonly Button button; + + public ScrollToTopButton() + { + Size = new Vector2(50); + Child = button = new Button(); + } + + protected override bool OnMouseDown(MouseDownEvent e) => true; + + protected override void PopIn() => button.FadeIn(fade_duration, Easing.OutQuint); + + protected override void PopOut() => button.FadeOut(fade_duration, Easing.OutQuint); + + private class Button : OsuHoverContainer + { + public override bool PropagatePositionalInputSubTree => Alpha == 1; + + protected override IEnumerable EffectTargets => new[] { background }; + + private Color4 flashColour; + + private readonly Container content; + private readonly Box background; + + public Button() + { + RelativeSizeAxes = Axes.Both; + Add(content = new CircularContainer + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Masking = true, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Offset = new Vector2(0f, 1f), + Radius = 3f, + Colour = Color4.Black.Opacity(0.25f), + }, + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both + }, + new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(15), + Icon = FontAwesome.Solid.ChevronUp + } + } + }); + + TooltipText = "Scroll to top"; + } + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + IdleColour = colourProvider.Background6; + HoverColour = colourProvider.Background5; + flashColour = colourProvider.Light1; + } + + protected override bool OnClick(ClickEvent e) + { + background.FlashColour(flashColour, 800, Easing.OutQuint); + return base.OnClick(e); + } + + protected override bool OnMouseDown(MouseDownEvent e) + { + content.ScaleTo(0.75f, 2000, Easing.OutQuint); + return true; + } + + protected override void OnMouseUp(MouseUpEvent e) + { + content.ScaleTo(1, 1000, Easing.OutElastic); + base.OnMouseUp(e); + } + } + } + } +} From 45eb03bfe2adc868729915defc057b09e5fb7f90 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 28 Mar 2020 07:43:47 +0300 Subject: [PATCH 0051/1117] Apply review suggestions --- .../TestSceneHyperDashColouring.cs | 32 +++++++------------ 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs index 7fab961aa7..9ab8cf9113 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs @@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Catch.Tests }, false, false); }); - AddAssert("fruit colour default-hyperdash", () => checkFruitHyperDashColour(drawableFruit, Catcher.DefaultHyperDashColour, legacyFruit)); + AddAssert("default colour", () => checkFruitHyperDashColour(drawableFruit, Catcher.DefaultHyperDashColour, legacyFruit)); } [TestCase(false, true)] @@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Catch.Tests }, customCatcherHyperDashColour, true); }); - AddAssert("fruit colour custom-hyperdash", () => checkFruitHyperDashColour(drawableFruit, TestLegacySkin.CustomHyperDashFruitColour, legacyFruit)); + AddAssert("custom colour", () => checkFruitHyperDashColour(drawableFruit, TestLegacySkin.CustomHyperDashFruitColour, legacyFruit)); } [TestCase(false)] @@ -96,7 +96,7 @@ namespace osu.Game.Rulesets.Catch.Tests }, true, false); }); - AddAssert("fruit colour catcher-custom-hyperdash", () => checkFruitHyperDashColour(drawableFruit, TestLegacySkin.CustomHyperDashColour, legacyFruit)); + AddAssert("catcher custom colour", () => checkFruitHyperDashColour(drawableFruit, TestLegacySkin.CustomHyperDashColour, legacyFruit)); } private Drawable setupSkinHierarchy(Func getChild, bool customHyperDashCatcherColour = false, bool customHyperDashFruitColour = false, bool customHyperDashAfterColour = false) @@ -139,13 +139,12 @@ namespace osu.Game.Rulesets.Catch.Tests if (componentName == "fruit-pear") { // convince CatchLegacySkinTransformer to use the LegacyFruitPiece for pear fruit. - var texture = new Texture(Texture.WhitePixel.TextureGL) + return new Texture(Texture.WhitePixel.TextureGL) { Width = 1, Height = 1, ScaleAdjust = 1 / 96f }; - return texture; } return null; @@ -155,25 +154,16 @@ namespace osu.Game.Rulesets.Catch.Tests public IBindable GetConfig(TLookup lookup) { - switch (lookup) + if (lookup is CatchSkinConfiguration config) { - case CatchSkinConfiguration config when config == CatchSkinConfiguration.HyperDash: - if (customHyperDashCatcherColour) - return SkinUtils.As(new Bindable(CustomHyperDashColour)); + if (config == CatchSkinConfiguration.HyperDash && customHyperDashCatcherColour) + return SkinUtils.As(new Bindable(CustomHyperDashColour)); - return null; + if (config == CatchSkinConfiguration.HyperDashFruit && customHyperDashFruitColour) + return SkinUtils.As(new Bindable(CustomHyperDashFruitColour)); - case CatchSkinConfiguration config when config == CatchSkinConfiguration.HyperDashFruit: - if (customHyperDashFruitColour) - return SkinUtils.As(new Bindable(CustomHyperDashFruitColour)); - - return null; - - case CatchSkinConfiguration config when config == CatchSkinConfiguration.HyperDashAfterImage: - if (customHyperDashAfterColour) - return SkinUtils.As(new Bindable(CustomHyperDashAfterColour)); - - return null; + if (config == CatchSkinConfiguration.HyperDashAfterImage && customHyperDashAfterColour) + return SkinUtils.As(new Bindable(CustomHyperDashAfterColour)); } return null; From e51097da9e7ee6f6128fd5e9b19e23117a85904b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 30 Mar 2020 09:29:00 +0300 Subject: [PATCH 0052/1117] Add a legacy skin provider above the test skin --- .../TestSceneHyperDashColouring.cs | 122 +++++++++--------- 1 file changed, 63 insertions(+), 59 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs index 9ab8cf9113..fea2939eae 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs @@ -1,9 +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.Linq; using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -28,6 +28,9 @@ namespace osu.Game.Rulesets.Catch.Tests { public class TestSceneHyperDashColouring : OsuTestScene { + [Resolved] + private SkinManager skins { get; set; } + [TestCase(false)] [TestCase(true)] public void TestHyperDashFruitColour(bool legacyFruit) @@ -36,19 +39,21 @@ namespace osu.Game.Rulesets.Catch.Tests AddStep("setup fruit", () => { - var fruit = new Fruit { IndexInBeatmap = legacyFruit ? 0 : 1, HyperDashTarget = new Banana() }; + var fruit = new Fruit { HyperDashTarget = new Banana() }; fruit.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); - Child = setupSkinHierarchy(() => - drawableFruit = new DrawableFruit(fruit) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Scale = new Vector2(4f), - }, false, false); + Child = setupSkinHierarchy(drawableFruit = new DrawableFruit(fruit) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(4f), + }, false, false, false, legacyFruit); }); - AddAssert("default colour", () => checkFruitHyperDashColour(drawableFruit, Catcher.DefaultHyperDashColour, legacyFruit)); + AddAssert("default colour", () => + legacyFruit + ? checkLegacyFruitHyperDashColour(drawableFruit, Catcher.DefaultHyperDashColour) + : checkFruitHyperDashColour(drawableFruit, Catcher.DefaultHyperDashColour)); } [TestCase(false, true)] @@ -61,19 +66,21 @@ namespace osu.Game.Rulesets.Catch.Tests AddStep("setup fruit", () => { - var fruit = new Fruit { IndexInBeatmap = legacyFruit ? 0 : 1, HyperDashTarget = new Banana() }; + var fruit = new Fruit { HyperDashTarget = new Banana() }; fruit.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); - Child = setupSkinHierarchy(() => - drawableFruit = new DrawableFruit(fruit) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Scale = new Vector2(4f), - }, customCatcherHyperDashColour, true); + Child = setupSkinHierarchy(drawableFruit = new DrawableFruit(fruit) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(4f), + }, customCatcherHyperDashColour, false, true, legacyFruit); }); - AddAssert("custom colour", () => checkFruitHyperDashColour(drawableFruit, TestLegacySkin.CustomHyperDashFruitColour, legacyFruit)); + AddAssert("custom colour", () => + legacyFruit + ? checkLegacyFruitHyperDashColour(drawableFruit, TestSkin.CustomHyperDashFruitColour) + : checkFruitHyperDashColour(drawableFruit, TestSkin.CustomHyperDashFruitColour)); } [TestCase(false)] @@ -84,71 +91,68 @@ namespace osu.Game.Rulesets.Catch.Tests AddStep("setup fruit", () => { - var fruit = new Fruit { IndexInBeatmap = legacyFruit ? 0 : 1, HyperDashTarget = new Banana() }; + var fruit = new Fruit { HyperDashTarget = new Banana() }; fruit.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); - Child = setupSkinHierarchy(() => + Child = setupSkinHierarchy( drawableFruit = new DrawableFruit(fruit) { Anchor = Anchor.Centre, Origin = Anchor.Centre, Scale = new Vector2(4f), - }, true, false); + }, true, false, false, legacyFruit); }); - AddAssert("catcher custom colour", () => checkFruitHyperDashColour(drawableFruit, TestLegacySkin.CustomHyperDashColour, legacyFruit)); + AddAssert("catcher custom colour", () => + legacyFruit + ? checkLegacyFruitHyperDashColour(drawableFruit, TestSkin.CustomHyperDashColour) + : checkFruitHyperDashColour(drawableFruit, TestSkin.CustomHyperDashColour)); } - private Drawable setupSkinHierarchy(Func getChild, bool customHyperDashCatcherColour = false, bool customHyperDashFruitColour = false, bool customHyperDashAfterColour = false) + private Drawable setupSkinHierarchy(Drawable child, bool customCatcherColour = false, bool customAfterColour = false, bool customFruitColour = false, bool legacySkin = true) { - var testSkinProvider = new SkinProvidingContainer(new TestLegacySkin(customHyperDashCatcherColour, customHyperDashFruitColour, customHyperDashAfterColour)); + var testSkinProvider = new SkinProvidingContainer(new TestSkin(customCatcherColour, customAfterColour, customFruitColour)); - var legacySkinTransformer = new SkinProvidingContainer(new CatchLegacySkinTransformer(testSkinProvider)); + if (legacySkin) + { + var legacySkinProvider = new SkinProvidingContainer(skins.GetSkin(DefaultLegacySkin.Info)); + var legacySkinTransformer = new SkinProvidingContainer(new CatchLegacySkinTransformer(testSkinProvider)); - return testSkinProvider - .WithChild(legacySkinTransformer - .WithChild(getChild.Invoke())); + return legacySkinProvider + .WithChild(testSkinProvider + .WithChild(legacySkinTransformer + .WithChild(child))); + } + + return testSkinProvider.WithChild(child); } - private bool checkFruitHyperDashColour(DrawableFruit fruit, Color4 expectedColour, bool isLegacyFruit) => - isLegacyFruit - ? fruit.ChildrenOfType().First().Drawable.ChildrenOfType().Any(c => c.Colour == expectedColour) - : fruit.ChildrenOfType().First().Drawable.ChildrenOfType().Single(c => c.BorderColour == expectedColour).Any(d => d.Colour == expectedColour); + private bool checkFruitHyperDashColour(DrawableFruit fruit, Color4 expectedColour) => + fruit.ChildrenOfType().First().Drawable.ChildrenOfType().Single(c => c.BorderColour == expectedColour).Any(d => d.Colour == expectedColour); - private class TestLegacySkin : ISkin + private bool checkLegacyFruitHyperDashColour(DrawableFruit fruit, Color4 expectedColour) => + fruit.ChildrenOfType().First().Drawable.ChildrenOfType().Any(c => c.Colour == expectedColour); + + private class TestSkin : ISkin { public static Color4 CustomHyperDashColour { get; } = Color4.Goldenrod; public static Color4 CustomHyperDashFruitColour { get; } = Color4.Cyan; public static Color4 CustomHyperDashAfterColour { get; } = Color4.Lime; - private readonly bool customHyperDashCatcherColour; - private readonly bool customHyperDashFruitColour; - private readonly bool customHyperDashAfterColour; + private readonly bool customCatcherColour; + private readonly bool customAfterColour; + private readonly bool customFruitColour; - public TestLegacySkin(bool customHyperDashCatcherColour = false, bool customHyperDashFruitColour = false, bool customHyperDashAfterColour = false) + public TestSkin(bool customCatcherColour = false, bool customAfterColour = false, bool customFruitColour = false) { - this.customHyperDashCatcherColour = customHyperDashCatcherColour; - this.customHyperDashFruitColour = customHyperDashFruitColour; - this.customHyperDashAfterColour = customHyperDashAfterColour; + this.customCatcherColour = customCatcherColour; + this.customAfterColour = customAfterColour; + this.customFruitColour = customFruitColour; } public Drawable GetDrawableComponent(ISkinComponent component) => null; - public Texture GetTexture(string componentName) - { - if (componentName == "fruit-pear") - { - // convince CatchLegacySkinTransformer to use the LegacyFruitPiece for pear fruit. - return new Texture(Texture.WhitePixel.TextureGL) - { - Width = 1, - Height = 1, - ScaleAdjust = 1 / 96f - }; - } - - return null; - } + public Texture GetTexture(string componentName) => null; public SampleChannel GetSample(ISampleInfo sampleInfo) => null; @@ -156,13 +160,13 @@ namespace osu.Game.Rulesets.Catch.Tests { if (lookup is CatchSkinConfiguration config) { - if (config == CatchSkinConfiguration.HyperDash && customHyperDashCatcherColour) + if (config == CatchSkinConfiguration.HyperDash && customCatcherColour) return SkinUtils.As(new Bindable(CustomHyperDashColour)); - if (config == CatchSkinConfiguration.HyperDashFruit && customHyperDashFruitColour) + if (config == CatchSkinConfiguration.HyperDashFruit && customFruitColour) return SkinUtils.As(new Bindable(CustomHyperDashFruitColour)); - if (config == CatchSkinConfiguration.HyperDashAfterImage && customHyperDashAfterColour) + if (config == CatchSkinConfiguration.HyperDashAfterImage && customAfterColour) return SkinUtils.As(new Bindable(CustomHyperDashAfterColour)); } From 16a4525a9cdbe35aecc28579937a0606e919f5de Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 30 Mar 2020 09:33:47 +0300 Subject: [PATCH 0053/1117] CatchSkinConfiguration -> CatchSkinColour --- .../TestSceneHyperDashColouring.cs | 8 ++++---- osu.Game.Rulesets.Catch/Objects/Drawables/FruitPiece.cs | 4 ++-- .../{CatchSkinConfiguration.cs => CatchSkinColour.cs} | 2 +- osu.Game.Rulesets.Catch/Skinning/LegacyFruitPiece.cs | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) rename osu.Game.Rulesets.Catch/Skinning/{CatchSkinConfiguration.cs => CatchSkinColour.cs} (94%) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs index fea2939eae..ebc3d3bff1 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs @@ -158,15 +158,15 @@ namespace osu.Game.Rulesets.Catch.Tests public IBindable GetConfig(TLookup lookup) { - if (lookup is CatchSkinConfiguration config) + if (lookup is CatchSkinColour config) { - if (config == CatchSkinConfiguration.HyperDash && customCatcherColour) + if (config == CatchSkinColour.HyperDash && customCatcherColour) return SkinUtils.As(new Bindable(CustomHyperDashColour)); - if (config == CatchSkinConfiguration.HyperDashFruit && customFruitColour) + if (config == CatchSkinColour.HyperDashFruit && customFruitColour) return SkinUtils.As(new Bindable(CustomHyperDashFruitColour)); - if (config == CatchSkinConfiguration.HyperDashAfterImage && customAfterColour) + if (config == CatchSkinColour.HyperDashAfterImage && customAfterColour) return SkinUtils.As(new Bindable(CustomHyperDashAfterColour)); } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/FruitPiece.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/FruitPiece.cs index c8f7c4912e..16818746b5 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/FruitPiece.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/FruitPiece.cs @@ -64,8 +64,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables }); var hyperDashColour = - skin.GetConfig(CatchSkinConfiguration.HyperDashFruit)?.Value ?? - skin.GetConfig(CatchSkinConfiguration.HyperDash)?.Value ?? + skin.GetConfig(CatchSkinColour.HyperDashFruit)?.Value ?? + skin.GetConfig(CatchSkinColour.HyperDash)?.Value ?? Catcher.DefaultHyperDashColour; if (hitObject.HyperDash) diff --git a/osu.Game.Rulesets.Catch/Skinning/CatchSkinConfiguration.cs b/osu.Game.Rulesets.Catch/Skinning/CatchSkinColour.cs similarity index 94% rename from osu.Game.Rulesets.Catch/Skinning/CatchSkinConfiguration.cs rename to osu.Game.Rulesets.Catch/Skinning/CatchSkinColour.cs index aea5beaa6b..2ad8f89739 100644 --- a/osu.Game.Rulesets.Catch/Skinning/CatchSkinConfiguration.cs +++ b/osu.Game.Rulesets.Catch/Skinning/CatchSkinColour.cs @@ -3,7 +3,7 @@ namespace osu.Game.Rulesets.Catch.Skinning { - public enum CatchSkinConfiguration + public enum CatchSkinColour { /// /// The colour to be used for the catcher while on hyper-dashing state. diff --git a/osu.Game.Rulesets.Catch/Skinning/LegacyFruitPiece.cs b/osu.Game.Rulesets.Catch/Skinning/LegacyFruitPiece.cs index 99ecf12fd3..5235058c52 100644 --- a/osu.Game.Rulesets.Catch/Skinning/LegacyFruitPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/LegacyFruitPiece.cs @@ -55,8 +55,8 @@ namespace osu.Game.Rulesets.Catch.Skinning if (drawableCatchObject.HitObject.HyperDash) { var hyperDashColour = - skin.GetConfig(CatchSkinConfiguration.HyperDashFruit)?.Value ?? - skin.GetConfig(CatchSkinConfiguration.HyperDash)?.Value ?? + skin.GetConfig(CatchSkinColour.HyperDashFruit)?.Value ?? + skin.GetConfig(CatchSkinColour.HyperDash)?.Value ?? Catcher.DefaultHyperDashColour; var hyperDash = new Sprite From 35647d59a6f46e9f9284995edcd43af377954158 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 30 Mar 2020 19:09:05 +0900 Subject: [PATCH 0054/1117] Add failing test --- .../TestSceneOverlayScrollContainer.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayScrollContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayScrollContainer.cs index 1fc85c3c04..684436459f 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayScrollContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayScrollContainer.cs @@ -74,6 +74,20 @@ namespace osu.Game.Tests.Visual.UserInterface AddUntilStep("scrolled back to start", () => Precision.AlmostEquals(scroll.Current, 0, 0.1f)); } + [Test] + public void TestClick() + { + AddStep("scroll to end", () => scroll.ScrollToEnd(false)); + + AddStep("click button", () => + { + InputManager.MoveMouseTo(scroll.Button); + InputManager.Click(MouseButton.Left); + }); + + AddUntilStep("scrolled back to start", () => Precision.AlmostEquals(scroll.Current, 0, 0.1f)); + } + [Test] public void TestMultipleClicks() { From 179bd1ce7ebe2384fc819bdac20865f5176fd7d7 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 30 Mar 2020 13:38:04 +0300 Subject: [PATCH 0055/1117] Fix failing test --- osu.Game/Overlays/OverlayScrollContainer.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/OverlayScrollContainer.cs b/osu.Game/Overlays/OverlayScrollContainer.cs index 1a875ded95..a9524b9d32 100644 --- a/osu.Game/Overlays/OverlayScrollContainer.cs +++ b/osu.Game/Overlays/OverlayScrollContainer.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -83,7 +84,10 @@ namespace osu.Game.Overlays public ScrollToTopButton() { Size = new Vector2(50); - Child = button = new Button(); + Child = button = new Button + { + AreaState = { BindTarget = State } + }; } protected override bool OnMouseDown(MouseDownEvent e) => true; @@ -94,7 +98,9 @@ namespace osu.Game.Overlays private class Button : OsuHoverContainer { - public override bool PropagatePositionalInputSubTree => Alpha == 1; + public readonly Bindable AreaState = new Bindable(); + + public override bool HandlePositionalInput => AreaState.Value == Visibility.Visible; protected override IEnumerable EffectTargets => new[] { background }; From 3cae0cedeea80e1dc6206f26e5526a5b7e22662a Mon Sep 17 00:00:00 2001 From: Lucas A Date: Mon, 30 Mar 2020 12:59:39 +0200 Subject: [PATCH 0056/1117] Add a game setting to disable the layer --- osu.Game/Configuration/OsuConfigManager.cs | 2 ++ .../Settings/Sections/Gameplay/GeneralSettings.cs | 6 ++++++ osu.Game/Screens/Play/HUD/FailingLayer.cs | 8 +++++++- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 41f6747b74..6fed5ea5a2 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -87,6 +87,7 @@ namespace osu.Game.Configuration Set(OsuSetting.ShowInterface, true); Set(OsuSetting.ShowProgressGraph, true); Set(OsuSetting.ShowHealthDisplayWhenCantFail, true); + Set(OsuSetting.FadePlayfieldWhenHealthLow, true); Set(OsuSetting.KeyOverlay, false); Set(OsuSetting.ScoreMeter, ScoreMeterType.HitErrorBoth); @@ -181,6 +182,7 @@ namespace osu.Game.Configuration ShowInterface, ShowProgressGraph, ShowHealthDisplayWhenCantFail, + FadePlayfieldWhenHealthLow, MouseDisableButtons, MouseDisableWheel, AudioOffset, diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs index 2d2cd42213..4b75910454 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs @@ -53,6 +53,12 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay Keywords = new[] { "hp", "bar" } }, new SettingsCheckbox + { + LabelText = "Fade playfield to red when health is low", + Bindable = config.GetBindable(OsuSetting.FadePlayfieldWhenHealthLow), + Keywords = new[] { "hp", "low", "playfield", "red" } + }, + new SettingsCheckbox { LabelText = "Always show key overlay", Bindable = config.GetBindable(OsuSetting.KeyOverlay) diff --git a/osu.Game/Screens/Play/HUD/FailingLayer.cs b/osu.Game/Screens/Play/HUD/FailingLayer.cs index 97d2458674..761178b93d 100644 --- a/osu.Game/Screens/Play/HUD/FailingLayer.cs +++ b/osu.Game/Screens/Play/HUD/FailingLayer.cs @@ -3,9 +3,11 @@ using System; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; using osu.Framework.Utils; +using osu.Game.Configuration; using osu.Game.Graphics; namespace osu.Game.Screens.Play.HUD @@ -21,6 +23,8 @@ namespace osu.Game.Screens.Play.HUD private readonly Box box; + private Bindable enabled; + /// /// The threshold under which the current player life should be considered low and the layer should start fading in. /// @@ -37,9 +41,11 @@ namespace osu.Game.Screens.Play.HUD } [BackgroundDependencyLoader] - private void load(OsuColour color) + private void load(OsuColour color, OsuConfigManager config) { box.Colour = color.Red; + enabled = config.GetBindable(OsuSetting.FadePlayfieldWhenHealthLow); + enabled.BindValueChanged(e => this.FadeTo(e.NewValue ? 1 : 0, fade_time, Easing.OutQuint), true); } protected override void Update() From e26fbd5ed87681d8577fc5d9dced39cfd199b9cf Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 31 Mar 2020 13:45:59 +0300 Subject: [PATCH 0057/1117] Remove overcomplicated stuff --- .../TestSceneOverlayScrollContainer.cs | 6 +- osu.Game/Overlays/OverlayScrollContainer.cs | 159 ++++++++---------- 2 files changed, 75 insertions(+), 90 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayScrollContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayScrollContainer.cs index 684436459f..0eccc907a1 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayScrollContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayScrollContainer.cs @@ -55,13 +55,13 @@ namespace osu.Game.Tests.Visual.UserInterface [Test] public void TestButtonVisibility() { - AddAssert("button is hidden", () => scroll.Button.State.Value == Visibility.Hidden); + AddAssert("button is hidden", () => scroll.Button.Current.Value == Visibility.Hidden); AddStep("scroll to end", () => scroll.ScrollToEnd(false)); - AddAssert("button is visible", () => scroll.Button.State.Value == Visibility.Visible); + AddAssert("button is visible", () => scroll.Button.Current.Value == Visibility.Visible); AddStep("scroll to start", () => scroll.ScrollToStart(false)); - AddAssert("button is hidden", () => scroll.Button.State.Value == Visibility.Hidden); + AddAssert("button is hidden", () => scroll.Button.Current.Value == Visibility.Hidden); } [Test] diff --git a/osu.Game/Overlays/OverlayScrollContainer.cs b/osu.Game/Overlays/OverlayScrollContainer.cs index a9524b9d32..f96d9e3a31 100644 --- a/osu.Game/Overlays/OverlayScrollContainer.cs +++ b/osu.Game/Overlays/OverlayScrollContainer.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.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -11,6 +10,7 @@ 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.Events; using osu.Game.Graphics.Containers; using osuTK; @@ -43,7 +43,7 @@ namespace osu.Game.Overlays { ScrollToStart(); currentTarget = Target; - Button.State.Value = Visibility.Hidden; + Button.Current.Value = Visibility.Hidden; } }); } @@ -54,7 +54,7 @@ namespace osu.Game.Overlays if (ScrollContent.DrawHeight + button_scroll_position < DrawHeight) { - Button.State.Value = Visibility.Hidden; + Button.Current.Value = Visibility.Hidden; return; } @@ -62,113 +62,98 @@ namespace osu.Game.Overlays return; currentTarget = Target; - Button.State.Value = Current > button_scroll_position ? Visibility.Visible : Visibility.Hidden; + Button.Current.Value = Current > button_scroll_position ? Visibility.Visible : Visibility.Hidden; } - public class ScrollToTopButton : VisibilityContainer + public class ScrollToTopButton : OsuHoverContainer, IHasCurrentValue { private const int fade_duration = 500; - public Action Action + private readonly BindableWithCurrent current = new BindableWithCurrent(); + + public Bindable Current { - get => button.Action; - set => button.Action = value; + get => current.Current; + set => current.Current = value; } - public override bool PropagatePositionalInputSubTree => true; + protected override IEnumerable EffectTargets => new[] { background }; - protected override bool StartHidden => true; + private Color4 flashColour; - private readonly Button button; + private readonly Container content; + private readonly Box background; public ScrollToTopButton() { Size = new Vector2(50); - Child = button = new Button + Alpha = 0; + Add(content = new CircularContainer { - AreaState = { BindTarget = State } - }; + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Masking = true, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Offset = new Vector2(0f, 1f), + Radius = 3f, + Colour = Color4.Black.Opacity(0.25f), + }, + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both + }, + new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(15), + Icon = FontAwesome.Solid.ChevronUp + } + } + }); + + TooltipText = "Scroll to top"; } - protected override bool OnMouseDown(MouseDownEvent e) => true; - - protected override void PopIn() => button.FadeIn(fade_duration, Easing.OutQuint); - - protected override void PopOut() => button.FadeOut(fade_duration, Easing.OutQuint); - - private class Button : OsuHoverContainer + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) { - public readonly Bindable AreaState = new Bindable(); + IdleColour = colourProvider.Background6; + HoverColour = colourProvider.Background5; + flashColour = colourProvider.Light1; + } - public override bool HandlePositionalInput => AreaState.Value == Visibility.Visible; - - protected override IEnumerable EffectTargets => new[] { background }; - - private Color4 flashColour; - - private readonly Container content; - private readonly Box background; - - public Button() + protected override void LoadComplete() + { + base.LoadComplete(); + Current.BindValueChanged(visibility => { - RelativeSizeAxes = Axes.Both; - Add(content = new CircularContainer - { - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Masking = true, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Offset = new Vector2(0f, 1f), - Radius = 3f, - Colour = Color4.Black.Opacity(0.25f), - }, - Children = new Drawable[] - { - background = new Box - { - RelativeSizeAxes = Axes.Both - }, - new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(15), - Icon = FontAwesome.Solid.ChevronUp - } - } - }); + Enabled.Value = visibility.NewValue == Visibility.Visible; + this.FadeTo(visibility.NewValue == Visibility.Visible ? 1 : 0, fade_duration, Easing.OutQuint); + }, true); + } - TooltipText = "Scroll to top"; - } + protected override bool OnClick(ClickEvent e) + { + background.FlashColour(flashColour, 800, Easing.OutQuint); + return base.OnClick(e); + } - [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider) - { - IdleColour = colourProvider.Background6; - HoverColour = colourProvider.Background5; - flashColour = colourProvider.Light1; - } + protected override bool OnMouseDown(MouseDownEvent e) + { + content.ScaleTo(0.75f, 2000, Easing.OutQuint); + return true; + } - protected override bool OnClick(ClickEvent e) - { - background.FlashColour(flashColour, 800, Easing.OutQuint); - return base.OnClick(e); - } - - protected override bool OnMouseDown(MouseDownEvent e) - { - content.ScaleTo(0.75f, 2000, Easing.OutQuint); - return true; - } - - protected override void OnMouseUp(MouseUpEvent e) - { - content.ScaleTo(1, 1000, Easing.OutElastic); - base.OnMouseUp(e); - } + protected override void OnMouseUp(MouseUpEvent e) + { + content.ScaleTo(1, 1000, Easing.OutElastic); + base.OnMouseUp(e); } } } From 1562612f41681da4ead59c0d220068f933ef5faf Mon Sep 17 00:00:00 2001 From: Lucas A Date: Wed, 1 Apr 2020 15:12:31 +0200 Subject: [PATCH 0058/1117] Update visual tests and remove unessecary XMLDoc tag --- .../Visual/Gameplay/TestSceneFailingLayer.cs | 30 ++++++++++++++++--- osu.Game/Screens/Play/HUD/HealthDisplay.cs | 1 - 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs index 3016890ade..97fe0ac769 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs @@ -1,7 +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.Linq; using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Testing; +using osu.Game.Configuration; using osu.Game.Screens.Play.HUD; namespace osu.Game.Tests.Visual.Gameplay @@ -10,22 +15,39 @@ namespace osu.Game.Tests.Visual.Gameplay { private readonly FailingLayer layer; + [Resolved] + private OsuConfigManager config { get; set; } + public TestSceneFailingLayer() { Child = layer = new FailingLayer(); } + [Test] + public void TestLayerConfig() + { + AddStep("enable layer", () => config.Set(OsuSetting.FadePlayfieldWhenHealthLow, true)); + AddWaitStep("wait for transition to finish", 5); + AddAssert("layer is enabled", () => layer.IsPresent); + + AddStep("disable layer", () => config.Set(OsuSetting.FadePlayfieldWhenHealthLow, false)); + AddWaitStep("wait for transition to finish", 5); + AddAssert("layer is disabled", () => !layer.IsPresent); + AddStep("restore layer enabling", () => config.Set(OsuSetting.FadePlayfieldWhenHealthLow, true)); + } + [Test] public void TestLayerFading() { - AddSliderStep("current health", 0.0, 1.0, 1.0, val => - { - layer.Current.Value = val; - }); + AddSliderStep("current health", 0.0, 1.0, 1.0, val => layer.Current.Value = val); + var box = layer.ChildrenOfType().First(); AddStep("set health to 0.10", () => layer.Current.Value = 0.10); AddWaitStep("wait for fade to finish", 5); + AddAssert("layer fade is visible", () => box.IsPresent); AddStep("set health to 1", () => layer.Current.Value = 1f); + AddWaitStep("wait for fade to finish", 10); + AddAssert("layer fade is invisible", () => !box.IsPresent); } } } diff --git a/osu.Game/Screens/Play/HUD/HealthDisplay.cs b/osu.Game/Screens/Play/HUD/HealthDisplay.cs index 4ea08626ad..01cb64a88c 100644 --- a/osu.Game/Screens/Play/HUD/HealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/HealthDisplay.cs @@ -23,7 +23,6 @@ namespace osu.Game.Screens.Play.HUD /// /// Bind the tracked fields of to this health display. /// - /// public void BindHealthProcessor(HealthProcessor processor) { Current.BindTo(processor.Health); From 7e82f5740b0668e1f21321cd257be9928026ad54 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 3 Apr 2020 19:35:50 +0300 Subject: [PATCH 0059/1117] Add a skin extension for simplifying falling back on hyper-dash colours --- .../Objects/Drawables/FruitPiece.cs | 3 +-- .../Skinning/CatchSkinExtensions.cs | 16 ++++++++++++++++ .../Skinning/LegacyFruitPiece.cs | 7 +------ 3 files changed, 18 insertions(+), 8 deletions(-) create mode 100644 osu.Game.Rulesets.Catch/Skinning/CatchSkinExtensions.cs diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/FruitPiece.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/FruitPiece.cs index 16818746b5..2437958916 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/FruitPiece.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/FruitPiece.cs @@ -64,8 +64,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables }); var hyperDashColour = - skin.GetConfig(CatchSkinColour.HyperDashFruit)?.Value ?? - skin.GetConfig(CatchSkinColour.HyperDash)?.Value ?? + skin.GetHyperDashFruitColour()?.Value ?? Catcher.DefaultHyperDashColour; if (hitObject.HyperDash) diff --git a/osu.Game.Rulesets.Catch/Skinning/CatchSkinExtensions.cs b/osu.Game.Rulesets.Catch/Skinning/CatchSkinExtensions.cs new file mode 100644 index 0000000000..8fc0831918 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Skinning/CatchSkinExtensions.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 osu.Framework.Bindables; +using osu.Game.Skinning; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Catch.Skinning +{ + internal static class CatchSkinExtensions + { + public static IBindable GetHyperDashFruitColour(this ISkin skin) + => skin.GetConfig(CatchSkinColour.HyperDashFruit) ?? + skin.GetConfig(CatchSkinColour.HyperDash); + } +} diff --git a/osu.Game.Rulesets.Catch/Skinning/LegacyFruitPiece.cs b/osu.Game.Rulesets.Catch/Skinning/LegacyFruitPiece.cs index 5235058c52..d8489399d2 100644 --- a/osu.Game.Rulesets.Catch/Skinning/LegacyFruitPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/LegacyFruitPiece.cs @@ -54,15 +54,10 @@ namespace osu.Game.Rulesets.Catch.Skinning if (drawableCatchObject.HitObject.HyperDash) { - var hyperDashColour = - skin.GetConfig(CatchSkinColour.HyperDashFruit)?.Value ?? - skin.GetConfig(CatchSkinColour.HyperDash)?.Value ?? - Catcher.DefaultHyperDashColour; - var hyperDash = new Sprite { Texture = skin.GetTexture(lookupName), - Colour = hyperDashColour, + Colour = skin.GetHyperDashFruitColour()?.Value ?? Catcher.DefaultHyperDashColour, Anchor = Anchor.Centre, Origin = Anchor.Centre, Blending = BlendingParameters.Additive, From 0340b6db51f88120511ac243cc0872bc8527eb32 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 3 Apr 2020 19:50:32 +0300 Subject: [PATCH 0060/1117] Describe step names more --- .../TestSceneHyperDashColouring.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs index ebc3d3bff1..066b399f13 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Catch.Tests { DrawableFruit drawableFruit = null; - AddStep("setup fruit", () => + AddStep("setup hyper-dash fruit", () => { var fruit = new Fruit { HyperDashTarget = new Banana() }; fruit.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); @@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Catch.Tests }, false, false, false, legacyFruit); }); - AddAssert("default colour", () => + AddAssert("hyper-dash fruit has default colour", () => legacyFruit ? checkLegacyFruitHyperDashColour(drawableFruit, Catcher.DefaultHyperDashColour) : checkFruitHyperDashColour(drawableFruit, Catcher.DefaultHyperDashColour)); @@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Catch.Tests { DrawableFruit drawableFruit = null; - AddStep("setup fruit", () => + AddStep("setup hyper-dash fruit", () => { var fruit = new Fruit { HyperDashTarget = new Banana() }; fruit.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); @@ -77,7 +77,7 @@ namespace osu.Game.Rulesets.Catch.Tests }, customCatcherHyperDashColour, false, true, legacyFruit); }); - AddAssert("custom colour", () => + AddAssert("hyper-dash fruit use fruit colour from skin", () => legacyFruit ? checkLegacyFruitHyperDashColour(drawableFruit, TestSkin.CustomHyperDashFruitColour) : checkFruitHyperDashColour(drawableFruit, TestSkin.CustomHyperDashFruitColour)); @@ -89,7 +89,7 @@ namespace osu.Game.Rulesets.Catch.Tests { DrawableFruit drawableFruit = null; - AddStep("setup fruit", () => + AddStep("setup hyper-dash fruit", () => { var fruit = new Fruit { HyperDashTarget = new Banana() }; fruit.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); @@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Catch.Tests }, true, false, false, legacyFruit); }); - AddAssert("catcher custom colour", () => + AddAssert("hyper-dash fruit colour falls back to catcher colour from skin", () => legacyFruit ? checkLegacyFruitHyperDashColour(drawableFruit, TestSkin.CustomHyperDashColour) : checkFruitHyperDashColour(drawableFruit, TestSkin.CustomHyperDashColour)); From dd684b68d9cef47cc6e5a61a730343536428dc3b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 3 Apr 2020 19:53:38 +0300 Subject: [PATCH 0061/1117] Make parameters required --- osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs index 066b399f13..2009099a61 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs @@ -109,7 +109,7 @@ namespace osu.Game.Rulesets.Catch.Tests : checkFruitHyperDashColour(drawableFruit, TestSkin.CustomHyperDashColour)); } - private Drawable setupSkinHierarchy(Drawable child, bool customCatcherColour = false, bool customAfterColour = false, bool customFruitColour = false, bool legacySkin = true) + private Drawable setupSkinHierarchy(Drawable child, bool customCatcherColour, bool customAfterColour, bool customFruitColour, bool legacySkin = true) { var testSkinProvider = new SkinProvidingContainer(new TestSkin(customCatcherColour, customAfterColour, customFruitColour)); @@ -143,7 +143,7 @@ namespace osu.Game.Rulesets.Catch.Tests private readonly bool customAfterColour; private readonly bool customFruitColour; - public TestSkin(bool customCatcherColour = false, bool customAfterColour = false, bool customFruitColour = false) + public TestSkin(bool customCatcherColour, bool customAfterColour, bool customFruitColour) { this.customCatcherColour = customCatcherColour; this.customAfterColour = customAfterColour; From 8cdae790c3b0fc90996ae473ffe998206f9af51a Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 3 Apr 2020 17:32:37 +0200 Subject: [PATCH 0062/1117] Load user rulesets from the game data directory --- osu.Game/OsuGameBase.cs | 2 +- osu.Game/Rulesets/RulesetStore.cs | 40 +++++++++++++++++++++++++++---- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 5487bd9320..609b6ce98e 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -168,7 +168,7 @@ namespace osu.Game var defaultBeatmap = new DummyWorkingBeatmap(Audio, Textures); - dependencies.Cache(RulesetStore = new RulesetStore(contextFactory)); + dependencies.Cache(RulesetStore = new RulesetStore(contextFactory, Storage)); dependencies.Cache(FileStore = new FileStore(contextFactory, Storage)); // ordering is important here to ensure foreign keys rules are not broken in ModelStore.Cleanup() diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index a389d4ff75..c3c7b653da 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -7,6 +7,7 @@ using System.IO; using System.Linq; using System.Reflection; using osu.Framework.Logging; +using osu.Framework.Platform; using osu.Game.Database; namespace osu.Game.Rulesets @@ -17,16 +18,20 @@ namespace osu.Game.Rulesets private readonly Dictionary loadedAssemblies = new Dictionary(); - public RulesetStore(IDatabaseContextFactory factory) + private readonly Storage rulesetStorage; + + public RulesetStore(IDatabaseContextFactory factory, Storage storage = null) : base(factory) { + rulesetStorage = storage?.GetStorageForDirectory("rulesets"); + AppDomain.CurrentDomain.AssemblyResolve += resolveRulesetDependencyAssembly; + // On android in release configuration assemblies are loaded from the apk directly into memory. // We cannot read assemblies from cwd, so should check loaded assemblies instead. loadFromAppDomain(); loadFromDisk(); + loadUserRulesets(); addMissingRulesets(); - - AppDomain.CurrentDomain.AssemblyResolve += resolveRulesetAssembly; } /// @@ -48,7 +53,17 @@ namespace osu.Game.Rulesets /// public IEnumerable AvailableRulesets { get; private set; } - private Assembly resolveRulesetAssembly(object sender, ResolveEventArgs args) => loadedAssemblies.Keys.FirstOrDefault(a => a.FullName == args.Name); + private Assembly resolveRulesetDependencyAssembly(object sender, ResolveEventArgs args) + { + var asm = new AssemblyName(args.Name); + + // this assumes the only explicit dependency of the ruleset is the game core assembly. + // the ruleset dependency on the game core assembly requires manual resolving, transient dependencies should be resolved automatically + if (asm.Name.Equals(typeof(OsuGame).Assembly.GetName().Name, StringComparison.Ordinal)) + return Assembly.GetExecutingAssembly(); + + return null; + } private void addMissingRulesets() { @@ -120,6 +135,21 @@ namespace osu.Game.Rulesets } } + private void loadUserRulesets() + { + try + { + var rulesets = rulesetStorage?.GetFiles(".", $"{ruleset_library_prefix}.*.dll"); + + foreach (var ruleset in rulesets.Where(f => !f.Contains("Tests"))) + loadRulesetFromFile(rulesetStorage?.GetFullPath(ruleset)); + } + catch (Exception e) + { + Logger.Error(e, "Couldn't load user rulesets"); + } + } + private void loadFromDisk() { try @@ -175,7 +205,7 @@ namespace osu.Game.Rulesets protected virtual void Dispose(bool disposing) { - AppDomain.CurrentDomain.AssemblyResolve -= resolveRulesetAssembly; + AppDomain.CurrentDomain.AssemblyResolve -= resolveRulesetDependencyAssembly; } } } From f3bcb0628c828b12484c4b6f46be8b44c3599ccd Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 4 Apr 2020 19:09:52 +0300 Subject: [PATCH 0063/1117] Add helper methods for retrieving other skin hyper-dash colours --- osu.Game.Rulesets.Catch/Skinning/CatchSkinExtensions.cs | 7 +++++++ osu.Game.Rulesets.Catch/UI/Catcher.cs | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Skinning/CatchSkinExtensions.cs b/osu.Game.Rulesets.Catch/Skinning/CatchSkinExtensions.cs index 8fc0831918..48e11121ea 100644 --- a/osu.Game.Rulesets.Catch/Skinning/CatchSkinExtensions.cs +++ b/osu.Game.Rulesets.Catch/Skinning/CatchSkinExtensions.cs @@ -9,6 +9,13 @@ namespace osu.Game.Rulesets.Catch.Skinning { internal static class CatchSkinExtensions { + public static IBindable GetHyperDashCatcherColour(this ISkin skin) + => skin.GetConfig(CatchSkinColour.HyperDash); + + public static IBindable GetHyperDashEndGlowColour(this ISkin skin) + => skin.GetConfig(CatchSkinColour.HyperDashAfterImage) ?? + skin.GetConfig(CatchSkinColour.HyperDash); + public static IBindable GetHyperDashFruitColour(this ISkin skin) => skin.GetConfig(CatchSkinColour.HyperDashFruit) ?? skin.GetConfig(CatchSkinColour.HyperDash); diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 98cc10aa31..49c9a77277 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -389,9 +389,9 @@ namespace osu.Game.Rulesets.Catch.UI { base.SkinChanged(skin, allowFallback); - hyperDashColour = skin.GetConfig(CatchSkinColour.HyperDash)?.Value ?? DefaultHyperDashColour; - hyperDashEndGlowColour = skin.GetConfig(CatchSkinColour.HyperDashAfterImage)?.Value ?? hyperDashColour; updateCatcherColour(); + hyperDashColour = skin.GetHyperDashCatcherColour()?.Value ?? DefaultHyperDashColour; + hyperDashEndGlowColour = skin.GetHyperDashEndGlowColour()?.Value ?? DefaultHyperDashColour; } protected override void Update() From 50604dc7b22624632f015fcb55c0cb44bd3f4080 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 4 Apr 2020 19:29:06 +0300 Subject: [PATCH 0064/1117] Update catcher hyper-dashing colours on changing hyper-dash state only --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 49c9a77277..0b73c510d9 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -254,7 +254,10 @@ namespace osu.Game.Rulesets.Catch.UI hyperDashDirection = 0; if (wasHyperDashing) + { + updateCatcherColour(false); Trail &= Dashing; + } } else { @@ -264,6 +267,7 @@ namespace osu.Game.Rulesets.Catch.UI if (!wasHyperDashing) { + updateCatcherColour(true); Trail = true; var hyperDashEndGlow = createAdditiveSprite(endGlowSprites); @@ -273,15 +277,13 @@ namespace osu.Game.Rulesets.Catch.UI hyperDashEndGlow.Expire(true); } } - - updateCatcherColour(); } - private void updateCatcherColour() + private void updateCatcherColour(bool hyperDashing) { const float hyper_dash_transition_length = 180; - if (HyperDashing) + if (hyperDashing) { this.FadeColour(hyperDashColour == DefaultHyperDashColour ? Color4.OrangeRed : hyperDashColour, hyper_dash_transition_length, Easing.OutQuint); this.FadeTo(0.2f, hyper_dash_transition_length, Easing.OutQuint); @@ -389,9 +391,9 @@ namespace osu.Game.Rulesets.Catch.UI { base.SkinChanged(skin, allowFallback); - updateCatcherColour(); hyperDashColour = skin.GetHyperDashCatcherColour()?.Value ?? DefaultHyperDashColour; hyperDashEndGlowColour = skin.GetHyperDashEndGlowColour()?.Value ?? DefaultHyperDashColour; + updateCatcherColour(HyperDashing); } protected override void Update() From fbe95a52e3b068d37c49dafa6057a744d0d0df9c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 4 Apr 2020 19:29:41 +0300 Subject: [PATCH 0065/1117] Remove unnecessary restating comment --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 0b73c510d9..1cb6987397 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -294,7 +294,6 @@ namespace osu.Game.Rulesets.Catch.UI this.FadeTo(1f, hyper_dash_transition_length, Easing.OutQuint); } - // update hyper-dash colour of the hyper-dashing catcher sprites containers. hyperDashTrails?.FadeColour(hyperDashColour, hyper_dash_transition_length, Easing.OutQuint); endGlowSprites?.FadeColour(hyperDashEndGlowColour, hyper_dash_transition_length, Easing.OutQuint); } From 19f39fe6327ffd3b3bdf27cde0da4d5b1a2801a7 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 4 Apr 2020 19:33:52 +0300 Subject: [PATCH 0066/1117] Change AdditiveTarget into a set method --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 39 ++++++++++++----------- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 5 ++- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 1cb6987397..0e42c19455 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -42,25 +42,6 @@ namespace osu.Game.Rulesets.Catch.UI private Container hyperDashTrails; private Container endGlowSprites; - public Container AdditiveTarget - { - get => additiveTarget; - set - { - if (additiveTarget == value) - return; - - additiveTarget?.RemoveRange(new[] { dashTrails, hyperDashTrails, endGlowSprites }); - - additiveTarget = value; - additiveTarget?.AddRange(new[] - { - dashTrails ??= new Container { RelativeSizeAxes = Axes.Both, Colour = Color4.White }, - hyperDashTrails ??= new Container { RelativeSizeAxes = Axes.Both, Colour = hyperDashColour }, - endGlowSprites ??= new Container { RelativeSizeAxes = Axes.Both, Colour = hyperDashEndGlowColour }, - }); - } - } public CatcherAnimationState CurrentState { get; private set; } @@ -167,6 +148,26 @@ namespace osu.Game.Rulesets.Catch.UI updateCatcher(); } + /// + /// Sets container target to provide catcher additive trails content in. + /// + /// The container to add catcher trails in. + public void SetAdditiveTarget(Container target) + { + if (additiveTarget == target) + return; + + additiveTarget?.RemoveRange(new[] { dashTrails, hyperDashTrails, endGlowSprites }); + + additiveTarget = target; + additiveTarget?.AddRange(new[] + { + dashTrails ??= new Container { RelativeSizeAxes = Axes.Both, Colour = Color4.White }, + hyperDashTrails ??= new Container { RelativeSizeAxes = Axes.Both, Colour = hyperDashColour }, + endGlowSprites ??= new Container { RelativeSizeAxes = Axes.Both, Colour = hyperDashEndGlowColour }, + }); + } + /// /// Add a caught fruit to the catcher's stack. /// diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 37501736ff..641b81599e 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -33,10 +33,9 @@ namespace osu.Game.Rulesets.Catch.UI { RelativeSizeAxes = Axes.X; Height = CATCHER_SIZE; - Child = MovableCatcher = new Catcher(difficulty); - // this property adds containers to 'this' so it must not be set in the object initializer. - MovableCatcher.AdditiveTarget = this; + Child = MovableCatcher = new Catcher(difficulty); + MovableCatcher.SetAdditiveTarget(this); } public static float GetCatcherSize(BeatmapDifficulty difficulty) From 0e45a4d54e1dd372f120dbfb926234065f4158af Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 4 Apr 2020 20:13:46 +0200 Subject: [PATCH 0067/1117] Add back cached ruleset assembly lookup --- osu.Game/Rulesets/RulesetStore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index c3c7b653da..7b4c0302aa 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -62,7 +62,7 @@ namespace osu.Game.Rulesets if (asm.Name.Equals(typeof(OsuGame).Assembly.GetName().Name, StringComparison.Ordinal)) return Assembly.GetExecutingAssembly(); - return null; + return loadedAssemblies.Keys.FirstOrDefault(a => a.FullName == asm.FullName); } private void addMissingRulesets() From 0014a8404e17d196d3aebf386346efd20790d023 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 4 Apr 2020 23:12:42 +0300 Subject: [PATCH 0068/1117] GetHyperDashEndGlowColour() -> GetHyperDashCatcherAfterImageColour() --- osu.Game.Rulesets.Catch/Skinning/CatchSkinExtensions.cs | 2 +- osu.Game.Rulesets.Catch/UI/Catcher.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Skinning/CatchSkinExtensions.cs b/osu.Game.Rulesets.Catch/Skinning/CatchSkinExtensions.cs index 48e11121ea..06d21f8c5e 100644 --- a/osu.Game.Rulesets.Catch/Skinning/CatchSkinExtensions.cs +++ b/osu.Game.Rulesets.Catch/Skinning/CatchSkinExtensions.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Catch.Skinning public static IBindable GetHyperDashCatcherColour(this ISkin skin) => skin.GetConfig(CatchSkinColour.HyperDash); - public static IBindable GetHyperDashEndGlowColour(this ISkin skin) + public static IBindable GetHyperDashCatcherAfterImageColour(this ISkin skin) => skin.GetConfig(CatchSkinColour.HyperDashAfterImage) ?? skin.GetConfig(CatchSkinColour.HyperDash); diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 0e42c19455..0d5b454a9d 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -392,7 +392,7 @@ namespace osu.Game.Rulesets.Catch.UI base.SkinChanged(skin, allowFallback); hyperDashColour = skin.GetHyperDashCatcherColour()?.Value ?? DefaultHyperDashColour; - hyperDashEndGlowColour = skin.GetHyperDashEndGlowColour()?.Value ?? DefaultHyperDashColour; + hyperDashEndGlowColour = skin.GetHyperDashCatcherAfterImageColour()?.Value ?? DefaultHyperDashColour; updateCatcherColour(HyperDashing); } From c4f7b4576848da614959c8a427f7886e7a2f66f3 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 5 Apr 2020 00:02:33 +0300 Subject: [PATCH 0069/1117] Revert "Add support for custom hyper-dash fruit colouring" This reverts commit 6f2cc5471adabc4392fcf1f63a5de32266016c10 and also its testing cases. This became dead code after actual correct osu!catch skin colouring, we don't support modern skinning (non-legacy skinning) at the moment, so for what it's worth this can be reverted to default red-coloured --- .../TestSceneHyperDashColouring.cs | 67 ++++++------------- .../Objects/Drawables/FruitPiece.cs | 12 +--- 2 files changed, 23 insertions(+), 56 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs index 2009099a61..10739a3131 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs @@ -4,15 +4,10 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; -using osu.Framework.Audio.Sample; -using osu.Framework.Bindables; 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.Testing; -using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Catch.Objects; @@ -31,9 +26,8 @@ namespace osu.Game.Rulesets.Catch.Tests [Resolved] private SkinManager skins { get; set; } - [TestCase(false)] - [TestCase(true)] - public void TestHyperDashFruitColour(bool legacyFruit) + [Test] + public void TestHyperDashFruitColour() { DrawableFruit drawableFruit = null; @@ -47,20 +41,15 @@ namespace osu.Game.Rulesets.Catch.Tests Anchor = Anchor.Centre, Origin = Anchor.Centre, Scale = new Vector2(4f), - }, false, false, false, legacyFruit); + }, false, false, false); }); - AddAssert("hyper-dash fruit has default colour", () => - legacyFruit - ? checkLegacyFruitHyperDashColour(drawableFruit, Catcher.DefaultHyperDashColour) - : checkFruitHyperDashColour(drawableFruit, Catcher.DefaultHyperDashColour)); + AddAssert("hyper-dash fruit has default colour", () => checkLegacyFruitHyperDashColour(drawableFruit, Catcher.DefaultHyperDashColour)); } - [TestCase(false, true)] - [TestCase(false, false)] - [TestCase(true, true)] - [TestCase(true, false)] - public void TestCustomHyperDashFruitColour(bool legacyFruit, bool customCatcherHyperDashColour) + [TestCase(true)] + [TestCase(false)] + public void TestCustomHyperDashFruitColour(bool customCatcherHyperDashColour) { DrawableFruit drawableFruit = null; @@ -74,18 +63,14 @@ namespace osu.Game.Rulesets.Catch.Tests Anchor = Anchor.Centre, Origin = Anchor.Centre, Scale = new Vector2(4f), - }, customCatcherHyperDashColour, false, true, legacyFruit); + }, customCatcherHyperDashColour, false, true); }); - AddAssert("hyper-dash fruit use fruit colour from skin", () => - legacyFruit - ? checkLegacyFruitHyperDashColour(drawableFruit, TestSkin.CustomHyperDashFruitColour) - : checkFruitHyperDashColour(drawableFruit, TestSkin.CustomHyperDashFruitColour)); + AddAssert("hyper-dash fruit use fruit colour from skin", () => checkLegacyFruitHyperDashColour(drawableFruit, TestSkin.CustomHyperDashFruitColour)); } - [TestCase(false)] - [TestCase(true)] - public void TestCustomHyperDashFruitColourFallback(bool legacyFruit) + [Test] + public void TestCustomHyperDashFruitColourFallback() { DrawableFruit drawableFruit = null; @@ -100,36 +85,24 @@ namespace osu.Game.Rulesets.Catch.Tests Anchor = Anchor.Centre, Origin = Anchor.Centre, Scale = new Vector2(4f), - }, true, false, false, legacyFruit); + }, true, false, false); }); - AddAssert("hyper-dash fruit colour falls back to catcher colour from skin", () => - legacyFruit - ? checkLegacyFruitHyperDashColour(drawableFruit, TestSkin.CustomHyperDashColour) - : checkFruitHyperDashColour(drawableFruit, TestSkin.CustomHyperDashColour)); + AddAssert("hyper-dash fruit colour falls back to catcher colour from skin", () => checkLegacyFruitHyperDashColour(drawableFruit, TestSkin.CustomHyperDashColour)); } - private Drawable setupSkinHierarchy(Drawable child, bool customCatcherColour, bool customAfterColour, bool customFruitColour, bool legacySkin = true) + private Drawable setupSkinHierarchy(Drawable child, bool customCatcherColour, bool customAfterColour, bool customFruitColour) { + var legacySkinProvider = new SkinProvidingContainer(skins.GetSkin(DefaultLegacySkin.Info)); var testSkinProvider = new SkinProvidingContainer(new TestSkin(customCatcherColour, customAfterColour, customFruitColour)); + var legacySkinTransformer = new SkinProvidingContainer(new CatchLegacySkinTransformer(testSkinProvider)); - if (legacySkin) - { - var legacySkinProvider = new SkinProvidingContainer(skins.GetSkin(DefaultLegacySkin.Info)); - var legacySkinTransformer = new SkinProvidingContainer(new CatchLegacySkinTransformer(testSkinProvider)); - - return legacySkinProvider - .WithChild(testSkinProvider - .WithChild(legacySkinTransformer - .WithChild(child))); - } - - return testSkinProvider.WithChild(child); + return legacySkinProvider + .WithChild(testSkinProvider + .WithChild(legacySkinTransformer + .WithChild(child))); } - private bool checkFruitHyperDashColour(DrawableFruit fruit, Color4 expectedColour) => - fruit.ChildrenOfType().First().Drawable.ChildrenOfType().Single(c => c.BorderColour == expectedColour).Any(d => d.Colour == expectedColour); - private bool checkLegacyFruitHyperDashColour(DrawableFruit fruit, Color4 expectedColour) => fruit.ChildrenOfType().First().Drawable.ChildrenOfType().Any(c => c.Colour == expectedColour); diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/FruitPiece.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/FruitPiece.cs index 2437958916..359329885c 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/FruitPiece.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/FruitPiece.cs @@ -7,10 +7,8 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Game.Rulesets.Catch.Skinning; using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Skinning; using osuTK.Graphics; namespace osu.Game.Rulesets.Catch.Objects.Drawables @@ -34,7 +32,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables } [BackgroundDependencyLoader] - private void load(DrawableHitObject drawableObject, ISkinSource skin) + private void load(DrawableHitObject drawableObject) { DrawableCatchHitObject drawableCatchObject = (DrawableCatchHitObject)drawableObject; hitObject = drawableCatchObject.HitObject; @@ -63,10 +61,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables }, }); - var hyperDashColour = - skin.GetHyperDashFruitColour()?.Value ?? - Catcher.DefaultHyperDashColour; - if (hitObject.HyperDash) { AddInternal(new Circle @@ -74,7 +68,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables RelativeSizeAxes = Axes.Both, Anchor = Anchor.Centre, Origin = Anchor.Centre, - BorderColour = hyperDashColour, + BorderColour = Catcher.DefaultHyperDashColour, BorderThickness = 12f * RADIUS_ADJUST, Children = new Drawable[] { @@ -84,7 +78,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables Alpha = 0.3f, Blending = BlendingParameters.Additive, RelativeSizeAxes = Axes.Both, - Colour = hyperDashColour, + Colour = Catcher.DefaultHyperDashColour, } } }); From 10e65c4f53929cf477042c58aa302fd0f6e3b076 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 5 Apr 2020 00:10:12 +0300 Subject: [PATCH 0070/1117] Add handling for legacy CatchTheBeat section in LegacyDecoder --- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index 561707f9ef..743a470e6e 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -73,6 +73,9 @@ namespace osu.Game.Beatmaps.Formats switch (section) { case Section.Colours: + // osu!catch section only has colour settings + // so no harm in handling the entire section + case Section.CatchTheBeat: HandleColours(output, line); return; } @@ -149,7 +152,8 @@ namespace osu.Game.Beatmaps.Formats HitObjects, Variables, Fonts, - Mania + CatchTheBeat, + Mania, } internal class LegacyDifficultyControlPoint : DifficultyControlPoint From 55d076d6f359ed50edd3cf371195b656effd9929 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 5 Apr 2020 00:10:25 +0300 Subject: [PATCH 0071/1117] Transform CatchSkinColour lookup to skin configuration custom colours lookup --- .../Skinning/CatchLegacySkinTransformer.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Skinning/CatchLegacySkinTransformer.cs b/osu.Game.Rulesets.Catch/Skinning/CatchLegacySkinTransformer.cs index 65e6e6f209..4a87eb95e7 100644 --- a/osu.Game.Rulesets.Catch/Skinning/CatchLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Catch/Skinning/CatchLegacySkinTransformer.cs @@ -65,6 +65,15 @@ namespace osu.Game.Rulesets.Catch.Skinning public SampleChannel GetSample(ISampleInfo sample) => source.GetSample(sample); - public IBindable GetConfig(TLookup lookup) => source.GetConfig(lookup); + public IBindable GetConfig(TLookup lookup) + { + switch (lookup) + { + case CatchSkinColour colour: + return source.GetConfig(new SkinCustomColourLookup(colour)); + } + + return source.GetConfig(lookup); + } } } From b100230538707ffcbed2a70fc17b0981b618b3eb Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 5 Apr 2020 00:13:23 +0300 Subject: [PATCH 0072/1117] Test CatchSkinColour transformation on colour retrieval implicitly --- .../TestSceneHyperDashColouring.cs | 37 ++++--------------- 1 file changed, 8 insertions(+), 29 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs index 10739a3131..c8d28dbaeb 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs @@ -106,44 +106,23 @@ namespace osu.Game.Rulesets.Catch.Tests private bool checkLegacyFruitHyperDashColour(DrawableFruit fruit, Color4 expectedColour) => fruit.ChildrenOfType().First().Drawable.ChildrenOfType().Any(c => c.Colour == expectedColour); - private class TestSkin : ISkin + private class TestSkin : LegacySkin { public static Color4 CustomHyperDashColour { get; } = Color4.Goldenrod; public static Color4 CustomHyperDashFruitColour { get; } = Color4.Cyan; public static Color4 CustomHyperDashAfterColour { get; } = Color4.Lime; - private readonly bool customCatcherColour; - private readonly bool customAfterColour; - private readonly bool customFruitColour; - public TestSkin(bool customCatcherColour, bool customAfterColour, bool customFruitColour) + : base(new SkinInfo(), null, null, string.Empty) { - this.customCatcherColour = customCatcherColour; - this.customAfterColour = customAfterColour; - this.customFruitColour = customFruitColour; - } + if (customCatcherColour) + Configuration.CustomColours[CatchSkinColour.HyperDash.ToString()] = CustomHyperDashColour; - public Drawable GetDrawableComponent(ISkinComponent component) => null; + if (customAfterColour) + Configuration.CustomColours[CatchSkinColour.HyperDashAfterImage.ToString()] = CustomHyperDashAfterColour; - public Texture GetTexture(string componentName) => null; - - public SampleChannel GetSample(ISampleInfo sampleInfo) => null; - - public IBindable GetConfig(TLookup lookup) - { - if (lookup is CatchSkinColour config) - { - if (config == CatchSkinColour.HyperDash && customCatcherColour) - return SkinUtils.As(new Bindable(CustomHyperDashColour)); - - if (config == CatchSkinColour.HyperDashFruit && customFruitColour) - return SkinUtils.As(new Bindable(CustomHyperDashFruitColour)); - - if (config == CatchSkinColour.HyperDashAfterImage && customAfterColour) - return SkinUtils.As(new Bindable(CustomHyperDashAfterColour)); - } - - return null; + if (customFruitColour) + Configuration.CustomColours[CatchSkinColour.HyperDashFruit.ToString()] = CustomHyperDashFruitColour; } } } From dfd86e643bd415e5653a942e2432d56d224c6a1b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 5 Apr 2020 00:14:07 +0300 Subject: [PATCH 0073/1117] Add custom-coloured osu!catch skin configuration to 'Resources/special-skin' --- osu.Game.Rulesets.Catch.Tests/Resources/special-skin/skin.ini | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/special-skin/skin.ini diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/skin.ini b/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/skin.ini new file mode 100644 index 0000000000..36515f33c5 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/skin.ini @@ -0,0 +1,4 @@ +[CatchTheBeat] +HyperDash: 232,185,35 +HyperDashFruit: 0,255,255 +HyperDashAfterImage: 232,74,35 \ No newline at end of file From 42ccee5e6c8a2eedc459a369e129cccab853133d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 5 Apr 2020 00:15:42 +0300 Subject: [PATCH 0074/1117] Fix CI issue --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 0d5b454a9d..7971a17e68 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -42,7 +42,6 @@ namespace osu.Game.Rulesets.Catch.UI private Container hyperDashTrails; private Container endGlowSprites; - public CatcherAnimationState CurrentState { get; private set; } /// From f6bbec72bfd664f1a29f27de192496bba08df6ca Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 5 Apr 2020 00:20:21 +0300 Subject: [PATCH 0075/1117] Revert "Add custom-coloured osu!catch skin configuration to 'Resources/special-skin'" This reverts commit dfd86e643bd415e5653a942e2432d56d224c6a1b. --- osu.Game.Rulesets.Catch.Tests/Resources/special-skin/skin.ini | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/special-skin/skin.ini diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/skin.ini b/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/skin.ini deleted file mode 100644 index 36515f33c5..0000000000 --- a/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/skin.ini +++ /dev/null @@ -1,4 +0,0 @@ -[CatchTheBeat] -HyperDash: 232,185,35 -HyperDashFruit: 0,255,255 -HyperDashAfterImage: 232,74,35 \ No newline at end of file From b8327ed877b61ac867210f7b611240c28063fdc7 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 5 Apr 2020 00:30:10 +0300 Subject: [PATCH 0076/1117] Add test for osu!catch skin colour decoding Tests the skin configuration CatchTheBeat section's colours decoding part --- .../CatchSkinColourDecodingTest.cs | 36 +++++++++++++++++++ .../Resources/special-skin/skin.ini | 4 +++ .../Skinning/CatchLegacySkinTransformer.cs | 2 +- 3 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Rulesets.Catch.Tests/CatchSkinColourDecodingTest.cs create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/special-skin/skin.ini diff --git a/osu.Game.Rulesets.Catch.Tests/CatchSkinColourDecodingTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchSkinColourDecodingTest.cs new file mode 100644 index 0000000000..57228210d6 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/CatchSkinColourDecodingTest.cs @@ -0,0 +1,36 @@ +// 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.IO.Stores; +using osu.Game.Rulesets.Catch.Skinning; +using osu.Game.Skinning; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Catch.Tests +{ + [TestFixture] + public class CatchSkinColourDecodingTest + { + [Test] + public void TestCatchSkinColourDecoding() + { + var store = new NamespacedResourceStore(new DllResourceStore(GetType().Assembly), "Resources/special-skin"); + var rawSkin = new TestLegacySkin(new SkinInfo { Name = "special-skin" }, store); + var skin = new CatchLegacySkinTransformer(rawSkin); + + Assert.AreEqual(new Color4(232, 185, 35, 255), skin.GetHyperDashCatcherColour()?.Value); + Assert.AreEqual(new Color4(232, 74, 35, 255), skin.GetHyperDashCatcherAfterImageColour()?.Value); + Assert.AreEqual(new Color4(0, 255, 255, 255), skin.GetHyperDashFruitColour()?.Value); + } + + private class TestLegacySkin : LegacySkin + { + public TestLegacySkin(SkinInfo skin, IResourceStore storage) + // Bypass LegacySkinResourceStore to avoid returning null for retrieving files due to bad skin info (SkinInfo.Files = null). + : base(skin, storage, null, "skin.ini") + { + } + } + } +} diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/skin.ini b/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/skin.ini new file mode 100644 index 0000000000..96d50f1451 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/skin.ini @@ -0,0 +1,4 @@ +[CatchTheBeat] +HyperDash: 232,185,35 +HyperDashFruit: 0,255,255 +HyperDashAfterImage: 232,74,35 diff --git a/osu.Game.Rulesets.Catch/Skinning/CatchLegacySkinTransformer.cs b/osu.Game.Rulesets.Catch/Skinning/CatchLegacySkinTransformer.cs index 65e6e6f209..ba939157ea 100644 --- a/osu.Game.Rulesets.Catch/Skinning/CatchLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Catch/Skinning/CatchLegacySkinTransformer.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Catch.Skinning { private readonly ISkin source; - public CatchLegacySkinTransformer(ISkinSource source) + public CatchLegacySkinTransformer(ISkin source) { this.source = source; } From 42ac0c72eac12ea39415f9bc9926adcc5a1a6ff6 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 5 Apr 2020 00:46:52 +0300 Subject: [PATCH 0077/1117] Fix grammer issue and more rewording MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game.Rulesets.Catch/Skinning/CatchSkinColour.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Skinning/CatchSkinColour.cs b/osu.Game.Rulesets.Catch/Skinning/CatchSkinColour.cs index 2ad8f89739..4506111498 100644 --- a/osu.Game.Rulesets.Catch/Skinning/CatchSkinColour.cs +++ b/osu.Game.Rulesets.Catch/Skinning/CatchSkinColour.cs @@ -6,12 +6,12 @@ namespace osu.Game.Rulesets.Catch.Skinning public enum CatchSkinColour { /// - /// The colour to be used for the catcher while on hyper-dashing state. + /// The colour to be used for the catcher while in hyper-dashing state. /// HyperDash, /// - /// The colour to be used for hyper-dash fruits. + /// The colour to be used for fruits that grant the catcher the ability to hyper-dash. /// HyperDashFruit, From 2fec8b7b8555101200250846e9ea0958f77b6b1c Mon Sep 17 00:00:00 2001 From: Shane Woolcock Date: Sun, 5 Apr 2020 13:01:10 +0930 Subject: [PATCH 0078/1117] Use DisplayModes rather than AvailableResolutions --- .../Settings/Sections/Graphics/LayoutSettings.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index b73c8f7622..00b7643332 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -209,15 +209,16 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics private IReadOnlyList getResolutions() { var resolutions = new List { new Size(9999, 9999) }; + var currentDisplay = game.Window?.CurrentDisplay.Value; - if (game.Window != null) + if (currentDisplay != null) { - resolutions.AddRange(game.Window.AvailableResolutions - .Where(r => r.Width >= 800 && r.Height >= 600) - .OrderByDescending(r => r.Width) - .ThenByDescending(r => r.Height) - .Select(res => new Size(res.Width, res.Height)) - .Distinct()); + resolutions.AddRange(currentDisplay.DisplayModes + .Where(m => m.Size.Width >= 800 && m.Size.Height >= 600) + .OrderByDescending(m => m.Size.Width) + .ThenByDescending(m => m.Size.Height) + .Select(m => m.Size) + .Distinct()); } return resolutions; From e71a9668a537b2616b71b8f192c37671e2447553 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 5 Apr 2020 21:32:55 +0300 Subject: [PATCH 0079/1117] Disallow draining in non-draining sections --- .../Scoring/DrainingHealthProcessor.cs | 57 ++++++++++++++++--- 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs b/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs index fffcbb3c9f..b36e42326c 100644 --- a/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs +++ b/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs @@ -3,7 +3,9 @@ using System; using System.Collections.Generic; +using System.Linq; using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; @@ -47,6 +49,34 @@ namespace osu.Game.Rulesets.Scoring private double targetMinimumHealth; private double drainRate = 1; + private readonly List<(double startTime, double endTime)> nonDrainSections = new List<(double, double)>(); + private int currentNonDrainSection; + + private bool isInNonDrainSection + { + get + { + if (nonDrainSections.Count == 0) + return false; + + var time = Time.Current; + + if (time > nonDrainSections[currentNonDrainSection].endTime) + { + while (time > nonDrainSections[currentNonDrainSection].endTime && currentNonDrainSection < nonDrainSections.Count - 1) + currentNonDrainSection++; + } + else + { + while (time < nonDrainSections[currentNonDrainSection].startTime && currentNonDrainSection > 0) + currentNonDrainSection--; + } + + var closestSection = nonDrainSections[currentNonDrainSection]; + return time >= closestSection.startTime && time <= closestSection.endTime; + } + } + /// /// Creates a new . /// @@ -60,23 +90,36 @@ namespace osu.Game.Rulesets.Scoring { base.Update(); - if (!IsBreakTime.Value) - { - // 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); + if (isInNonDrainSection) + return; - Health.Value -= drainRate * (currentGameplayTime - lastGameplayTime); - } + // 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) { + nonDrainSections.Clear(); + this.beatmap = beatmap; if (beatmap.HitObjects.Count > 0) gameplayEndTime = beatmap.HitObjects[^1].GetEndTime(); + // Ranges between the end of last hit object before a break + // and the start of first hit object after a break should + // not allow HP draining. (with break periods in) + foreach (BreakPeriod b in beatmap.Breaks) + { + var startTime = beatmap.HitObjects.LastOrDefault(h => h.GetEndTime() < b.StartTime)?.GetEndTime() ?? double.MinValue; + var endTime = beatmap.HitObjects.FirstOrDefault(h => h.StartTime > b.EndTime)?.StartTime ?? double.MaxValue; + + nonDrainSections.Add((startTime, endTime)); + } + targetMinimumHealth = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, min_health_target, mid_health_target, max_health_target); base.ApplyBeatmap(beatmap); From 7fab07670ed8ff6725bc4b8c1c543e0101af9664 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 5 Apr 2020 21:35:09 +0300 Subject: [PATCH 0080/1117] Remove no longer necessary usage of IsBreakTime --- osu.Game/Rulesets/Scoring/HealthProcessor.cs | 5 ----- osu.Game/Screens/Play/Player.cs | 2 -- 2 files changed, 7 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/HealthProcessor.cs b/osu.Game/Rulesets/Scoring/HealthProcessor.cs index 45edc0f4a3..1535fe4d00 100644 --- a/osu.Game/Rulesets/Scoring/HealthProcessor.cs +++ b/osu.Game/Rulesets/Scoring/HealthProcessor.cs @@ -26,11 +26,6 @@ namespace osu.Game.Rulesets.Scoring /// public readonly BindableDouble Health = new BindableDouble(1) { MinValue = 0, MaxValue = 1 }; - /// - /// Whether gameplay is currently in a break. - /// - public readonly IBindable IsBreakTime = new Bindable(); - /// /// Whether this ScoreProcessor has already triggered the failed state. /// diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 4597ae760c..f2051790db 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -243,8 +243,6 @@ namespace osu.Game.Screens.Play Breaks = working.Beatmap.Breaks } }); - - HealthProcessor.IsBreakTime.BindTo(breakTracker.IsBreakTime); } private void addOverlayComponents(Container target, WorkingBeatmap working) From c902ba40860b2757ea960925322bf9731b4eeefc Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 5 Apr 2020 21:46:07 +0300 Subject: [PATCH 0081/1117] Add test cases for HP draining not applied before a break and after it --- .../TestSceneDrainingHealthProcessor.cs | 87 ++++++++++++++----- 1 file changed, 66 insertions(+), 21 deletions(-) diff --git a/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs b/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs index 885abb61b5..2f83ea4832 100644 --- a/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs +++ b/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs @@ -1,13 +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 System.Linq; using NUnit.Framework; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Utils; using osu.Framework.Testing; using osu.Framework.Timing; using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; @@ -18,7 +20,6 @@ namespace osu.Game.Tests.Gameplay [HeadlessTest] public class TestSceneDrainingHealthProcessor : OsuTestScene { - private Bindable breakTime; private HealthProcessor processor; private ManualClock clock; @@ -41,6 +42,55 @@ namespace osu.Game.Tests.Gameplay assertHealthEqualTo(1); } + [Test] + public void TestHealthNotDrainedBeforeBreak() + { + createProcessor(createBeatmap(0, 2000, + new BreakPeriod(400, 600), new BreakPeriod(1200, 1400))); + + setTime(300); + setHealth(1); + + setTime(400); + assertHealthEqualTo(1); + + setTime(1100); + setHealth(1); + + setTime(1200); + assertHealthEqualTo(1); + } + + [Test] + public void TestHealthNotDrainedDuringBreak() + { + createProcessor(createBeatmap(0, 2000, new BreakPeriod(0, 1200))); + + setTime(700); + assertHealthEqualTo(1); + setTime(900); + assertHealthEqualTo(1); + } + + [Test] + public void TestHealthNotDrainedAfterBreak() + { + createProcessor(createBeatmap(0, 2000, + new BreakPeriod(400, 600), new BreakPeriod(1200, 1400))); + + setTime(600); + setHealth(1); + + setTime(700); + assertHealthEqualTo(1); + + setTime(1400); + setHealth(1); + + setTime(1500); + assertHealthEqualTo(1); + } + [Test] public void TestHealthNotDrainedAfterGameplayEnd() { @@ -54,18 +104,6 @@ namespace osu.Game.Tests.Gameplay assertHealthEqualTo(1); } - [Test] - public void TestHealthNotDrainedDuringBreak() - { - createProcessor(createBeatmap(0, 2000)); - setBreak(true); - - setTime(700); - assertHealthEqualTo(1); - setTime(900); - assertHealthEqualTo(1); - } - [Test] public void TestHealthDrainedDuringGameplay() { @@ -112,30 +150,39 @@ namespace osu.Game.Tests.Gameplay assertHealthNotEqualTo(1); } - private Beatmap createBeatmap(double startTime, double endTime) + private Beatmap createBeatmap(double startTime, double endTime, params BreakPeriod[] breaks) { var beatmap = new Beatmap { BeatmapInfo = { BaseDifficulty = { DrainRate = 5 } }, }; - for (double time = startTime; time <= endTime; time += 100) + double time = startTime; + + while (time <= endTime) + { beatmap.HitObjects.Add(new JudgeableHitObject { StartTime = time }); + // leave a 100ms gap between the start and end of a break period. + time += (getCurrentBreak(breaks, time)?.Duration ?? 0) + 100; + } + + beatmap.Breaks.AddRange(breaks); + + static BreakPeriod getCurrentBreak(IEnumerable breaks, double time) => + breaks?.FirstOrDefault(b => time >= b.StartTime && time <= b.EndTime); + return beatmap; } private void createProcessor(Beatmap beatmap) => AddStep("create processor", () => { - breakTime = new Bindable(); - Child = processor = new DrainingHealthProcessor(beatmap.HitObjects[0].StartTime).With(d => { d.RelativeSizeAxes = Axes.Both; d.Clock = new FramedClock(clock = new ManualClock()); }); - processor.IsBreakTime.BindTo(breakTime); processor.ApplyBeatmap(beatmap); }); @@ -143,8 +190,6 @@ namespace osu.Game.Tests.Gameplay private void setHealth(double health) => AddStep($"set health = {health}", () => processor.Health.Value = health); - private void setBreak(bool enabled) => AddStep($"{(enabled ? "enable" : "disable")} break", () => breakTime.Value = enabled); - private void assertHealthEqualTo(double value) => AddAssert($"health = {value}", () => Precision.AlmostEquals(value, processor.Health.Value, 0.0001f)); From 1b76a53d329acbe4c3dc73800e867b69277e5a0a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 5 Apr 2020 22:10:35 +0300 Subject: [PATCH 0082/1117] Move CatchTheBeat section handling to LegacySkinDecoder Best place to reside at --- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 3 --- osu.Game/Skinning/LegacySkinDecoder.cs | 6 ++++++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index 743a470e6e..113526f9dd 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -73,9 +73,6 @@ namespace osu.Game.Beatmaps.Formats switch (section) { case Section.Colours: - // osu!catch section only has colour settings - // so no harm in handling the entire section - case Section.CatchTheBeat: HandleColours(output, line); return; } diff --git a/osu.Game/Skinning/LegacySkinDecoder.cs b/osu.Game/Skinning/LegacySkinDecoder.cs index 88ba7b23b7..b5734edacf 100644 --- a/osu.Game/Skinning/LegacySkinDecoder.cs +++ b/osu.Game/Skinning/LegacySkinDecoder.cs @@ -44,6 +44,12 @@ namespace osu.Game.Skinning } break; + + // osu!catch section only has colour settings + // so no harm in handling the entire section + case Section.CatchTheBeat: + HandleColours(skin, line); + return; } if (!string.IsNullOrEmpty(pair.Key)) From 7f3ad6d5be7b79358f6c1d9ed2c44c3e60a30dda Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 5 Apr 2020 22:15:11 +0300 Subject: [PATCH 0083/1117] Move default colour fallback to the extension methods itself --- osu.Game.Rulesets.Catch/Skinning/CatchSkinExtensions.cs | 6 +++++- osu.Game.Rulesets.Catch/Skinning/LegacyFruitPiece.cs | 3 +-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Skinning/CatchSkinExtensions.cs b/osu.Game.Rulesets.Catch/Skinning/CatchSkinExtensions.cs index 8fc0831918..623f87bf11 100644 --- a/osu.Game.Rulesets.Catch/Skinning/CatchSkinExtensions.cs +++ b/osu.Game.Rulesets.Catch/Skinning/CatchSkinExtensions.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 JetBrains.Annotations; using osu.Framework.Bindables; +using osu.Game.Rulesets.Catch.UI; using osu.Game.Skinning; using osuTK.Graphics; @@ -9,8 +11,10 @@ namespace osu.Game.Rulesets.Catch.Skinning { internal static class CatchSkinExtensions { + [NotNull] public static IBindable GetHyperDashFruitColour(this ISkin skin) => skin.GetConfig(CatchSkinColour.HyperDashFruit) ?? - skin.GetConfig(CatchSkinColour.HyperDash); + skin.GetConfig(CatchSkinColour.HyperDash) ?? + new Bindable(Catcher.DefaultHyperDashColour); } } diff --git a/osu.Game.Rulesets.Catch/Skinning/LegacyFruitPiece.cs b/osu.Game.Rulesets.Catch/Skinning/LegacyFruitPiece.cs index d8489399d2..470c12559e 100644 --- a/osu.Game.Rulesets.Catch/Skinning/LegacyFruitPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/LegacyFruitPiece.cs @@ -7,7 +7,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Game.Rulesets.Catch.Objects.Drawables; -using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Skinning; using osuTK; @@ -57,7 +56,7 @@ namespace osu.Game.Rulesets.Catch.Skinning var hyperDash = new Sprite { Texture = skin.GetTexture(lookupName), - Colour = skin.GetHyperDashFruitColour()?.Value ?? Catcher.DefaultHyperDashColour, + Colour = skin.GetHyperDashFruitColour().Value, Anchor = Anchor.Centre, Origin = Anchor.Centre, Blending = BlendingParameters.Additive, From ccc764eace14444e4421d83eb4ee1c842f46f2f1 Mon Sep 17 00:00:00 2001 From: Tim Oliver Date: Tue, 7 Apr 2020 16:39:41 +0800 Subject: [PATCH 0084/1117] =?UTF-8?q?Added=20=E2=80=9Cinstant=20fly?= =?UTF-8?q?=E2=80=9D=20variant=20of=20hit=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Objects/Drawables/DrawableCentreHit.cs | 17 +++++++++++++++++ .../Objects/Drawables/DrawableRimHit.cs | 17 +++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs index 4979135f50..08df05e719 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs @@ -2,7 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; +using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; namespace osu.Game.Rulesets.Taiko.Objects.Drawables @@ -23,4 +26,18 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables MainPiece.AccentColour = colours.PinkDarker; } } + + public class DrawableFlyingCentreHit : DrawableCentreHit + { + protected override void CheckForResult(bool userTriggered, double timeOffset) + { + ApplyResult(r => r.Type = HitResult.Good); + } + + public DrawableFlyingCentreHit(double time) + : base(new Hit { StartTime = time }) + { + HitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); + } + } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs index 5a12d71cea..0c2c9fbdef 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs @@ -2,7 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; +using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; namespace osu.Game.Rulesets.Taiko.Objects.Drawables @@ -23,4 +26,18 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables MainPiece.AccentColour = colours.BlueDarker; } } + + public class DrawableFlyingRimHit : DrawableRimHit + { + protected override void CheckForResult(bool userTriggered, double timeOffset) + { + ApplyResult(r => r.Type = HitResult.Good); + } + + public DrawableFlyingRimHit(double time) + : base(new Hit { StartTime = time }) + { + HitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); + } + } } From 2705de70a206ad71d8db84d70c4af6cbd259b4ca Mon Sep 17 00:00:00 2001 From: Tim Oliver Date: Tue, 7 Apr 2020 16:40:01 +0800 Subject: [PATCH 0085/1117] Added arbitrary hit handler to drum roll object --- .../Objects/Drawables/DrawableDrumRoll.cs | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs index 5806c90115..3e7b6dfd31 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs @@ -34,6 +34,14 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables private Color4 colourIdle; private Color4 colourEngaged; + private bool judgingStarted; + + /// + /// A handler action for when the drumroll has been hit, + /// regardless of any judgement. + /// + public Action OnHit; + public DrawableDrumRoll(DrumRoll drumRoll) : base(drumRoll) { @@ -86,15 +94,27 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables protected override TaikoPiece CreateMainPiece() => new ElongatedCirclePiece(); - public override bool OnPressed(TaikoAction action) => false; + public override bool OnPressed(TaikoAction action) + { + if (judgingStarted) + OnHit.Invoke(action); + + return false; + } private void onNewResult(DrawableHitObject obj, JudgementResult result) { if (!(obj is DrawableDrumRollTick)) return; + DrawableDrumRollTick drumRollTick = (DrawableDrumRollTick)obj; + if (result.Type > HitResult.Miss) + { + OnHit.Invoke(drumRollTick.JudgedAction); + judgingStarted = true; rollingHits++; + } else rollingHits--; @@ -113,8 +133,11 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables return; int countHit = NestedHitObjects.Count(o => o.IsHit); + if (countHit >= HitObject.RequiredGoodHits) + { ApplyResult(r => r.Type = countHit >= HitObject.RequiredGreatHits ? HitResult.Great : HitResult.Good); + } else ApplyResult(r => r.Type = HitResult.Miss); } From 7c3c198212d70205ee4897eafc7cff815e55c929 Mon Sep 17 00:00:00 2001 From: Tim Oliver Date: Tue, 7 Apr 2020 16:40:18 +0800 Subject: [PATCH 0086/1117] Added judgement forwarder to drumroll tick object --- .../Objects/Drawables/DrawableDrumRollTick.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs index 25b6141a0e..9961cb6ea2 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs @@ -11,6 +11,12 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { public class DrawableDrumRollTick : DrawableTaikoHitObject { + /// + /// The action type that the user took which caused this tick to + /// have been judged as "hit" + /// + public TaikoAction JudgedAction; + public DrawableDrumRollTick(DrumRollTick tick) : base(tick) { @@ -49,7 +55,11 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables } } - public override bool OnPressed(TaikoAction action) => UpdateResult(true); + public override bool OnPressed(TaikoAction action) + { + JudgedAction = action; + return UpdateResult(true); + } protected override DrawableStrongNestedHit CreateStrongHit(StrongHitObject hitObject) => new StrongNestedHit(hitObject, this); From 3fec213c928db64bb7dcb07bf6b468fb9d9e39c8 Mon Sep 17 00:00:00 2001 From: Tim Oliver Date: Tue, 7 Apr 2020 16:40:32 +0800 Subject: [PATCH 0087/1117] Added separate scrolling track to display drum roll notes --- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 25 ++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index bde9085c23..b32e7b53da 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -42,6 +42,7 @@ namespace osu.Game.Rulesets.Taiko.UI private readonly Container hitExplosionContainer; private readonly Container kiaiExplosionContainer; private readonly JudgementContainer judgementContainer; + private readonly ScrollingHitObjectContainer drumRollHitContainer; internal readonly HitTarget HitTarget; private readonly ProxyContainer topLevelHitContainer; @@ -135,6 +136,14 @@ namespace osu.Game.Rulesets.Taiko.UI Margin = new MarginPadding { Left = HIT_TARGET_OFFSET }, Blending = BlendingParameters.Additive }, + drumRollHitContainer = new ScrollingHitObjectContainer + { + Name = "Drumroll hit", + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Stretch, + Margin = new MarginPadding { Left = HIT_TARGET_OFFSET }, + Width = 1.0f + } } }, overlayBackgroundContainer = new Container @@ -212,12 +221,28 @@ namespace osu.Game.Rulesets.Taiko.UI barlineContainer.Add(barline.CreateProxy()); break; + case DrawableDrumRoll drumRoll: + drumRoll.OnHit += onDrumrollArbitraryHit; + break; + case DrawableTaikoHitObject taikoObject: topLevelHitContainer.Add(taikoObject.CreateProxiedContent()); break; } } + private void onDrumrollArbitraryHit(TaikoAction action) + { + DrawableHit drawableHit; + + if (action == TaikoAction.LeftRim || action == TaikoAction.RightRim) + drawableHit = new DrawableFlyingRimHit(Time.Current); + else + drawableHit = new DrawableFlyingCentreHit(Time.Current); + + drumRollHitContainer.Add(drawableHit); + } + internal void OnNewResult(DrawableHitObject judgedObject, JudgementResult result) { if (!DisplayJudgements.Value) From a1e215888eda520a432e63465e5a6c4c33bc111c Mon Sep 17 00:00:00 2001 From: Tim Oliver Date: Tue, 7 Apr 2020 17:25:47 +0800 Subject: [PATCH 0088/1117] Added logic to allow strong notes --- .../Objects/Drawables/DrawableCentreHit.cs | 4 ++-- .../Objects/Drawables/DrawableDrumRoll.cs | 6 +++--- osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs | 4 ++-- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 6 +++--- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs index 08df05e719..86e885239f 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs @@ -34,8 +34,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables ApplyResult(r => r.Type = HitResult.Good); } - public DrawableFlyingCentreHit(double time) - : base(new Hit { StartTime = time }) + public DrawableFlyingCentreHit(double time, bool isStrong = false) + : base(new Hit { StartTime = time, IsStrong = isStrong }) { HitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs index 3e7b6dfd31..64be870262 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs @@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables /// A handler action for when the drumroll has been hit, /// regardless of any judgement. /// - public Action OnHit; + public Action OnHit; public DrawableDrumRoll(DrumRoll drumRoll) : base(drumRoll) @@ -97,7 +97,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables public override bool OnPressed(TaikoAction action) { if (judgingStarted) - OnHit.Invoke(action); + OnHit.Invoke(action, HitObject.IsStrong); return false; } @@ -111,7 +111,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables if (result.Type > HitResult.Miss) { - OnHit.Invoke(drumRollTick.JudgedAction); + OnHit.Invoke(drumRollTick.JudgedAction, HitObject.IsStrong); judgingStarted = true; rollingHits++; } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs index 0c2c9fbdef..ad9872b21f 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs @@ -34,8 +34,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables ApplyResult(r => r.Type = HitResult.Good); } - public DrawableFlyingRimHit(double time) - : base(new Hit { StartTime = time }) + public DrawableFlyingRimHit(double time, bool isStrong = false) + : base(new Hit { StartTime = time, IsStrong = isStrong }) { HitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); } diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index b32e7b53da..59cf9193b5 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -231,14 +231,14 @@ namespace osu.Game.Rulesets.Taiko.UI } } - private void onDrumrollArbitraryHit(TaikoAction action) + private void onDrumrollArbitraryHit(TaikoAction action, bool isStrong) { DrawableHit drawableHit; if (action == TaikoAction.LeftRim || action == TaikoAction.RightRim) - drawableHit = new DrawableFlyingRimHit(Time.Current); + drawableHit = new DrawableFlyingRimHit(Time.Current, isStrong); else - drawableHit = new DrawableFlyingCentreHit(Time.Current); + drawableHit = new DrawableFlyingCentreHit(Time.Current, isStrong); drumRollHitContainer.Add(drawableHit); } From 08308e07e7a1ea594214594400f3cb784936a9a9 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Tue, 7 Apr 2020 12:20:54 +0200 Subject: [PATCH 0089/1117] Apply review suggestions --- osu.Game/Rulesets/RulesetStore.cs | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index 7b4c0302aa..ef72f187c3 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -24,12 +24,12 @@ namespace osu.Game.Rulesets : base(factory) { rulesetStorage = storage?.GetStorageForDirectory("rulesets"); - AppDomain.CurrentDomain.AssemblyResolve += resolveRulesetDependencyAssembly; // On android in release configuration assemblies are loaded from the apk directly into memory. // We cannot read assemblies from cwd, so should check loaded assemblies instead. loadFromAppDomain(); loadFromDisk(); + AppDomain.CurrentDomain.AssemblyResolve += resolveRulesetDependencyAssembly; loadUserRulesets(); addMissingRulesets(); } @@ -57,6 +57,7 @@ namespace osu.Game.Rulesets { var asm = new AssemblyName(args.Name); + // the requesting assembly may be located out of the executable's base directory, thus requiring manual resolving of its dependencies. // this assumes the only explicit dependency of the ruleset is the game core assembly. // the ruleset dependency on the game core assembly requires manual resolving, transient dependencies should be resolved automatically if (asm.Name.Equals(typeof(OsuGame).Assembly.GetName().Name, StringComparison.Ordinal)) @@ -137,17 +138,10 @@ namespace osu.Game.Rulesets private void loadUserRulesets() { - try - { - var rulesets = rulesetStorage?.GetFiles(".", $"{ruleset_library_prefix}.*.dll"); + var rulesets = rulesetStorage?.GetFiles(".", $"{ruleset_library_prefix}.*.dll"); - foreach (var ruleset in rulesets.Where(f => !f.Contains("Tests"))) - loadRulesetFromFile(rulesetStorage?.GetFullPath(ruleset)); - } - catch (Exception e) - { - Logger.Error(e, "Couldn't load user rulesets"); - } + foreach (var ruleset in rulesets.Where(f => !f.Contains("Tests"))) + loadRulesetFromFile(rulesetStorage?.GetFullPath(ruleset)); } private void loadFromDisk() From 2087d8d09e92c4662a9cc228e25476801624539a Mon Sep 17 00:00:00 2001 From: Lucas A Date: Tue, 7 Apr 2020 16:01:47 +0200 Subject: [PATCH 0090/1117] Don't search for user rulesets if rulesetsStorage isn't set (Testing environnment) --- osu.Game/Rulesets/RulesetStore.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index ef72f187c3..34da2dc2db 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -138,10 +138,12 @@ namespace osu.Game.Rulesets private void loadUserRulesets() { - var rulesets = rulesetStorage?.GetFiles(".", $"{ruleset_library_prefix}.*.dll"); + if (rulesetStorage == null) return; + + var rulesets = rulesetStorage.GetFiles(".", $"{ruleset_library_prefix}.*.dll"); foreach (var ruleset in rulesets.Where(f => !f.Contains("Tests"))) - loadRulesetFromFile(rulesetStorage?.GetFullPath(ruleset)); + loadRulesetFromFile(rulesetStorage.GetFullPath(ruleset)); } private void loadFromDisk() From 66a474619ce2f56d127d92c2c0ca2429f845ab10 Mon Sep 17 00:00:00 2001 From: Alchyr Date: Tue, 7 Apr 2020 18:13:26 -0700 Subject: [PATCH 0091/1117] Adjust TimingControlPoint equivalency --- osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs index 51b3377394..158788964b 100644 --- a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs @@ -50,6 +50,6 @@ namespace osu.Game.Beatmaps.ControlPoints public override bool EquivalentTo(ControlPoint other) => other is TimingControlPoint otherTyped - && TimeSignature == otherTyped.TimeSignature && BeatLength.Equals(otherTyped.BeatLength); + && Time == otherTyped.Time && TimeSignature == otherTyped.TimeSignature && BeatLength.Equals(otherTyped.BeatLength); } } From c5aae9b757ef7c726b513a7b0dfd2e1b55d2cda2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 8 Apr 2020 12:19:09 +0900 Subject: [PATCH 0092/1117] Fix post-merge errors --- .../Difficulty/CatchDifficultyCalculator.cs | 14 +++++--------- .../Preprocessing/CatchDifficultyHitObject.cs | 1 + 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 7d763f1792..ee3b410780 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -50,15 +50,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) { - float halfCatchWidth; - - using (var catcher = new CatcherArea.Catcher(beatmap.BeatmapInfo.BaseDifficulty)) - { - halfCatchWidth = catcher.CatchWidth * 0.5f; - // We're only using 80% of the catcher's width to simulate imperfect gameplay, reduced further at circle sizes above 5.5 - halfCatchWidth *= Math.Min(1.075f - (0.05f * beatmap.BeatmapInfo.BaseDifficulty.CircleSize), 0.8f); - } - CatchHitObject lastObject = null; // In 2B beatmaps, it is possible that a normal Fruit is placed in the middle of a JuiceStream. @@ -81,8 +72,13 @@ namespace osu.Game.Rulesets.Catch.Difficulty protected override Skill[] CreateSkills(IBeatmap beatmap) { using (var catcher = new Catcher(beatmap.BeatmapInfo.BaseDifficulty)) + { halfCatcherWidth = catcher.CatchWidth * 0.5f; + // For circle sizes above 5.5, reduce the catcher width further to simulate imperfect gameplay. + halfCatcherWidth *= 1 - (Math.Max(0, beatmap.BeatmapInfo.BaseDifficulty.CircleSize - 5.5f) * 0.0625f); + } + return new Skill[] { new Movement(halfCatcherWidth), diff --git a/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs b/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs index b2b4129c8a..360af1a8c9 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs @@ -24,6 +24,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing /// Milliseconds elapsed since the start time of the previous , with a minimum of 40ms. /// public readonly double StrainTime; + public readonly double ClockRate; public CatchDifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate, float halfCatcherWidth) From fd51bbb9ecd1e7e381c481d50c85ba392a63bc75 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 8 Apr 2020 12:20:46 +0900 Subject: [PATCH 0093/1117] Apply latest changes --- .../Difficulty/Skills/Movement.cs | 25 ++++--------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs index 3ab4ae63a1..5cd2f1f581 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs @@ -46,29 +46,29 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills float distanceMoved = playerPosition - lastPlayerPosition.Value; - double weightedStrainTime = catchCurrent.StrainTime + 10 + (8 / catchCurrent.ClockRate); + double weightedStrainTime = catchCurrent.StrainTime + 13 + (3 / catchCurrent.ClockRate); double distanceAddition = (Math.Pow(Math.Abs(distanceMoved), 1.3) / 510); double sqrtStrain = Math.Sqrt(weightedStrainTime); double edgeDashBonus = 0; - // Direction changes give an extra point! + // Direction change bonus. if (Math.Abs(distanceMoved) > 0.1) { if (Math.Abs(lastDistanceMoved) > 0.1 && Math.Sign(distanceMoved) != Math.Sign(lastDistanceMoved)) { double bonusFactor = Math.Min(50, Math.Abs(distanceMoved)) / 50; - double antiflowFactor = Math.Max(Math.Min(70, Math.Abs(lastDistanceMoved)) / 70, 0.3); + double antiflowFactor = Math.Max(Math.Min(70, Math.Abs(lastDistanceMoved)) / 70, 0.38); - distanceAddition += direction_change_bonus / Math.Sqrt(lastStrainTime + 18) * bonusFactor * antiflowFactor * Math.Max(1 - Math.Pow(weightedStrainTime / 1000, 3), 0); + distanceAddition += direction_change_bonus / Math.Sqrt(lastStrainTime + 16) * bonusFactor * antiflowFactor * Math.Max(1 - Math.Pow(weightedStrainTime / 1000, 3), 0); } // Base bonus for every movement, giving some weight to streams. distanceAddition += 12.5 * Math.Min(Math.Abs(distanceMoved), normalized_hitobject_radius * 2) / (normalized_hitobject_radius * 6) / sqrtStrain; } - // Bonus for "almost" hyperdashes at corner points + // Bonus for edge dashes. if (catchCurrent.LastObject.DistanceToHyperDash <= 20.0f / CatchPlayfield.BASE_WIDTH) { if (!catchCurrent.LastObject.HyperDash) @@ -82,21 +82,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills distanceAddition *= 1.0 + edgeDashBonus * ((20 - catchCurrent.LastObject.DistanceToHyperDash * CatchPlayfield.BASE_WIDTH) / 20) * Math.Pow((Math.Min(catchCurrent.StrainTime * catchCurrent.ClockRate, 265) / 265), 1.5); // Edge Dashes are easier at lower ms values } - // Prevent wide dense stacks of notes which fit on the catcher from greatly increasing SR - if (Math.Abs(distanceMoved) > 0.1) - { - if (Math.Abs(lastDistanceMoved) > 0.1 && Math.Sign(distanceMoved) != Math.Sign(lastDistanceMoved)) - { - if (Math.Abs(distanceMoved) <= (CatcherArea.CATCHER_SIZE) && Math.Abs(lastDistanceMoved) == Math.Abs(distanceMoved)) - { - if (catchCurrent.StrainTime <= 80 && lastStrainTime == catchCurrent.StrainTime) - { - distanceAddition *= Math.Max(((catchCurrent.StrainTime / 80) - 0.75) * 4, 0); - } - } - } - } - lastPlayerPosition = playerPosition; lastDistanceMoved = distanceMoved; lastStrainTime = catchCurrent.StrainTime; From a7d1eed3f5d938608eb9581ccde72072efd014b9 Mon Sep 17 00:00:00 2001 From: Tim Oliver Date: Wed, 8 Apr 2020 12:12:59 +0800 Subject: [PATCH 0094/1117] Added content proxying to drull roll elements --- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 59cf9193b5..e947795fe5 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -241,6 +241,7 @@ namespace osu.Game.Rulesets.Taiko.UI drawableHit = new DrawableFlyingCentreHit(Time.Current, isStrong); drumRollHitContainer.Add(drawableHit); + topLevelHitContainer.Add(drawableHit.CreateProxiedContent()); } internal void OnNewResult(DrawableHitObject judgedObject, JudgementResult result) From 6e12f1b69b95e0d9b0fbe84b4666b6a87f8b5caa Mon Sep 17 00:00:00 2001 From: Tim Oliver Date: Tue, 7 Apr 2020 16:39:41 +0800 Subject: [PATCH 0095/1117] =?UTF-8?q?Added=20=E2=80=9Cinstant=20fly?= =?UTF-8?q?=E2=80=9D=20variant=20of=20hit=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Objects/Drawables/DrawableCentreHit.cs | 17 +++++++++++++++++ .../Objects/Drawables/DrawableRimHit.cs | 17 +++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs index 4979135f50..08df05e719 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs @@ -2,7 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; +using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; namespace osu.Game.Rulesets.Taiko.Objects.Drawables @@ -23,4 +26,18 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables MainPiece.AccentColour = colours.PinkDarker; } } + + public class DrawableFlyingCentreHit : DrawableCentreHit + { + protected override void CheckForResult(bool userTriggered, double timeOffset) + { + ApplyResult(r => r.Type = HitResult.Good); + } + + public DrawableFlyingCentreHit(double time) + : base(new Hit { StartTime = time }) + { + HitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); + } + } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs index 5a12d71cea..0c2c9fbdef 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs @@ -2,7 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; +using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; namespace osu.Game.Rulesets.Taiko.Objects.Drawables @@ -23,4 +26,18 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables MainPiece.AccentColour = colours.BlueDarker; } } + + public class DrawableFlyingRimHit : DrawableRimHit + { + protected override void CheckForResult(bool userTriggered, double timeOffset) + { + ApplyResult(r => r.Type = HitResult.Good); + } + + public DrawableFlyingRimHit(double time) + : base(new Hit { StartTime = time }) + { + HitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); + } + } } From 1057981c793dfabd262123cacfb47f36df57a844 Mon Sep 17 00:00:00 2001 From: Tim Oliver Date: Tue, 7 Apr 2020 16:40:01 +0800 Subject: [PATCH 0096/1117] Added arbitrary hit handler to drum roll object --- .../Objects/Drawables/DrawableDrumRoll.cs | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs index 5806c90115..3e7b6dfd31 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs @@ -34,6 +34,14 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables private Color4 colourIdle; private Color4 colourEngaged; + private bool judgingStarted; + + /// + /// A handler action for when the drumroll has been hit, + /// regardless of any judgement. + /// + public Action OnHit; + public DrawableDrumRoll(DrumRoll drumRoll) : base(drumRoll) { @@ -86,15 +94,27 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables protected override TaikoPiece CreateMainPiece() => new ElongatedCirclePiece(); - public override bool OnPressed(TaikoAction action) => false; + public override bool OnPressed(TaikoAction action) + { + if (judgingStarted) + OnHit.Invoke(action); + + return false; + } private void onNewResult(DrawableHitObject obj, JudgementResult result) { if (!(obj is DrawableDrumRollTick)) return; + DrawableDrumRollTick drumRollTick = (DrawableDrumRollTick)obj; + if (result.Type > HitResult.Miss) + { + OnHit.Invoke(drumRollTick.JudgedAction); + judgingStarted = true; rollingHits++; + } else rollingHits--; @@ -113,8 +133,11 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables return; int countHit = NestedHitObjects.Count(o => o.IsHit); + if (countHit >= HitObject.RequiredGoodHits) + { ApplyResult(r => r.Type = countHit >= HitObject.RequiredGreatHits ? HitResult.Great : HitResult.Good); + } else ApplyResult(r => r.Type = HitResult.Miss); } From 9d5a9775017f7ffa67207419b28b5cf3e6fdca61 Mon Sep 17 00:00:00 2001 From: Tim Oliver Date: Tue, 7 Apr 2020 16:40:18 +0800 Subject: [PATCH 0097/1117] Added judgement forwarder to drumroll tick object --- .../Objects/Drawables/DrawableDrumRollTick.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs index 25b6141a0e..9961cb6ea2 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs @@ -11,6 +11,12 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { public class DrawableDrumRollTick : DrawableTaikoHitObject { + /// + /// The action type that the user took which caused this tick to + /// have been judged as "hit" + /// + public TaikoAction JudgedAction; + public DrawableDrumRollTick(DrumRollTick tick) : base(tick) { @@ -49,7 +55,11 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables } } - public override bool OnPressed(TaikoAction action) => UpdateResult(true); + public override bool OnPressed(TaikoAction action) + { + JudgedAction = action; + return UpdateResult(true); + } protected override DrawableStrongNestedHit CreateStrongHit(StrongHitObject hitObject) => new StrongNestedHit(hitObject, this); From 7751c5e3aa30fc530b4d9c8973c307765b5f7b16 Mon Sep 17 00:00:00 2001 From: Tim Oliver Date: Tue, 7 Apr 2020 16:40:32 +0800 Subject: [PATCH 0098/1117] Added separate scrolling track to display drum roll notes --- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 25 ++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index bde9085c23..b32e7b53da 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -42,6 +42,7 @@ namespace osu.Game.Rulesets.Taiko.UI private readonly Container hitExplosionContainer; private readonly Container kiaiExplosionContainer; private readonly JudgementContainer judgementContainer; + private readonly ScrollingHitObjectContainer drumRollHitContainer; internal readonly HitTarget HitTarget; private readonly ProxyContainer topLevelHitContainer; @@ -135,6 +136,14 @@ namespace osu.Game.Rulesets.Taiko.UI Margin = new MarginPadding { Left = HIT_TARGET_OFFSET }, Blending = BlendingParameters.Additive }, + drumRollHitContainer = new ScrollingHitObjectContainer + { + Name = "Drumroll hit", + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Stretch, + Margin = new MarginPadding { Left = HIT_TARGET_OFFSET }, + Width = 1.0f + } } }, overlayBackgroundContainer = new Container @@ -212,12 +221,28 @@ namespace osu.Game.Rulesets.Taiko.UI barlineContainer.Add(barline.CreateProxy()); break; + case DrawableDrumRoll drumRoll: + drumRoll.OnHit += onDrumrollArbitraryHit; + break; + case DrawableTaikoHitObject taikoObject: topLevelHitContainer.Add(taikoObject.CreateProxiedContent()); break; } } + private void onDrumrollArbitraryHit(TaikoAction action) + { + DrawableHit drawableHit; + + if (action == TaikoAction.LeftRim || action == TaikoAction.RightRim) + drawableHit = new DrawableFlyingRimHit(Time.Current); + else + drawableHit = new DrawableFlyingCentreHit(Time.Current); + + drumRollHitContainer.Add(drawableHit); + } + internal void OnNewResult(DrawableHitObject judgedObject, JudgementResult result) { if (!DisplayJudgements.Value) From b883586addd31c3933c8549af61a071ad65a143d Mon Sep 17 00:00:00 2001 From: Tim Oliver Date: Tue, 7 Apr 2020 17:25:47 +0800 Subject: [PATCH 0099/1117] Added logic to allow strong notes --- .../Objects/Drawables/DrawableCentreHit.cs | 4 ++-- .../Objects/Drawables/DrawableDrumRoll.cs | 6 +++--- osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs | 4 ++-- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 6 +++--- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs index 08df05e719..86e885239f 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs @@ -34,8 +34,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables ApplyResult(r => r.Type = HitResult.Good); } - public DrawableFlyingCentreHit(double time) - : base(new Hit { StartTime = time }) + public DrawableFlyingCentreHit(double time, bool isStrong = false) + : base(new Hit { StartTime = time, IsStrong = isStrong }) { HitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs index 3e7b6dfd31..64be870262 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs @@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables /// A handler action for when the drumroll has been hit, /// regardless of any judgement. /// - public Action OnHit; + public Action OnHit; public DrawableDrumRoll(DrumRoll drumRoll) : base(drumRoll) @@ -97,7 +97,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables public override bool OnPressed(TaikoAction action) { if (judgingStarted) - OnHit.Invoke(action); + OnHit.Invoke(action, HitObject.IsStrong); return false; } @@ -111,7 +111,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables if (result.Type > HitResult.Miss) { - OnHit.Invoke(drumRollTick.JudgedAction); + OnHit.Invoke(drumRollTick.JudgedAction, HitObject.IsStrong); judgingStarted = true; rollingHits++; } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs index 0c2c9fbdef..ad9872b21f 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs @@ -34,8 +34,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables ApplyResult(r => r.Type = HitResult.Good); } - public DrawableFlyingRimHit(double time) - : base(new Hit { StartTime = time }) + public DrawableFlyingRimHit(double time, bool isStrong = false) + : base(new Hit { StartTime = time, IsStrong = isStrong }) { HitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); } diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index b32e7b53da..59cf9193b5 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -231,14 +231,14 @@ namespace osu.Game.Rulesets.Taiko.UI } } - private void onDrumrollArbitraryHit(TaikoAction action) + private void onDrumrollArbitraryHit(TaikoAction action, bool isStrong) { DrawableHit drawableHit; if (action == TaikoAction.LeftRim || action == TaikoAction.RightRim) - drawableHit = new DrawableFlyingRimHit(Time.Current); + drawableHit = new DrawableFlyingRimHit(Time.Current, isStrong); else - drawableHit = new DrawableFlyingCentreHit(Time.Current); + drawableHit = new DrawableFlyingCentreHit(Time.Current, isStrong); drumRollHitContainer.Add(drawableHit); } From c30ea2ec2916d2f5852a63b0e21f599bb34b6ef0 Mon Sep 17 00:00:00 2001 From: Tim Oliver Date: Wed, 8 Apr 2020 12:12:59 +0800 Subject: [PATCH 0100/1117] Added content proxying to drull roll elements --- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 59cf9193b5..e947795fe5 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -241,6 +241,7 @@ namespace osu.Game.Rulesets.Taiko.UI drawableHit = new DrawableFlyingCentreHit(Time.Current, isStrong); drumRollHitContainer.Add(drawableHit); + topLevelHitContainer.Add(drawableHit.CreateProxiedContent()); } internal void OnNewResult(DrawableHitObject judgedObject, JudgementResult result) From 65823fb2e1101f4b73bc7446063520ca95bbf846 Mon Sep 17 00:00:00 2001 From: Alchyr Date: Wed, 8 Apr 2020 01:42:35 -0700 Subject: [PATCH 0101/1117] Use redundancy test --- .../NonVisual/ControlPointInfoTest.cs | 23 ++++++++++++------- .../Beatmaps/ControlPoints/ControlPoint.cs | 8 +++++++ .../ControlPoints/ControlPointInfo.cs | 2 +- .../ControlPoints/DifficultyControlPoint.cs | 1 + .../ControlPoints/EffectControlPoint.cs | 1 + .../ControlPoints/SampleControlPoint.cs | 1 + .../ControlPoints/TimingControlPoint.cs | 6 ++++- 7 files changed, 32 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs b/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs index 2782e902fe..158954106d 100644 --- a/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs +++ b/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs @@ -29,11 +29,17 @@ namespace osu.Game.Tests.NonVisual var cpi = new ControlPointInfo(); cpi.Add(0, new TimingControlPoint()); // is *not* redundant, special exception for first timing point. - cpi.Add(1000, new TimingControlPoint()); // is redundant + cpi.Add(1000, new TimingControlPoint()); // is also not redundant, due to change of offset - Assert.That(cpi.Groups.Count, Is.EqualTo(1)); - Assert.That(cpi.TimingPoints.Count, Is.EqualTo(1)); - Assert.That(cpi.AllControlPoints.Count(), Is.EqualTo(1)); + Assert.That(cpi.Groups.Count, Is.EqualTo(2)); + Assert.That(cpi.TimingPoints.Count, Is.EqualTo(2)); + Assert.That(cpi.AllControlPoints.Count(), Is.EqualTo(2)); + + cpi.Add(1000, new TimingControlPoint()); //is redundant + + Assert.That(cpi.Groups.Count, Is.EqualTo(2)); + Assert.That(cpi.TimingPoints.Count, Is.EqualTo(2)); + Assert.That(cpi.AllControlPoints.Count(), Is.EqualTo(2)); } [Test] @@ -86,11 +92,12 @@ namespace osu.Game.Tests.NonVisual Assert.That(cpi.EffectPoints.Count, Is.EqualTo(0)); Assert.That(cpi.AllControlPoints.Count(), Is.EqualTo(0)); - cpi.Add(1000, new EffectControlPoint { KiaiMode = true }); // is not redundant + cpi.Add(1000, new EffectControlPoint { KiaiMode = true, OmitFirstBarLine = true }); // is not redundant + cpi.Add(1400, new EffectControlPoint { KiaiMode = true, OmitFirstBarLine = true }); // same settings, but is not redundant - Assert.That(cpi.Groups.Count, Is.EqualTo(1)); - Assert.That(cpi.EffectPoints.Count, Is.EqualTo(1)); - Assert.That(cpi.AllControlPoints.Count(), Is.EqualTo(1)); + Assert.That(cpi.Groups.Count, Is.EqualTo(2)); + Assert.That(cpi.EffectPoints.Count, Is.EqualTo(2)); + Assert.That(cpi.AllControlPoints.Count(), Is.EqualTo(2)); } [Test] diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs index 39a0e6f6d4..411a4441de 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs @@ -25,6 +25,14 @@ namespace osu.Game.Beatmaps.ControlPoints /// Whether equivalent. public abstract bool EquivalentTo(ControlPoint other); + /// + /// Whether this control point results in a meaningful change when placed after another. + /// + /// Another control point to compare with. + /// The time this timing point will be placed at. + /// Whether redundant. + public abstract bool IsRedundant(ControlPoint other, double time); + public bool Equals(ControlPoint other) => Time == other?.Time && EquivalentTo(other); } } diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs index df68d8acd2..37a3dbf592 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs @@ -247,7 +247,7 @@ namespace osu.Game.Beatmaps.ControlPoints break; } - return existing?.EquivalentTo(newPoint) == true; + return newPoint.IsRedundant(existing, time); } private void groupItemAdded(ControlPoint controlPoint) diff --git a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs index 8b21098a51..44522dc927 100644 --- a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs @@ -29,5 +29,6 @@ namespace osu.Game.Beatmaps.ControlPoints public override bool EquivalentTo(ControlPoint other) => other is DifficultyControlPoint otherTyped && otherTyped.SpeedMultiplier.Equals(SpeedMultiplier); + public override bool IsRedundant(ControlPoint other, double time) => EquivalentTo(other); } } diff --git a/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs index 369b93ff3d..8066c6b577 100644 --- a/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs @@ -38,5 +38,6 @@ namespace osu.Game.Beatmaps.ControlPoints public override bool EquivalentTo(ControlPoint other) => other is EffectControlPoint otherTyped && KiaiMode == otherTyped.KiaiMode && OmitFirstBarLine == otherTyped.OmitFirstBarLine; + public override bool IsRedundant(ControlPoint other, double time) => !OmitFirstBarLine && EquivalentTo(other); } } diff --git a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs index 393bcfdb3c..cf7c842b24 100644 --- a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs @@ -71,5 +71,6 @@ namespace osu.Game.Beatmaps.ControlPoints public override bool EquivalentTo(ControlPoint other) => other is SampleControlPoint otherTyped && SampleBank == otherTyped.SampleBank && SampleVolume == otherTyped.SampleVolume; + public override bool IsRedundant(ControlPoint other, double time) => EquivalentTo(other); } } diff --git a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs index 158788964b..d14ac1221b 100644 --- a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs @@ -50,6 +50,10 @@ namespace osu.Game.Beatmaps.ControlPoints public override bool EquivalentTo(ControlPoint other) => other is TimingControlPoint otherTyped - && Time == otherTyped.Time && TimeSignature == otherTyped.TimeSignature && BeatLength.Equals(otherTyped.BeatLength); + && TimeSignature == otherTyped.TimeSignature && BeatLength.Equals(otherTyped.BeatLength); + + public override bool IsRedundant(ControlPoint other, double time) => + EquivalentTo(other) + && other.Time == time; } } From d27d8671ab08e6a334f9859e46a295c68b3d5f01 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 8 Apr 2020 14:23:29 +0300 Subject: [PATCH 0102/1117] Convert all static getter-only properties to static readonly fields --- .../TestSceneHyperDashColouring.cs | 18 +++++++++--------- .../Objects/Drawables/FruitPiece.cs | 4 ++-- .../Skinning/CatchSkinExtensions.cs | 2 +- osu.Game.Rulesets.Catch/UI/Catcher.cs | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs index c8d28dbaeb..846b17f324 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs @@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Catch.Tests }, false, false, false); }); - AddAssert("hyper-dash fruit has default colour", () => checkLegacyFruitHyperDashColour(drawableFruit, Catcher.DefaultHyperDashColour)); + AddAssert("hyper-dash fruit has default colour", () => checkLegacyFruitHyperDashColour(drawableFruit, Catcher.DEFAULT_HYPER_DASH_COLOUR)); } [TestCase(true)] @@ -66,7 +66,7 @@ namespace osu.Game.Rulesets.Catch.Tests }, customCatcherHyperDashColour, false, true); }); - AddAssert("hyper-dash fruit use fruit colour from skin", () => checkLegacyFruitHyperDashColour(drawableFruit, TestSkin.CustomHyperDashFruitColour)); + AddAssert("hyper-dash fruit use fruit colour from skin", () => checkLegacyFruitHyperDashColour(drawableFruit, TestSkin.CUSTOM_HYPER_DASH_FRUIT_COLOUR)); } [Test] @@ -88,7 +88,7 @@ namespace osu.Game.Rulesets.Catch.Tests }, true, false, false); }); - AddAssert("hyper-dash fruit colour falls back to catcher colour from skin", () => checkLegacyFruitHyperDashColour(drawableFruit, TestSkin.CustomHyperDashColour)); + AddAssert("hyper-dash fruit colour falls back to catcher colour from skin", () => checkLegacyFruitHyperDashColour(drawableFruit, TestSkin.CUSTOM_HYPER_DASH_COLOUR)); } private Drawable setupSkinHierarchy(Drawable child, bool customCatcherColour, bool customAfterColour, bool customFruitColour) @@ -108,21 +108,21 @@ namespace osu.Game.Rulesets.Catch.Tests private class TestSkin : LegacySkin { - public static Color4 CustomHyperDashColour { get; } = Color4.Goldenrod; - public static Color4 CustomHyperDashFruitColour { get; } = Color4.Cyan; - public static Color4 CustomHyperDashAfterColour { get; } = Color4.Lime; + public static readonly Color4 CUSTOM_HYPER_DASH_COLOUR = Color4.Goldenrod; + public static readonly Color4 CUSTOM_HYPER_DASH_AFTER_COLOUR = Color4.Lime; + public static readonly Color4 CUSTOM_HYPER_DASH_FRUIT_COLOUR = Color4.Cyan; public TestSkin(bool customCatcherColour, bool customAfterColour, bool customFruitColour) : base(new SkinInfo(), null, null, string.Empty) { if (customCatcherColour) - Configuration.CustomColours[CatchSkinColour.HyperDash.ToString()] = CustomHyperDashColour; + Configuration.CustomColours[CatchSkinColour.HyperDash.ToString()] = CUSTOM_HYPER_DASH_COLOUR; if (customAfterColour) - Configuration.CustomColours[CatchSkinColour.HyperDashAfterImage.ToString()] = CustomHyperDashAfterColour; + Configuration.CustomColours[CatchSkinColour.HyperDashAfterImage.ToString()] = CUSTOM_HYPER_DASH_AFTER_COLOUR; if (customFruitColour) - Configuration.CustomColours[CatchSkinColour.HyperDashFruit.ToString()] = CustomHyperDashFruitColour; + Configuration.CustomColours[CatchSkinColour.HyperDashFruit.ToString()] = CUSTOM_HYPER_DASH_FRUIT_COLOUR; } } } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/FruitPiece.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/FruitPiece.cs index 359329885c..7ac9f11ad6 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/FruitPiece.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/FruitPiece.cs @@ -68,7 +68,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables RelativeSizeAxes = Axes.Both, Anchor = Anchor.Centre, Origin = Anchor.Centre, - BorderColour = Catcher.DefaultHyperDashColour, + BorderColour = Catcher.DEFAULT_HYPER_DASH_COLOUR, BorderThickness = 12f * RADIUS_ADJUST, Children = new Drawable[] { @@ -78,7 +78,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables Alpha = 0.3f, Blending = BlendingParameters.Additive, RelativeSizeAxes = Axes.Both, - Colour = Catcher.DefaultHyperDashColour, + Colour = Catcher.DEFAULT_HYPER_DASH_COLOUR, } } }); diff --git a/osu.Game.Rulesets.Catch/Skinning/CatchSkinExtensions.cs b/osu.Game.Rulesets.Catch/Skinning/CatchSkinExtensions.cs index 623f87bf11..718b22a0fb 100644 --- a/osu.Game.Rulesets.Catch/Skinning/CatchSkinExtensions.cs +++ b/osu.Game.Rulesets.Catch/Skinning/CatchSkinExtensions.cs @@ -15,6 +15,6 @@ namespace osu.Game.Rulesets.Catch.Skinning public static IBindable GetHyperDashFruitColour(this ISkin skin) => skin.GetConfig(CatchSkinColour.HyperDashFruit) ?? skin.GetConfig(CatchSkinColour.HyperDash) ?? - new Bindable(Catcher.DefaultHyperDashColour); + new Bindable(Catcher.DEFAULT_HYPER_DASH_COLOUR); } } diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 9bfff209d5..920d804e72 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Catch.UI { public class Catcher : Container, IKeyBindingHandler { - public static Color4 DefaultHyperDashColour { get; } = Color4.Red; + public static readonly Color4 DEFAULT_HYPER_DASH_COLOUR = Color4.Red; /// /// Whether we are hyper-dashing or not. From 4976f80b7107ae0e7554b02423ef0515c1b023f6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 9 Apr 2020 14:31:25 +0900 Subject: [PATCH 0103/1117] Move implementation to HUD --- osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs | 8 -------- osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs | 3 --- osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs | 8 -------- osu.Game/Screens/Play/HUD/FailingLayer.cs | 12 ++++++++++++ osu.Game/Screens/Play/HUD/HealthDisplay.cs | 2 +- osu.Game/Screens/Play/HUDOverlay.cs | 7 ++++++- 6 files changed, 19 insertions(+), 21 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs index 4df2bc0f52..ebe45aa3ab 100644 --- a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using osu.Framework.Allocation; using osu.Framework.Input; using osu.Game.Beatmaps; using osu.Game.Configuration; @@ -15,7 +14,6 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; -using osu.Game.Screens.Play.HUD; namespace osu.Game.Rulesets.Catch.UI { @@ -32,12 +30,6 @@ namespace osu.Game.Rulesets.Catch.UI TimeRange.Value = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450); } - [BackgroundDependencyLoader] - private void load() - { - Overlays.Add(new FailingLayer()); - } - protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay); protected override ReplayRecorder CreateReplayRecorder(Replay replay) => new CatchReplayRecorder(replay, (CatchPlayfield)Playfield); diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs index 18bdfa5b5d..14cad39b04 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs @@ -21,7 +21,6 @@ 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.Screens.Play.HUD; using osuTK; namespace osu.Game.Rulesets.Mania.UI @@ -78,8 +77,6 @@ namespace osu.Game.Rulesets.Mania.UI configDirection.BindValueChanged(direction => Direction.Value = (ScrollingDirection)direction.NewValue, true); Config.BindWith(ManiaRulesetSetting.ScrollTime, TimeRange); - - Overlays.Add(new FailingLayer()); } protected override void AdjustScrollSpeed(int amount) diff --git a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs index b04e3cef3b..b4d51d11c9 100644 --- a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; -using osu.Framework.Allocation; using osu.Framework.Input; using osu.Game.Beatmaps; using osu.Game.Input.Handlers; @@ -17,7 +16,6 @@ using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Replays; using osu.Game.Rulesets.UI; using osu.Game.Screens.Play; -using osu.Game.Screens.Play.HUD; using osuTK; namespace osu.Game.Rulesets.Osu.UI @@ -31,12 +29,6 @@ namespace osu.Game.Rulesets.Osu.UI { } - [BackgroundDependencyLoader] - private void load() - { - Overlays.Add(new FailingLayer()); - } - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; // always show the gameplay cursor protected override Playfield CreatePlayfield() => new OsuPlayfield(); diff --git a/osu.Game/Screens/Play/HUD/FailingLayer.cs b/osu.Game/Screens/Play/HUD/FailingLayer.cs index 761178b93d..f026d09c39 100644 --- a/osu.Game/Screens/Play/HUD/FailingLayer.cs +++ b/osu.Game/Screens/Play/HUD/FailingLayer.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Utils; using osu.Game.Configuration; using osu.Game.Graphics; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Screens.Play.HUD { @@ -48,6 +49,17 @@ namespace osu.Game.Screens.Play.HUD enabled.BindValueChanged(e => this.FadeTo(e.NewValue ? 1 : 0, fade_time, Easing.OutQuint), true); } + public override void BindHealthProcessor(HealthProcessor processor) + { + base.BindHealthProcessor(processor); + + if (!(processor is DrainingHealthProcessor)) + { + enabled.UnbindBindings(); + enabled.Value = false; + } + } + protected override void Update() { box.Alpha = (float)Interpolation.ValueAt(Math.Clamp(Clock.ElapsedFrameTime, 0, fade_time), box.Alpha, diff --git a/osu.Game/Screens/Play/HUD/HealthDisplay.cs b/osu.Game/Screens/Play/HUD/HealthDisplay.cs index 01cb64a88c..08cb07d7ee 100644 --- a/osu.Game/Screens/Play/HUD/HealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/HealthDisplay.cs @@ -23,7 +23,7 @@ namespace osu.Game.Screens.Play.HUD /// /// Bind the tracked fields of to this health display. /// - public void BindHealthProcessor(HealthProcessor processor) + public virtual void BindHealthProcessor(HealthProcessor processor) { Current.BindTo(processor.Health); } diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index e06f6d19c2..5114efd9a9 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -37,6 +37,7 @@ namespace osu.Game.Screens.Play public readonly HitErrorDisplay HitErrorDisplay; public readonly HoldForMenuButton HoldToQuit; public readonly PlayerSettingsOverlay PlayerSettingsOverlay; + public readonly FailingLayer FailingLayer; public Bindable ShowHealthbar = new Bindable(true); @@ -75,6 +76,7 @@ namespace osu.Game.Screens.Play Children = new Drawable[] { + FailingLayer = CreateFailingLayer(), visibilityContainer = new Container { RelativeSizeAxes = Axes.Both, @@ -260,6 +262,8 @@ namespace osu.Game.Screens.Play Margin = new MarginPadding { Top = 20 } }; + protected virtual FailingLayer CreateFailingLayer() => new FailingLayer(); + protected virtual KeyCounterDisplay CreateKeyCounter() => new KeyCounterDisplay { Anchor = Anchor.BottomRight, @@ -304,7 +308,8 @@ namespace osu.Game.Screens.Play protected virtual void BindHealthProcessor(HealthProcessor processor) { - HealthDisplay?.Current.BindTo(processor.Health); + HealthDisplay?.BindHealthProcessor(processor); + FailingLayer?.BindHealthProcessor(processor); } } } From 947745d87eff2d73b2f6f3c7da091897245217e7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 9 Apr 2020 14:33:11 +0900 Subject: [PATCH 0104/1117] Change fail effect to be less distracting --- .../Visual/Gameplay/TestSceneFailingLayer.cs | 5 +-- osu.Game/Screens/Play/HUD/FailingLayer.cs | 40 +++++++++++++++---- 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs index 97fe0ac769..42a211cb3d 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs @@ -1,11 +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 NUnit.Framework; using osu.Framework.Allocation; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Testing; using osu.Game.Configuration; using osu.Game.Screens.Play.HUD; @@ -40,7 +37,7 @@ namespace osu.Game.Tests.Visual.Gameplay public void TestLayerFading() { AddSliderStep("current health", 0.0, 1.0, 1.0, val => layer.Current.Value = val); - var box = layer.ChildrenOfType().First(); + var box = layer.Child; AddStep("set health to 0.10", () => layer.Current.Value = 0.10); AddWaitStep("wait for fade to finish", 5); diff --git a/osu.Game/Screens/Play/HUD/FailingLayer.cs b/osu.Game/Screens/Play/HUD/FailingLayer.cs index f026d09c39..79f6855804 100644 --- a/osu.Game/Screens/Play/HUD/FailingLayer.cs +++ b/osu.Game/Screens/Play/HUD/FailingLayer.cs @@ -4,12 +4,16 @@ using System; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Utils; using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Rulesets.Scoring; +using osuTK.Graphics; namespace osu.Game.Screens.Play.HUD { @@ -22,8 +26,6 @@ namespace osu.Game.Screens.Play.HUD private const int fade_time = 400; - private readonly Box box; - private Bindable enabled; /// @@ -31,20 +33,43 @@ namespace osu.Game.Screens.Play.HUD /// public double LowHealthThreshold = 0.20f; + private readonly Container boxes; + public FailingLayer() { RelativeSizeAxes = Axes.Both; - Child = box = new Box + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both, - Alpha = 0 + boxes = new Container + { + Blending = BlendingParameters.Additive, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientVertical(Color4.White, Color4.White.Opacity(0)), + Height = 0.2f, + }, + new Box + { + RelativeSizeAxes = Axes.Both, + Height = 0.2f, + Colour = ColourInfo.GradientVertical(Color4.White.Opacity(0), Color4.White), + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + }, + } + }, }; } [BackgroundDependencyLoader] private void load(OsuColour color, OsuConfigManager config) { - box.Colour = color.Red; + boxes.Colour = color.Red; + enabled = config.GetBindable(OsuSetting.FadePlayfieldWhenHealthLow); enabled.BindValueChanged(e => this.FadeTo(e.NewValue ? 1 : 0, fade_time, Easing.OutQuint), true); } @@ -53,6 +78,7 @@ namespace osu.Game.Screens.Play.HUD { base.BindHealthProcessor(processor); + // don't display ever if the ruleset is not using a draining health display. if (!(processor is DrainingHealthProcessor)) { enabled.UnbindBindings(); @@ -62,7 +88,7 @@ namespace osu.Game.Screens.Play.HUD protected override void Update() { - box.Alpha = (float)Interpolation.ValueAt(Math.Clamp(Clock.ElapsedFrameTime, 0, fade_time), box.Alpha, + boxes.Alpha = (float)Interpolation.ValueAt(Math.Clamp(Clock.ElapsedFrameTime, 0, fade_time), boxes.Alpha, Math.Clamp(max_alpha * (1 - Current.Value / LowHealthThreshold), 0, max_alpha), 0, fade_time, Easing.Out); base.Update(); From 52c976265146e754738a5c8f94222687537cd769 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 9 Apr 2020 14:36:04 +0900 Subject: [PATCH 0105/1117] Remove pointless keywords --- osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs index 4b75910454..0e854e8e9f 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs @@ -56,7 +56,6 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay { LabelText = "Fade playfield to red when health is low", Bindable = config.GetBindable(OsuSetting.FadePlayfieldWhenHealthLow), - Keywords = new[] { "hp", "low", "playfield", "red" } }, new SettingsCheckbox { From 6db22366e2bcd50bb60aaa5b327d42e6fa639ad0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 9 Apr 2020 14:47:48 +0900 Subject: [PATCH 0106/1117] Add new tests and tidy up existing tests --- .../Visual/Gameplay/TestSceneFailingLayer.cs | 51 ++++++++++++------- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs index 42a211cb3d..0b5f023007 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs @@ -3,48 +3,63 @@ using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Testing; using osu.Game.Configuration; +using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Play.HUD; namespace osu.Game.Tests.Visual.Gameplay { public class TestSceneFailingLayer : OsuTestScene { - private readonly FailingLayer layer; + private FailingLayer layer; [Resolved] private OsuConfigManager config { get; set; } - public TestSceneFailingLayer() + [SetUpSteps] + public void SetUpSteps() { - Child = layer = new FailingLayer(); + AddStep("create layer", () => Child = layer = new FailingLayer()); + AddStep("enable layer", () => config.Set(OsuSetting.FadePlayfieldWhenHealthLow, true)); + AddUntilStep("layer is visible", () => layer.IsPresent); } [Test] - public void TestLayerConfig() + public void TestLayerDisabledViaConfig() { - AddStep("enable layer", () => config.Set(OsuSetting.FadePlayfieldWhenHealthLow, true)); - AddWaitStep("wait for transition to finish", 5); - AddAssert("layer is enabled", () => layer.IsPresent); - AddStep("disable layer", () => config.Set(OsuSetting.FadePlayfieldWhenHealthLow, false)); - AddWaitStep("wait for transition to finish", 5); - AddAssert("layer is disabled", () => !layer.IsPresent); - AddStep("restore layer enabling", () => config.Set(OsuSetting.FadePlayfieldWhenHealthLow, true)); + AddUntilStep("layer is not visible", () => !layer.IsPresent); + } + + [Test] + public void TestLayerVisibilityWithAccumulatingProcessor() + { + AddStep("bind accumulating processor", () => layer.BindHealthProcessor(new AccumulatingHealthProcessor(1))); + AddUntilStep("layer is not visible", () => !layer.IsPresent); + } + + [Test] + public void TestLayerVisibilityWithDrainingProcessor() + { + AddStep("bind accumulating processor", () => layer.BindHealthProcessor(new DrainingHealthProcessor(1))); + AddWaitStep("wait for potential fade", 10); + AddAssert("layer is still visible", () => layer.IsPresent); } [Test] public void TestLayerFading() { - AddSliderStep("current health", 0.0, 1.0, 1.0, val => layer.Current.Value = val); - var box = layer.Child; + AddSliderStep("current health", 0.0, 1.0, 1.0, val => + { + if (layer != null) + layer.Current.Value = val; + }); - AddStep("set health to 0.10", () => layer.Current.Value = 0.10); - AddWaitStep("wait for fade to finish", 5); - AddAssert("layer fade is visible", () => box.IsPresent); + AddStep("set health to 0.10", () => layer.Current.Value = 0.1); + AddUntilStep("layer fade is visible", () => layer.Child.Alpha > 0.1f); AddStep("set health to 1", () => layer.Current.Value = 1f); - AddWaitStep("wait for fade to finish", 10); - AddAssert("layer fade is invisible", () => !box.IsPresent); + AddUntilStep("layer fade is invisible", () => !layer.Child.IsPresent); } } } From c44957db3f8261da13f277c1b225e0bedbdebf3b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 9 Apr 2020 14:49:09 +0900 Subject: [PATCH 0107/1117] Change initial health to 1 to avoid false fail display --- osu.Game/Screens/Play/HUD/FailingLayer.cs | 1 + osu.Game/Screens/Play/HUD/HealthDisplay.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/FailingLayer.cs b/osu.Game/Screens/Play/HUD/FailingLayer.cs index 79f6855804..335516a767 100644 --- a/osu.Game/Screens/Play/HUD/FailingLayer.cs +++ b/osu.Game/Screens/Play/HUD/FailingLayer.cs @@ -42,6 +42,7 @@ namespace osu.Game.Screens.Play.HUD { boxes = new Container { + Alpha = 0, Blending = BlendingParameters.Additive, RelativeSizeAxes = Axes.Both, Children = new Drawable[] diff --git a/osu.Game/Screens/Play/HUD/HealthDisplay.cs b/osu.Game/Screens/Play/HUD/HealthDisplay.cs index 08cb07d7ee..edc9dedf24 100644 --- a/osu.Game/Screens/Play/HUD/HealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/HealthDisplay.cs @@ -14,7 +14,7 @@ namespace osu.Game.Screens.Play.HUD /// public abstract class HealthDisplay : Container { - public readonly BindableDouble Current = new BindableDouble + public readonly BindableDouble Current = new BindableDouble(1) { MinValue = 0, MaxValue = 1 From 5a78e74470740ab31141c71b623d3d0c4bfa2987 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 9 Apr 2020 14:51:50 +0900 Subject: [PATCH 0108/1117] Use Lerp instead of ValueAt --- osu.Game/Screens/Play/HUD/FailingLayer.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/FailingLayer.cs b/osu.Game/Screens/Play/HUD/FailingLayer.cs index 335516a767..2a98e277b3 100644 --- a/osu.Game/Screens/Play/HUD/FailingLayer.cs +++ b/osu.Game/Screens/Play/HUD/FailingLayer.cs @@ -89,8 +89,9 @@ namespace osu.Game.Screens.Play.HUD protected override void Update() { - boxes.Alpha = (float)Interpolation.ValueAt(Math.Clamp(Clock.ElapsedFrameTime, 0, fade_time), boxes.Alpha, - Math.Clamp(max_alpha * (1 - Current.Value / LowHealthThreshold), 0, max_alpha), 0, fade_time, Easing.Out); + double target = Math.Clamp(max_alpha * (1 - Current.Value / LowHealthThreshold), 0, max_alpha); + + boxes.Alpha = (float)Interpolation.Lerp(boxes.Alpha, target, Clock.ElapsedFrameTime * 0.01f); base.Update(); } From 1c72afe8c49bfbe8df6f7670bb21c8f45cbcd271 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 9 Apr 2020 14:52:40 +0900 Subject: [PATCH 0109/1117] Move fading test to top for convenience --- .../Visual/Gameplay/TestSceneFailingLayer.cs | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs index 0b5f023007..d831ea1835 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs @@ -25,6 +25,21 @@ namespace osu.Game.Tests.Visual.Gameplay AddUntilStep("layer is visible", () => layer.IsPresent); } + [Test] + public void TestLayerFading() + { + AddSliderStep("current health", 0.0, 1.0, 1.0, val => + { + if (layer != null) + layer.Current.Value = val; + }); + + AddStep("set health to 0.10", () => layer.Current.Value = 0.1); + AddUntilStep("layer fade is visible", () => layer.Child.Alpha > 0.1f); + AddStep("set health to 1", () => layer.Current.Value = 1f); + AddUntilStep("layer fade is invisible", () => !layer.Child.IsPresent); + } + [Test] public void TestLayerDisabledViaConfig() { @@ -46,20 +61,5 @@ namespace osu.Game.Tests.Visual.Gameplay AddWaitStep("wait for potential fade", 10); AddAssert("layer is still visible", () => layer.IsPresent); } - - [Test] - public void TestLayerFading() - { - AddSliderStep("current health", 0.0, 1.0, 1.0, val => - { - if (layer != null) - layer.Current.Value = val; - }); - - AddStep("set health to 0.10", () => layer.Current.Value = 0.1); - AddUntilStep("layer fade is visible", () => layer.Child.Alpha > 0.1f); - AddStep("set health to 1", () => layer.Current.Value = 1f); - AddUntilStep("layer fade is invisible", () => !layer.Child.IsPresent); - } } } From c5005eb378c657fff32971aebcb336ea4ff20ad1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 9 Apr 2020 14:55:02 +0900 Subject: [PATCH 0110/1117] Adjust gradient size slightly and make const --- osu.Game/Screens/Play/HUD/FailingLayer.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/FailingLayer.cs b/osu.Game/Screens/Play/HUD/FailingLayer.cs index 2a98e277b3..a1188343ac 100644 --- a/osu.Game/Screens/Play/HUD/FailingLayer.cs +++ b/osu.Game/Screens/Play/HUD/FailingLayer.cs @@ -33,6 +33,8 @@ namespace osu.Game.Screens.Play.HUD /// public double LowHealthThreshold = 0.20f; + private const float gradient_size = 0.3f; + private readonly Container boxes; public FailingLayer() @@ -51,12 +53,12 @@ namespace osu.Game.Screens.Play.HUD { RelativeSizeAxes = Axes.Both, Colour = ColourInfo.GradientVertical(Color4.White, Color4.White.Opacity(0)), - Height = 0.2f, + Height = gradient_size, }, new Box { RelativeSizeAxes = Axes.Both, - Height = 0.2f, + Height = gradient_size, Colour = ColourInfo.GradientVertical(Color4.White.Opacity(0), Color4.White), Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, From 134feefa141b27b81ca9674a8332257216ee4755 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 9 Apr 2020 13:10:09 +0300 Subject: [PATCH 0111/1117] Remove bindable --- .../TestSceneOverlayScrollContainer.cs | 6 ++-- osu.Game/Overlays/OverlayScrollContainer.cs | 36 +++++++++---------- 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayScrollContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayScrollContainer.cs index 0eccc907a1..4205d65100 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayScrollContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayScrollContainer.cs @@ -55,13 +55,13 @@ namespace osu.Game.Tests.Visual.UserInterface [Test] public void TestButtonVisibility() { - AddAssert("button is hidden", () => scroll.Button.Current.Value == Visibility.Hidden); + AddAssert("button is hidden", () => scroll.Button.State == Visibility.Hidden); AddStep("scroll to end", () => scroll.ScrollToEnd(false)); - AddAssert("button is visible", () => scroll.Button.Current.Value == Visibility.Visible); + AddAssert("button is visible", () => scroll.Button.State == Visibility.Visible); AddStep("scroll to start", () => scroll.ScrollToStart(false)); - AddAssert("button is hidden", () => scroll.Button.Current.Value == Visibility.Hidden); + AddAssert("button is hidden", () => scroll.Button.State == Visibility.Hidden); } [Test] diff --git a/osu.Game/Overlays/OverlayScrollContainer.cs b/osu.Game/Overlays/OverlayScrollContainer.cs index f96d9e3a31..a6c687f28f 100644 --- a/osu.Game/Overlays/OverlayScrollContainer.cs +++ b/osu.Game/Overlays/OverlayScrollContainer.cs @@ -3,14 +3,12 @@ using System.Collections.Generic; 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.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; using osu.Game.Graphics.Containers; using osuTK; @@ -43,7 +41,7 @@ namespace osu.Game.Overlays { ScrollToStart(); currentTarget = Target; - Button.Current.Value = Visibility.Hidden; + Button.State = Visibility.Hidden; } }); } @@ -54,7 +52,7 @@ namespace osu.Game.Overlays if (ScrollContent.DrawHeight + button_scroll_position < DrawHeight) { - Button.Current.Value = Visibility.Hidden; + Button.State = Visibility.Hidden; return; } @@ -62,19 +60,27 @@ namespace osu.Game.Overlays return; currentTarget = Target; - Button.Current.Value = Current > button_scroll_position ? Visibility.Visible : Visibility.Hidden; + Button.State = Current > button_scroll_position ? Visibility.Visible : Visibility.Hidden; } - public class ScrollToTopButton : OsuHoverContainer, IHasCurrentValue + public class ScrollToTopButton : OsuHoverContainer { private const int fade_duration = 500; - private readonly BindableWithCurrent current = new BindableWithCurrent(); + private Visibility state; - public Bindable Current + public Visibility State { - get => current.Current; - set => current.Current = value; + get => state; + set + { + if (value == state) + return; + + state = value; + Enabled.Value = state == Visibility.Visible; + this.FadeTo(state == Visibility.Visible ? 1 : 0, fade_duration, Easing.OutQuint); + } } protected override IEnumerable EffectTargets => new[] { background }; @@ -128,16 +134,6 @@ namespace osu.Game.Overlays flashColour = colourProvider.Light1; } - protected override void LoadComplete() - { - base.LoadComplete(); - Current.BindValueChanged(visibility => - { - Enabled.Value = visibility.NewValue == Visibility.Visible; - this.FadeTo(visibility.NewValue == Visibility.Visible ? 1 : 0, fade_duration, Easing.OutQuint); - }, true); - } - protected override bool OnClick(ClickEvent e) { background.FlashColour(flashColour, 800, Easing.OutQuint); From e58bf8a0d08154d87043f1a90170e8f46f6eea8c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 9 Apr 2020 20:38:38 +0900 Subject: [PATCH 0112/1117] Add DiffPlex package --- osu.Game/osu.Game.csproj | 1 + osu.iOS.props | 1 + 2 files changed, 2 insertions(+) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 3e2c2b1599..3dd84caea9 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -18,6 +18,7 @@ + diff --git a/osu.iOS.props b/osu.iOS.props index 7903d964ce..7e6f6b5246 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -75,6 +75,7 @@ + From 86243d463f423367c2f140be93d00986c044894b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 9 Apr 2020 20:48:59 +0900 Subject: [PATCH 0113/1117] Add legacy beatmap diffing --- .../Editor/LegacyEditorBeatmapDifferTest.cs | 342 ++++++++++++++++++ osu.Game/Screens/Edit/EditorBeatmap.cs | 40 +- .../Screens/Edit/LegacyEditorBeatmapDiffer.cs | 110 ++++++ 3 files changed, 488 insertions(+), 4 deletions(-) create mode 100644 osu.Game.Tests/Editor/LegacyEditorBeatmapDifferTest.cs create mode 100644 osu.Game/Screens/Edit/LegacyEditorBeatmapDiffer.cs diff --git a/osu.Game.Tests/Editor/LegacyEditorBeatmapDifferTest.cs b/osu.Game.Tests/Editor/LegacyEditorBeatmapDifferTest.cs new file mode 100644 index 0000000000..d70a112b7f --- /dev/null +++ b/osu.Game.Tests/Editor/LegacyEditorBeatmapDifferTest.cs @@ -0,0 +1,342 @@ +// 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.Text; +using NUnit.Framework; +using osu.Game.Audio; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Formats; +using osu.Game.IO; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Beatmaps; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Screens.Edit; +using osuTK; +using Decoder = osu.Game.Beatmaps.Formats.Decoder; + +namespace osu.Game.Tests.Editor +{ + [TestFixture] + public class LegacyEditorBeatmapDifferTest + { + private LegacyEditorBeatmapDiffer differ; + private EditorBeatmap current; + + [SetUp] + public void Setup() + { + differ = new LegacyEditorBeatmapDiffer(current = new EditorBeatmap(new OsuBeatmap + { + BeatmapInfo = + { + Ruleset = new OsuRuleset().RulesetInfo + } + })); + } + + [Test] + public void TestAddHitObject() + { + var patch = new OsuBeatmap + { + HitObjects = + { + new HitCircle { StartTime = 1000 } + } + }; + + runTest(patch); + } + + [Test] + public void TestInsertHitObject() + { + current.AddRange(new[] + { + new HitCircle { StartTime = 1000 }, + new HitCircle { StartTime = 3000 }, + }); + + var patch = new OsuBeatmap + { + HitObjects = + { + (OsuHitObject)current.HitObjects[0], + new HitCircle { StartTime = 2000 }, + (OsuHitObject)current.HitObjects[1], + } + }; + + runTest(patch); + } + + [Test] + public void TestDeleteHitObject() + { + current.AddRange(new[] + { + new HitCircle { StartTime = 1000 }, + new HitCircle { StartTime = 2000 }, + new HitCircle { StartTime = 3000 }, + }); + + var patch = new OsuBeatmap + { + HitObjects = + { + (OsuHitObject)current.HitObjects[0], + (OsuHitObject)current.HitObjects[2], + } + }; + + runTest(patch); + } + + [Test] + public void TestChangeStartTime() + { + current.AddRange(new[] + { + new HitCircle { StartTime = 1000 }, + new HitCircle { StartTime = 2000 }, + new HitCircle { StartTime = 3000 }, + }); + + var patch = new OsuBeatmap + { + HitObjects = + { + new HitCircle { StartTime = 500 }, + (OsuHitObject)current.HitObjects[1], + (OsuHitObject)current.HitObjects[2], + } + }; + + runTest(patch); + } + + [Test] + public void TestChangeSample() + { + current.AddRange(new[] + { + new HitCircle { StartTime = 1000 }, + new HitCircle { StartTime = 2000 }, + new HitCircle { StartTime = 3000 }, + }); + + var patch = new OsuBeatmap + { + HitObjects = + { + (OsuHitObject)current.HitObjects[0], + new HitCircle { StartTime = 2000, Samples = { new HitSampleInfo { Name = HitSampleInfo.HIT_FINISH } } }, + (OsuHitObject)current.HitObjects[2], + } + }; + + runTest(patch); + } + + [Test] + public void TestChangeSliderPath() + { + current.AddRange(new OsuHitObject[] + { + new HitCircle { StartTime = 1000 }, + new Slider + { + StartTime = 2000, + Path = new SliderPath(new[] + { + new PathControlPoint(Vector2.Zero), + new PathControlPoint(Vector2.One), + new PathControlPoint(new Vector2(2), PathType.Bezier), + new PathControlPoint(new Vector2(3)), + }, 50) + }, + new HitCircle { StartTime = 3000 }, + }); + + var patch = new OsuBeatmap + { + HitObjects = + { + (OsuHitObject)current.HitObjects[0], + new Slider + { + StartTime = 2000, + Path = new SliderPath(new[] + { + new PathControlPoint(Vector2.Zero, PathType.Bezier), + new PathControlPoint(new Vector2(4)), + new PathControlPoint(new Vector2(5)), + }, 100) + }, + (OsuHitObject)current.HitObjects[2], + } + }; + + runTest(patch); + } + + [Test] + public void TestAddMultipleHitObjects() + { + current.AddRange(new[] + { + new HitCircle { StartTime = 1000 }, + new HitCircle { StartTime = 2000 }, + new HitCircle { StartTime = 3000 }, + }); + + var patch = new OsuBeatmap + { + HitObjects = + { + new HitCircle { StartTime = 500 }, + (OsuHitObject)current.HitObjects[0], + new HitCircle { StartTime = 1500 }, + (OsuHitObject)current.HitObjects[1], + new HitCircle { StartTime = 2250 }, + new HitCircle { StartTime = 2500 }, + (OsuHitObject)current.HitObjects[2], + new HitCircle { StartTime = 3500 }, + } + }; + + runTest(patch); + } + + [Test] + public void TestDeleteMultipleHitObjects() + { + current.AddRange(new[] + { + new HitCircle { StartTime = 500 }, + new HitCircle { StartTime = 1000 }, + new HitCircle { StartTime = 1500 }, + new HitCircle { StartTime = 2000 }, + new HitCircle { StartTime = 2250 }, + new HitCircle { StartTime = 2500 }, + new HitCircle { StartTime = 3000 }, + new HitCircle { StartTime = 3500 }, + }); + + var patch = new OsuBeatmap + { + HitObjects = + { + (OsuHitObject)current.HitObjects[1], + (OsuHitObject)current.HitObjects[3], + (OsuHitObject)current.HitObjects[6], + } + }; + + runTest(patch); + } + + [Test] + public void TestChangeSamplesOfMultipleHitObjects() + { + current.AddRange(new[] + { + new HitCircle { StartTime = 500 }, + new HitCircle { StartTime = 1000 }, + new HitCircle { StartTime = 1500 }, + new HitCircle { StartTime = 2000 }, + new HitCircle { StartTime = 2250 }, + new HitCircle { StartTime = 2500 }, + new HitCircle { StartTime = 3000 }, + new HitCircle { StartTime = 3500 }, + }); + + var patch = new OsuBeatmap + { + HitObjects = + { + (OsuHitObject)current.HitObjects[0], + new HitCircle { StartTime = 1000, Samples = { new HitSampleInfo { Name = HitSampleInfo.HIT_FINISH } } }, + (OsuHitObject)current.HitObjects[2], + (OsuHitObject)current.HitObjects[3], + new HitCircle { StartTime = 2250, Samples = { new HitSampleInfo { Name = HitSampleInfo.HIT_WHISTLE } } }, + (OsuHitObject)current.HitObjects[5], + new HitCircle { StartTime = 3000, Samples = { new HitSampleInfo { Name = HitSampleInfo.HIT_CLAP } } }, + (OsuHitObject)current.HitObjects[7], + } + }; + + runTest(patch); + } + + [Test] + public void TestAddAndDeleteHitObjects() + { + current.AddRange(new[] + { + new HitCircle { StartTime = 500 }, + new HitCircle { StartTime = 1000 }, + new HitCircle { StartTime = 1500 }, + new HitCircle { StartTime = 2000 }, + new HitCircle { StartTime = 2250 }, + new HitCircle { StartTime = 2500 }, + new HitCircle { StartTime = 3000 }, + new HitCircle { StartTime = 3500 }, + }); + + var patch = new OsuBeatmap + { + HitObjects = + { + new HitCircle { StartTime = 750 }, + (OsuHitObject)current.HitObjects[1], + (OsuHitObject)current.HitObjects[4], + (OsuHitObject)current.HitObjects[5], + new HitCircle { StartTime = 2650 }, + new HitCircle { StartTime = 2750 }, + new HitCircle { StartTime = 4000 }, + } + }; + + runTest(patch); + } + + private void runTest(IBeatmap patch) + { + // Due to the method of testing, "patch" comes in without having been decoded via a beatmap decoder. + // This causes issues because the decoder adds various default properties (e.g. new combo on first object, default samples). + // To resolve "patch" into a sane state it is encoded and then re-decoded. + patch = decode(encode(patch)); + + // Apply the patch. + differ.Patch(encode(current), encode(patch)); + + // Convert beatmaps to strings for assertion purposes. + string currentStr = Encoding.ASCII.GetString(encode(current).ToArray()); + string patchStr = Encoding.ASCII.GetString(encode(patch).ToArray()); + + Assert.That(currentStr, Is.EqualTo(patchStr)); + } + + private MemoryStream encode(IBeatmap beatmap) + { + var encoded = new MemoryStream(); + + using (var sw = new StreamWriter(encoded, leaveOpen: true)) + new LegacyBeatmapEncoder(beatmap).Encode(sw); + + return encoded; + } + + private IBeatmap decode(Stream stream) + { + stream.Seek(0, SeekOrigin.Begin); + + using (var reader = new LineBufferedReader(stream, true)) + return Decoder.GetDecoder(reader).Decode(reader); + } + } +} diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs index 7f04a7a58d..22e0061b61 100644 --- a/osu.Game/Screens/Edit/EditorBeatmap.cs +++ b/osu.Game/Screens/Edit/EditorBeatmap.cs @@ -107,6 +107,16 @@ namespace osu.Game.Screens.Edit private IList mutableHitObjects => (IList)PlayableBeatmap.HitObjects; + /// + /// Adds a collection of s to this . + /// + /// The s to add. + public void AddRange(IEnumerable hitObjects) + { + foreach (var h in hitObjects) + Add(h); + } + /// /// Adds a to this . /// @@ -128,12 +138,34 @@ namespace osu.Game.Screens.Edit /// Removes a from this . /// /// The to add. - public void Remove(HitObject hitObject) + /// True if the has been removed, false otherwise. + public bool Remove(HitObject hitObject) { - if (!mutableHitObjects.Contains(hitObject)) - return; + int index = FindIndex(hitObject); - mutableHitObjects.Remove(hitObject); + if (index == -1) + return false; + + RemoveAt(index); + return true; + } + + /// + /// Finds the index of a in this . + /// + /// The to search for. + /// The index of . + public int FindIndex(HitObject hitObject) => mutableHitObjects.IndexOf(hitObject); + + /// + /// Removes a at an index in this . + /// + /// The index of the to remove. + public void RemoveAt(int index) + { + var hitObject = (HitObject)mutableHitObjects[index]; + + mutableHitObjects.RemoveAt(index); var bindable = startTimeBindables[hitObject]; bindable.UnbindAll(); diff --git a/osu.Game/Screens/Edit/LegacyEditorBeatmapDiffer.cs b/osu.Game/Screens/Edit/LegacyEditorBeatmapDiffer.cs new file mode 100644 index 0000000000..8d2f577a1d --- /dev/null +++ b/osu.Game/Screens/Edit/LegacyEditorBeatmapDiffer.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; +using System.Collections.Generic; +using System.IO; +using DiffPlex; +using osu.Framework.Audio.Track; +using osu.Framework.Graphics.Textures; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Formats; +using osu.Game.IO; + +namespace osu.Game.Screens.Edit +{ + public class LegacyEditorBeatmapDiffer + { + private readonly EditorBeatmap editorBeatmap; + + public LegacyEditorBeatmapDiffer(EditorBeatmap editorBeatmap) + { + this.editorBeatmap = editorBeatmap; + } + + public void Patch(Stream currentState, Stream newState) + { + // Diff the beatmaps + var result = new Differ().CreateLineDiffs(readString(currentState), readString(newState), true, false); + + // Find the index of [HitObject] sections. Lines changed prior to this index are ignored. + int oldHitObjectsIndex = Array.IndexOf(result.PiecesOld, "[HitObjects]"); + int newHitObjectsIndex = Array.IndexOf(result.PiecesNew, "[HitObjects]"); + + var toRemove = new List(); + var toAdd = new List(); + + foreach (var block in result.DiffBlocks) + { + // Removed hitobject + for (int i = 0; i < block.DeleteCountA; i++) + { + int hoIndex = block.DeleteStartA + i - oldHitObjectsIndex - 1; + + if (hoIndex < 0) + continue; + + toRemove.Add(hoIndex); + } + + // Added hitobject + for (int i = 0; i < block.InsertCountB; i++) + { + int hoIndex = block.InsertStartB + i - newHitObjectsIndex - 1; + + if (hoIndex < 0) + continue; + + toAdd.Add(hoIndex); + } + } + + // Make the removal indices are sorted so that iteration order doesn't get messed up post-removal. + toRemove.Sort(); + + // Apply the changes. + for (int i = toRemove.Count - 1; i >= 0; i--) + editorBeatmap.RemoveAt(toRemove[i]); + + if (toAdd.Count > 0) + { + IBeatmap newBeatmap = readBeatmap(newState); + foreach (var i in toAdd) + editorBeatmap.Add(newBeatmap.HitObjects[i]); + } + } + + private string readString(Stream stream) + { + stream.Seek(0, SeekOrigin.Begin); + + using (var sr = new StreamReader(stream, System.Text.Encoding.UTF8, true, 1024, true)) + return sr.ReadToEnd(); + } + + private IBeatmap readBeatmap(Stream stream) + { + stream.Seek(0, SeekOrigin.Begin); + + using (var reader = new LineBufferedReader(stream, true)) + return new PassThroughWorkingBeatmap(Decoder.GetDecoder(reader).Decode(reader)).GetPlayableBeatmap(editorBeatmap.BeatmapInfo.Ruleset); + } + + private class PassThroughWorkingBeatmap : WorkingBeatmap + { + private readonly IBeatmap beatmap; + + public PassThroughWorkingBeatmap(IBeatmap beatmap) + : base(beatmap.BeatmapInfo, null) + { + this.beatmap = beatmap; + } + + protected override IBeatmap GetBeatmap() => beatmap; + + protected override Texture GetBackground() => throw new NotImplementedException(); + + protected override Track GetTrack() => throw new NotImplementedException(); + } + } +} From 14eca3655b752222b609b19f5fe268d1f467e6e1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 9 Apr 2020 21:22:07 +0900 Subject: [PATCH 0114/1117] Add change state handling to the editor --- osu.Game/Screens/Edit/Editor.cs | 30 ++++- osu.Game/Screens/Edit/EditorChangeHandler.cs | 108 ++++++++++++++++++ osu.Game/Screens/Edit/IEditorChangeHandler.cs | 33 ++++++ 3 files changed, 170 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Screens/Edit/EditorChangeHandler.cs create mode 100644 osu.Game/Screens/Edit/IEditorChangeHandler.cs diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index f1cbed57f1..14a227eb07 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -62,6 +62,7 @@ namespace osu.Game.Screens.Edit private IBeatmap playableBeatmap; private EditorBeatmap editorBeatmap; + private EditorChangeHandler changeHandler; private DependencyContainer dependencies; @@ -100,9 +101,11 @@ namespace osu.Game.Screens.Edit } AddInternal(editorBeatmap = new EditorBeatmap(playableBeatmap)); - dependencies.CacheAs(editorBeatmap); + changeHandler = new EditorChangeHandler(editorBeatmap); + dependencies.CacheAs(changeHandler); + EditorMenuBar menuBar; var fileMenuItems = new List @@ -147,6 +150,14 @@ namespace osu.Game.Screens.Edit new MenuItem("File") { Items = fileMenuItems + }, + new MenuItem("Edit") + { + Items = new[] + { + new EditorMenuItem("Undo", MenuItemType.Standard, undo), + new EditorMenuItem("Redo", MenuItemType.Standard, redo) + } } } } @@ -233,6 +244,19 @@ namespace osu.Game.Screens.Edit return true; } + break; + + case Key.Z: + if (e.ControlPressed) + { + if (e.ShiftPressed) + redo(); + else + undo(); + + return true; + } + break; } @@ -297,6 +321,10 @@ namespace osu.Game.Screens.Edit return base.OnExiting(next); } + private void undo() => changeHandler.RestoreState(-1); + + private void redo() => changeHandler.RestoreState(1); + private void resetTrack(bool seekToStart = false) { Beatmap.Value.Track?.Stop(); diff --git a/osu.Game/Screens/Edit/EditorChangeHandler.cs b/osu.Game/Screens/Edit/EditorChangeHandler.cs new file mode 100644 index 0000000000..7e372926ba --- /dev/null +++ b/osu.Game/Screens/Edit/EditorChangeHandler.cs @@ -0,0 +1,108 @@ +// 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.Text; +using osu.Game.Beatmaps.Formats; +using osu.Game.Rulesets.Objects; + +namespace osu.Game.Screens.Edit +{ + /// + /// Tracks changes to the . + /// + public class EditorChangeHandler : IEditorChangeHandler + { + private readonly LegacyEditorBeatmapDiffer differ; + private readonly List savedStates = new List(); + private int currentState = -1; + + private readonly EditorBeatmap editorBeatmap; + private int bulkChangesStarted; + private bool isRestoring; + + /// + /// Creates a new . + /// + /// The to track the s of. + public EditorChangeHandler(EditorBeatmap editorBeatmap) + { + this.editorBeatmap = editorBeatmap; + + editorBeatmap.HitObjectAdded += hitObjectAdded; + editorBeatmap.HitObjectRemoved += hitObjectRemoved; + editorBeatmap.HitObjectUpdated += hitObjectUpdated; + + differ = new LegacyEditorBeatmapDiffer(editorBeatmap); + + // Initial state. + SaveState(); + } + + private void hitObjectAdded(HitObject obj) => SaveState(); + + private void hitObjectRemoved(HitObject obj) => SaveState(); + + private void hitObjectUpdated(HitObject obj) => SaveState(); + + public void BeginChange() => bulkChangesStarted++; + + public void EndChange() + { + if (bulkChangesStarted == 0) + throw new InvalidOperationException($"Cannot call {nameof(EndChange)} without a previous call to {nameof(BeginChange)}."); + + if (--bulkChangesStarted == 0) + SaveState(); + } + + /// + /// Saves the current state. + /// + public void SaveState() + { + if (bulkChangesStarted > 0) + return; + + if (isRestoring) + return; + + var stream = new MemoryStream(); + + using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) + new LegacyBeatmapEncoder(editorBeatmap).Encode(sw); + + if (currentState < savedStates.Count - 1) + savedStates.RemoveRange(currentState + 1, savedStates.Count - currentState - 1); + + savedStates.Add(stream); + currentState = savedStates.Count - 1; + } + + /// + /// Restores an older or newer state. + /// + /// The direction to restore in. If less than 0, an older state will be used. If greater than 0, a newer state will be used. + public void RestoreState(int direction) + { + if (bulkChangesStarted > 0) + return; + + if (savedStates.Count == 0) + return; + + int newState = Math.Clamp(currentState + direction, 0, savedStates.Count - 1); + if (currentState == newState) + return; + + isRestoring = true; + + differ.Patch(savedStates[currentState], savedStates[newState]); + currentState = newState; + + isRestoring = false; + } + } +} diff --git a/osu.Game/Screens/Edit/IEditorChangeHandler.cs b/osu.Game/Screens/Edit/IEditorChangeHandler.cs new file mode 100644 index 0000000000..c1328252d4 --- /dev/null +++ b/osu.Game/Screens/Edit/IEditorChangeHandler.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 osu.Game.Rulesets.Objects; + +namespace osu.Game.Screens.Edit +{ + /// + /// Interface for a component that manages changes in the . + /// + public interface IEditorChangeHandler + { + /// + /// Begins a bulk state change event. should be invoked soon after. + /// + /// + /// This should be invoked when multiple changes to the should be bundled together into one state change event. + /// When nested invocations are involved, a state change will not occur until an equal number of invocations of are received. + /// + /// + /// When a group of s are deleted, a single undo and redo state change should update the state of all . + /// + void BeginChange(); + + /// + /// Ends a bulk state change event. + /// + /// + /// This should be invoked as soon as possible after to cause a state change. + /// + void EndChange(); + } +} From ed4ce54ac3e7149b3a6f3d4cc9406ef74d2f6e38 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 9 Apr 2020 21:56:36 +0900 Subject: [PATCH 0115/1117] Add tests --- .../Editor/TestSceneEditorChangeStates.cs | 193 ++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 osu.Game.Tests/Visual/Editor/TestSceneEditorChangeStates.cs diff --git a/osu.Game.Tests/Visual/Editor/TestSceneEditorChangeStates.cs b/osu.Game.Tests/Visual/Editor/TestSceneEditorChangeStates.cs new file mode 100644 index 0000000000..abaa373cac --- /dev/null +++ b/osu.Game.Tests/Visual/Editor/TestSceneEditorChangeStates.cs @@ -0,0 +1,193 @@ +// 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.Rulesets.Edit; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Screens.Edit; +using osuTK.Input; + +namespace osu.Game.Tests.Visual.Editor +{ + public class TestSceneEditorChangeStates : ScreenTestScene + { + private EditorBeatmap editorBeatmap; + + public override void SetUpSteps() + { + base.SetUpSteps(); + + Screens.Edit.Editor editor = null; + + AddStep("load editor", () => + { + Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); + LoadScreen(editor = new Screens.Edit.Editor()); + }); + + AddUntilStep("wait for editor to load", () => editor.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); + AddStep("get beatmap", () => editorBeatmap = editor.ChildrenOfType().Single()); + } + + [Test] + public void TestUndoFromInitialState() + { + int hitObjectCount = 0; + + AddStep("get initial state", () => hitObjectCount = editorBeatmap.HitObjects.Count); + + addUndoSteps(); + + AddAssert("no change occurred", () => hitObjectCount == editorBeatmap.HitObjects.Count); + } + + [Test] + public void TestRedoFromInitialState() + { + int hitObjectCount = 0; + + AddStep("get initial state", () => hitObjectCount = editorBeatmap.HitObjects.Count); + + addRedoSteps(); + + AddAssert("no change occurred", () => hitObjectCount == editorBeatmap.HitObjects.Count); + } + + [Test] + public void TestAddObjectAndUndo() + { + HitObject addedObject = null; + HitObject removedObject = null; + HitObject expectedObject = null; + + AddStep("bind removal", () => + { + editorBeatmap.HitObjectAdded += h => addedObject = h; + editorBeatmap.HitObjectRemoved += h => removedObject = h; + }); + + AddStep("add hitobject", () => editorBeatmap.Add(expectedObject = new HitCircle { StartTime = 1000 })); + AddAssert("hitobject added", () => addedObject == expectedObject); + + addUndoSteps(); + AddAssert("hitobject removed", () => removedObject == expectedObject); + } + + [Test] + public void TestAddObjectThenUndoThenRedo() + { + HitObject addedObject = null; + HitObject removedObject = null; + HitObject expectedObject = null; + + AddStep("bind removal", () => + { + editorBeatmap.HitObjectAdded += h => addedObject = h; + editorBeatmap.HitObjectRemoved += h => removedObject = h; + }); + + AddStep("add hitobject", () => editorBeatmap.Add(expectedObject = new HitCircle { StartTime = 1000 })); + addUndoSteps(); + + AddStep("reset variables", () => + { + addedObject = null; + removedObject = null; + }); + + addRedoSteps(); + AddAssert("hitobject added", () => addedObject.StartTime == expectedObject.StartTime); // Can't compare via equality (new hitobject instance) + AddAssert("no hitobject removed", () => removedObject == null); + } + + [Test] + public void TestRemoveObjectThenUndo() + { + HitObject addedObject = null; + HitObject removedObject = null; + HitObject expectedObject = null; + + AddStep("bind removal", () => + { + editorBeatmap.HitObjectAdded += h => addedObject = h; + editorBeatmap.HitObjectRemoved += h => removedObject = h; + }); + + AddStep("add hitobject", () => editorBeatmap.Add(expectedObject = new HitCircle { StartTime = 1000 })); + AddStep("remove object", () => editorBeatmap.Remove(expectedObject)); + AddStep("reset variables", () => + { + addedObject = null; + removedObject = null; + }); + + addUndoSteps(); + AddAssert("hitobject added", () => addedObject.StartTime == expectedObject.StartTime); // Can't compare via equality (new hitobject instance) + AddAssert("no hitobject removed", () => removedObject == null); + } + + [Test] + public void TestRemoveObjectThenUndoThenRedo() + { + HitObject addedObject = null; + HitObject removedObject = null; + HitObject expectedObject = null; + + AddStep("bind removal", () => + { + editorBeatmap.HitObjectAdded += h => addedObject = h; + editorBeatmap.HitObjectRemoved += h => removedObject = h; + }); + + AddStep("add hitobject", () => editorBeatmap.Add(expectedObject = new HitCircle { StartTime = 1000 })); + AddStep("remove object", () => editorBeatmap.Remove(expectedObject)); + addUndoSteps(); + + AddStep("reset variables", () => + { + addedObject = null; + removedObject = null; + }); + + addRedoSteps(); + AddAssert("hitobject removed", () => removedObject.StartTime == expectedObject.StartTime); // Can't compare via equality (new hitobject instance after undo) + AddAssert("no hitobject added", () => addedObject == null); + } + + private void addUndoSteps() + { + AddStep("press undo", () => + { + InputManager.PressKey(Key.LControl); + InputManager.PressKey(Key.Z); + }); + + AddStep("release keys", () => + { + InputManager.ReleaseKey(Key.LControl); + InputManager.ReleaseKey(Key.Z); + }); + } + + private void addRedoSteps() + { + AddStep("press redo", () => + { + InputManager.PressKey(Key.LControl); + InputManager.PressKey(Key.LShift); + InputManager.PressKey(Key.Z); + }); + + AddStep("release keys", () => + { + InputManager.ReleaseKey(Key.LControl); + InputManager.ReleaseKey(Key.LShift); + InputManager.ReleaseKey(Key.Z); + }); + } + } +} From 575b061dd76a59fd39079904f53193bb524ea261 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 9 Apr 2020 22:00:56 +0900 Subject: [PATCH 0116/1117] Add change state support to more editor components --- .../Components/PathControlPointPiece.cs | 17 +++++++++++++++- .../Sliders/SliderSelectionBlueprint.cs | 20 +++++++++++++++++-- .../Compose/Components/BlueprintContainer.cs | 14 +++++++++++++ .../Compose/Components/SelectionHandler.cs | 9 ++++++++- .../Timeline/TimelineHitObjectBlueprint.cs | 12 +++++++++-- 5 files changed, 66 insertions(+), 6 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 af4da5e853..092a13cca5 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -12,6 +12,7 @@ using osu.Game.Graphics; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Screens.Edit; using osuTK; using osuTK.Graphics; using osuTK.Input; @@ -33,6 +34,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components private readonly Container marker; private readonly Drawable markerRing; + [Resolved(CanBeNull = true)] + private IEditorChangeHandler changeHandler { get; set; } + [Resolved(CanBeNull = true)] private IDistanceSnapProvider snapProvider { get; set; } @@ -137,7 +141,16 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components protected override bool OnClick(ClickEvent e) => RequestSelection != null; - protected override bool OnDragStart(DragStartEvent e) => e.Button == MouseButton.Left; + protected override bool OnDragStart(DragStartEvent e) + { + if (e.Button == MouseButton.Left) + { + changeHandler?.BeginChange(); + return true; + } + + return false; + } protected override void OnDrag(DragEvent e) { @@ -158,6 +171,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components ControlPoint.Position.Value += e.Delta; } + protected override void OnDragEnd(DragEndEvent e) => changeHandler?.EndChange(); + /// /// Updates the state of the circular control point marker. /// diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs index 001100d3ce..b7074b7ee5 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs @@ -38,6 +38,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders [Resolved(CanBeNull = true)] private EditorBeatmap editorBeatmap { get; set; } + [Resolved(CanBeNull = true)] + private IEditorChangeHandler changeHandler { get; set; } + public SliderSelectionBlueprint(DrawableSlider slider) : base(slider) { @@ -92,7 +95,16 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private int? placementControlPointIndex; - protected override bool OnDragStart(DragStartEvent e) => placementControlPointIndex != null; + protected override bool OnDragStart(DragStartEvent e) + { + if (placementControlPointIndex != null) + { + changeHandler?.BeginChange(); + return true; + } + + return false; + } protected override void OnDrag(DragEvent e) { @@ -103,7 +115,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders protected override void OnDragEnd(DragEndEvent e) { - placementControlPointIndex = null; + if (placementControlPointIndex != null) + { + placementControlPointIndex = null; + changeHandler?.EndChange(); + } } private BindableList controlPoints => HitObject.Path.ControlPoints; diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index c81c6059cc..ad16e22e5e 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -37,6 +37,9 @@ namespace osu.Game.Screens.Edit.Compose.Components private SelectionHandler selectionHandler; + [Resolved(CanBeNull = true)] + private IEditorChangeHandler changeHandler { get; set; } + [Resolved] private IAdjustableClock adjustableClock { get; set; } @@ -164,7 +167,11 @@ namespace osu.Game.Screens.Edit.Compose.Components return false; if (movementBlueprint != null) + { + isDraggingBlueprint = true; + changeHandler?.BeginChange(); return true; + } if (DragBox.HandleDrag(e)) { @@ -191,6 +198,12 @@ namespace osu.Game.Screens.Edit.Compose.Components if (e.Button == MouseButton.Right) return; + if (isDraggingBlueprint) + { + changeHandler?.EndChange(); + isDraggingBlueprint = false; + } + if (DragBox.State == Visibility.Visible) { DragBox.Hide(); @@ -354,6 +367,7 @@ namespace osu.Game.Screens.Edit.Compose.Components private Vector2? movementBlueprintOriginalPosition; private SelectionBlueprint movementBlueprint; + private bool isDraggingBlueprint; /// /// Attempts to begin the movement of any selected blueprints. diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index fc46bf3fed..e212979433 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -40,6 +40,9 @@ namespace osu.Game.Screens.Edit.Compose.Components [Resolved(CanBeNull = true)] private EditorBeatmap editorBeatmap { get; set; } + [Resolved(CanBeNull = true)] + private IEditorChangeHandler changeHandler { get; set; } + public SelectionHandler() { selectedBlueprints = new List(); @@ -152,8 +155,12 @@ namespace osu.Game.Screens.Edit.Compose.Components private void deleteSelected() { + changeHandler?.BeginChange(); + foreach (var h in selectedBlueprints.ToList()) - editorBeatmap.Remove(h.HitObject); + editorBeatmap?.Remove(h.HitObject); + + changeHandler?.EndChange(); } #endregion diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index 8f12c2f0ed..16ba3ba89a 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -254,14 +254,21 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline Colour = IsHovered || hasMouseDown ? Color4.OrangeRed : Color4.White; } - protected override bool OnDragStart(DragStartEvent e) => true; - [Resolved] private EditorBeatmap beatmap { get; set; } [Resolved] private IBeatSnapProvider beatSnapProvider { get; set; } + [Resolved(CanBeNull = true)] + private IEditorChangeHandler changeHandler { get; set; } + + protected override bool OnDragStart(DragStartEvent e) + { + changeHandler?.BeginChange(); + return true; + } + protected override void OnDrag(DragEvent e) { base.OnDrag(e); @@ -301,6 +308,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline base.OnDragEnd(e); OnDragHandled?.Invoke(null); + changeHandler?.EndChange(); } } } From e208251fc6f1729eac62cfa2e7e756b651eedbc4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 9 Apr 2020 23:18:43 +0900 Subject: [PATCH 0117/1117] Wait for timeline to also load --- osu.Game.Tests/Visual/Editor/TestSceneEditorChangeStates.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Editor/TestSceneEditorChangeStates.cs b/osu.Game.Tests/Visual/Editor/TestSceneEditorChangeStates.cs index abaa373cac..dd1b6cf6aa 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneEditorChangeStates.cs +++ b/osu.Game.Tests/Visual/Editor/TestSceneEditorChangeStates.cs @@ -9,6 +9,7 @@ using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Screens.Edit; +using osu.Game.Screens.Edit.Compose.Components.Timeline; using osuTK.Input; namespace osu.Game.Tests.Visual.Editor @@ -29,7 +30,8 @@ namespace osu.Game.Tests.Visual.Editor LoadScreen(editor = new Screens.Edit.Editor()); }); - AddUntilStep("wait for editor to load", () => editor.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); + AddUntilStep("wait for editor to load", () => editor.ChildrenOfType().FirstOrDefault()?.IsLoaded == true + && editor.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); AddStep("get beatmap", () => editorBeatmap = editor.ChildrenOfType().Single()); } From f40bdcd34e835731855d48b1ddf9af4574331320 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Thu, 9 Apr 2020 18:47:28 +0300 Subject: [PATCH 0118/1117] Initial rewrite, moving API logic to SongSelect --- osu.Game/Screens/Select/BeatmapCarousel.cs | 35 +++------- .../Select/Carousel/CarouselBeatmapSet.cs | 16 ++--- osu.Game/Screens/Select/SongSelect.cs | 65 +++++++++++++++++++ 3 files changed, 79 insertions(+), 37 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index c7221699de..5a62184ad8 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -34,8 +34,6 @@ namespace osu.Game.Screens.Select private const float bleed_top = FilterControl.HEIGHT; private const float bleed_bottom = Footer.HEIGHT; - protected readonly Bindable RecommendedStarDifficulty = new Bindable(); - /// /// Triggered when the loaded change and are completely loaded. /// @@ -122,6 +120,7 @@ namespace osu.Game.Screens.Select protected List Items = new List(); private CarouselRoot root; + public SongSelect.DifficultyRecommender DifficultyRecommender; public BeatmapCarousel() { @@ -145,10 +144,10 @@ namespace osu.Game.Screens.Select private BeatmapManager beatmaps { get; set; } [Resolved] - private IAPIProvider api { get; set; } + private Bindable decoupledRuleset { get; set; } [BackgroundDependencyLoader(permitNulls: true)] - private void load(OsuConfigManager config, Bindable decoupledRuleset) + private void load(OsuConfigManager config) { config.BindWith(OsuSetting.RandomSelectAlgorithm, RandomAlgorithm); config.BindWith(OsuSetting.SongSelectRightMouseScroll, RightClickScrollingEnabled); @@ -162,27 +161,6 @@ namespace osu.Game.Screens.Select beatmaps.BeatmapRestored += beatmapRestored; loadBeatmapSets(GetLoadableBeatmaps()); - - decoupledRuleset.BindValueChanged(UpdateRecommendedStarDifficulty, true); - } - - protected void UpdateRecommendedStarDifficulty(ValueChangedEvent ruleset) - { - if (api.LocalUser.Value is GuestUser) - { - RecommendedStarDifficulty.Value = 0; - return; - } - - var req = new GetUserRequest(api.LocalUser.Value.Id, ruleset.NewValue); - - req.Success += result => - { - // algorithm taken from https://github.com/ppy/osu-web/blob/e6e2825516449e3d0f3f5e1852c6bdd3428c3437/app/Models/User.php#L1505 - RecommendedStarDifficulty.Value = Math.Pow((double)(result.Statistics.PP ?? 0), 0.4) * 0.195; - }; - - api.PerformAsync(req); } protected virtual IEnumerable GetLoadableBeatmaps() => beatmaps.GetAllUsableBeatmapSetsEnumerable(); @@ -608,7 +586,12 @@ namespace osu.Game.Screens.Select b.Metadata = beatmapSet.Metadata; } - var set = new CarouselBeatmapSet(beatmapSet, RecommendedStarDifficulty); + BeatmapInfo recommender(IEnumerable beatmaps) + { + return DifficultyRecommender?.GetRecommendedBeatmap(beatmaps, decoupledRuleset.Value); + } + + var set = new CarouselBeatmapSet(beatmapSet, recommender); foreach (var c in set.Beatmaps) { diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs index 064840d99a..1b715cad02 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs @@ -13,13 +13,13 @@ namespace osu.Game.Screens.Select.Carousel { public class CarouselBeatmapSet : CarouselGroupEagerSelect { - private readonly Bindable recommendedStarDifficulty = new Bindable(); + private Func, BeatmapInfo> getRecommendedBeatmap; public IEnumerable Beatmaps => InternalChildren.OfType(); public BeatmapSetInfo BeatmapSet; - public CarouselBeatmapSet(BeatmapSetInfo beatmapSet, Bindable recommendedStarDifficulty) + public CarouselBeatmapSet(BeatmapSetInfo beatmapSet, Func, BeatmapInfo> getRecommendedBeatmap) { BeatmapSet = beatmapSet ?? throw new ArgumentNullException(nameof(beatmapSet)); @@ -28,7 +28,7 @@ namespace osu.Game.Screens.Select.Carousel .Select(b => new CarouselBeatmap(b)) .ForEach(AddChild); - this.recommendedStarDifficulty.BindTo(recommendedStarDifficulty); + this.getRecommendedBeatmap = getRecommendedBeatmap; } protected override DrawableCarouselItem CreateDrawableRepresentation() => new DrawableCarouselBeatmapSet(this); @@ -37,14 +37,8 @@ namespace osu.Game.Screens.Select.Carousel { if (LastSelected == null) { - return Children.OfType() - .Where(b => !b.Filtered.Value) - .OrderBy(b => - { - var difference = b.Beatmap.StarDifficulty - recommendedStarDifficulty.Value; - return difference >= 0 ? difference * 2 : difference * -1; // prefer easier over harder - }) - .FirstOrDefault(); + var recommendedBeatmapInfo = getRecommendedBeatmap(Children.OfType().Where(b => !b.Filtered.Value).Select(b => b.Beatmap)); + return Children.OfType().Where(b => b.Beatmap == recommendedBeatmapInfo).First(); } return base.GetNextToSelect(); diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 895a8ad0c9..fda5872f3b 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -36,6 +36,9 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; using osu.Game.Overlays.Notifications; using osu.Game.Scoring; +using osu.Game.Online.API; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Game.Online.API.Requests; namespace osu.Game.Screens.Select { @@ -80,6 +83,7 @@ namespace osu.Game.Screens.Select protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap.Value); protected BeatmapCarousel Carousel { get; private set; } + private DifficultyRecommender difficultyRecommender; private BeatmapInfoWedge beatmapInfoWedge; private DialogOverlay dialogOverlay; @@ -107,6 +111,8 @@ namespace osu.Game.Screens.Select // initial value transfer is required for FilterControl (it uses our re-cached bindables in its async load for the initial filter). transferRulesetValue(); + AddInternal(difficultyRecommender = new DifficultyRecommender()); + AddRangeInternal(new Drawable[] { new ResetScrollContainer(() => Carousel.ScrollToSelected()) @@ -156,6 +162,7 @@ namespace osu.Game.Screens.Select RelativeSizeAxes = Axes.Both, SelectionChanged = updateSelectedBeatmap, BeatmapSetsChanged = carouselBeatmapsLoaded, + DifficultyRecommender = difficultyRecommender, }, } }, @@ -780,6 +787,64 @@ namespace osu.Game.Screens.Select return base.OnKeyDown(e); } + public class DifficultyRecommender : Component + { + [Resolved] + private IAPIProvider api { get; set; } + + [Resolved] + private RulesetStore rulesets { get; set; } + + private Dictionary recommendedStarDifficulty = new Dictionary(); + + private int pendingAPIRequests = 0; + + [BackgroundDependencyLoader] + private void load() + { + updateRecommended(); + } + + private void updateRecommended() + { + if (pendingAPIRequests > 0) + return; + if (api.LocalUser.Value is GuestUser) + return; + rulesets.AvailableRulesets.ForEach(rulesetInfo => + { + var req = new GetUserRequest(api.LocalUser.Value.Id, rulesetInfo); + + req.Success += result => + { + // algorithm taken from https://github.com/ppy/osu-web/blob/e6e2825516449e3d0f3f5e1852c6bdd3428c3437/app/Models/User.php#L1505 + recommendedStarDifficulty[rulesetInfo] = Math.Pow((double)(result.Statistics.PP ?? 0), 0.4) * 0.195; + pendingAPIRequests--; + }; + + req.Failure += _ => pendingAPIRequests--; + + pendingAPIRequests++; + api.Queue(req); + }); + } + + public BeatmapInfo GetRecommendedBeatmap(IEnumerable beatmaps, RulesetInfo currentRuleset) + { + if (!recommendedStarDifficulty.ContainsKey(currentRuleset)) + { + updateRecommended(); + return null; + } + + return beatmaps.OrderBy(b => + { + var difference = b.StarDifficulty - recommendedStarDifficulty[currentRuleset]; + return difference >= 0 ? difference * 2 : difference * -1; // prefer easier over harder + }).FirstOrDefault(); + } + } + private class VerticalMaskingContainer : Container { private const float panel_overflow = 1.2f; From caa404f8fa6024b1fadff3d61ad46339bc50c34f Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Thu, 9 Apr 2020 18:48:13 +0300 Subject: [PATCH 0119/1117] Remove test for now --- .../SongSelect/TestSceneBeatmapCarousel.cs | 30 ------------------- 1 file changed, 30 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs index 5a199885c0..8d207be216 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs @@ -656,34 +656,6 @@ namespace osu.Game.Tests.Visual.SongSelect checkVisibleItemCount(true, 15); } - [Test] - public void TestSelectRecommendedDifficulty() - { - void setRecommendedAndExpect(double recommended, int expectedSet, int expectedDiff) - { - AddStep($"Recommend SR {recommended}", () => carousel.RecommendedStarDifficulty.Value = recommended); - advanceSelection(direction: 1, diff: false); - waitForSelection(expectedSet, expectedDiff); - } - - createCarousel(); - AddStep("Add beatmaps", () => - { - for (int i = 1; i <= 7; i++) - { - var set = createTestBeatmapSet(i); - carousel.UpdateBeatmapSet(set); - } - }); - waitForSelection(1, 1); - setRecommendedAndExpect(1, 2, 1); - setRecommendedAndExpect(3.9, 3, 1); - setRecommendedAndExpect(4.1, 4, 2); - setRecommendedAndExpect(5.6, 5, 2); - setRecommendedAndExpect(5.7, 6, 3); - setRecommendedAndExpect(10, 7, 3); - } - private void loadBeatmaps(List beatmapSets = null, Func initialCriteria = null) { createCarousel(); @@ -886,8 +858,6 @@ namespace osu.Game.Tests.Visual.SongSelect { public new List Items => base.Items; - public new Bindable RecommendedStarDifficulty => base.RecommendedStarDifficulty; - public bool PendingFilterTask => PendingFilter != null; protected override IEnumerable GetLoadableBeatmaps() => Enumerable.Empty(); From 35f97dfc7521a7952dad16896babc6cc649353c0 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Thu, 9 Apr 2020 18:59:18 +0300 Subject: [PATCH 0120/1117] Style changes --- osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs | 1 - osu.Game/Screens/Select/BeatmapCarousel.cs | 2 -- osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs | 5 ++--- osu.Game/Screens/Select/SongSelect.cs | 5 +++-- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs index 8d207be216..76a8ee9914 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs @@ -8,7 +8,6 @@ using System.Linq; using System.Text; using NUnit.Framework; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 5a62184ad8..3e619a1f80 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -23,9 +23,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Cursor; using osu.Game.Input.Bindings; using osu.Game.Screens.Select.Carousel; -using osu.Game.Online.API; using osu.Game.Rulesets; -using osu.Game.Online.API.Requests; namespace osu.Game.Screens.Select { diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs index 1b715cad02..e7a18e15c7 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; -using osu.Framework.Bindables; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Game.Beatmaps; using osu.Game.Screens.Select.Filter; @@ -13,7 +12,7 @@ namespace osu.Game.Screens.Select.Carousel { public class CarouselBeatmapSet : CarouselGroupEagerSelect { - private Func, BeatmapInfo> getRecommendedBeatmap; + private readonly Func, BeatmapInfo> getRecommendedBeatmap; public IEnumerable Beatmaps => InternalChildren.OfType(); @@ -38,7 +37,7 @@ namespace osu.Game.Screens.Select.Carousel if (LastSelected == null) { var recommendedBeatmapInfo = getRecommendedBeatmap(Children.OfType().Where(b => !b.Filtered.Value).Select(b => b.Beatmap)); - return Children.OfType().Where(b => b.Beatmap == recommendedBeatmapInfo).First(); + return Children.OfType().First(b => b.Beatmap == recommendedBeatmapInfo); } return base.GetNextToSelect(); diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index fda5872f3b..d6bc20df39 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -795,9 +795,9 @@ namespace osu.Game.Screens.Select [Resolved] private RulesetStore rulesets { get; set; } - private Dictionary recommendedStarDifficulty = new Dictionary(); + private readonly Dictionary recommendedStarDifficulty = new Dictionary(); - private int pendingAPIRequests = 0; + private int pendingAPIRequests; [BackgroundDependencyLoader] private void load() @@ -811,6 +811,7 @@ namespace osu.Game.Screens.Select return; if (api.LocalUser.Value is GuestUser) return; + rulesets.AvailableRulesets.ForEach(rulesetInfo => { var req = new GetUserRequest(api.LocalUser.Value.Id, rulesetInfo); From 2201e9b4ae00154d14a4b0ccb45b881550c7bdfb Mon Sep 17 00:00:00 2001 From: Fire937 Date: Thu, 9 Apr 2020 18:12:15 +0200 Subject: [PATCH 0121/1117] Add stereo shifted hitsound playback support There is now a setting in the general settings called "Positional hitsounds". If the setting is enabled, the hitsounds playback will be shifted according to their position on the beatmap. --- osu.Game/Configuration/OsuConfigManager.cs | 2 ++ .../Sections/Gameplay/GeneralSettings.cs | 5 ++++ .../Objects/Drawables/DrawableHitObject.cs | 26 ++++++++++++++++++- 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 41f6747b74..ab5a652a94 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -88,6 +88,7 @@ namespace osu.Game.Configuration Set(OsuSetting.ShowProgressGraph, true); Set(OsuSetting.ShowHealthDisplayWhenCantFail, true); Set(OsuSetting.KeyOverlay, false); + Set(OsuSetting.PositionalHitSounds, true); Set(OsuSetting.ScoreMeter, ScoreMeterType.HitErrorBoth); Set(OsuSetting.FloatingComments, false); @@ -176,6 +177,7 @@ namespace osu.Game.Configuration LightenDuringBreaks, ShowStoryboard, KeyOverlay, + PositionalHitSounds, ScoreMeter, FloatingComments, ShowInterface, diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs index 2d2cd42213..ef03c0622a 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs @@ -57,6 +57,11 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay LabelText = "Always show key overlay", Bindable = config.GetBindable(OsuSetting.KeyOverlay) }, + new SettingsCheckbox + { + LabelText = "Positional hitsounds", + Bindable = config.GetBindable(OsuSetting.PositionalHitSounds) + }, new SettingsEnumDropdown { LabelText = "Score meter type", diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 0011faefbb..ed9efba89f 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -12,11 +12,13 @@ using osu.Framework.Extensions.TypeExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Primitives; using osu.Framework.Threading; +using osu.Framework.Audio; using osu.Game.Audio; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Scoring; using osu.Game.Skinning; +using osu.Game.Configuration; using osuTK.Graphics; namespace osu.Game.Rulesets.Objects.Drawables @@ -31,6 +33,11 @@ namespace osu.Game.Rulesets.Objects.Drawables /// public readonly Bindable AccentColour = new Bindable(Color4.Gray); + /// + /// The stereo balance of the samples if the Positional hitsounds setting is set. + /// + private readonly BindableDouble positionalSoundAdjustment = new BindableDouble(); + protected SkinnableSound Samples { get; private set; } protected virtual IEnumerable GetSamples() => HitObject.Samples; @@ -84,8 +91,14 @@ namespace osu.Game.Rulesets.Objects.Drawables /// public JudgementResult Result { get; private set; } + /// + /// The stereo balance of the samples played if Positional hitsounds is set. + /// + protected virtual float PositionalSound => (HitObject is IHasXPosition position) ? (position.X / 512f - 0.5f) * 0.8f : 0; + private BindableList samplesBindable; private Bindable startTimeBindable; + private Bindable userPositionalHitSounds; private Bindable comboIndexBindable; public override bool RemoveWhenNotAlive => false; @@ -101,10 +114,11 @@ namespace osu.Game.Rulesets.Objects.Drawables protected DrawableHitObject([NotNull] HitObject hitObject) { HitObject = hitObject ?? throw new ArgumentNullException(nameof(hitObject)); + positionalSoundAdjustment.Value = PositionalSound; } [BackgroundDependencyLoader] - private void load() + private void load(OsuConfigManager config) { var judgement = HitObject.CreateJudgement(); @@ -113,6 +127,16 @@ namespace osu.Game.Rulesets.Objects.Drawables throw new InvalidOperationException($"{GetType().ReadableName()} must provide a {nameof(JudgementResult)} through {nameof(CreateResult)}."); loadSamples(); + + userPositionalHitSounds = config.GetBindable(OsuSetting.PositionalHitSounds); + userPositionalHitSounds.BindValueChanged(positional => + { + if (positional.NewValue) + Samples?.AddAdjustment(AdjustableProperty.Balance, positionalSoundAdjustment); + else + Samples?.RemoveAdjustment(AdjustableProperty.Balance, positionalSoundAdjustment); + }); + userPositionalHitSounds.TriggerChange(); } protected override void LoadComplete() From deaf24f1419f25f26297271e819caf73445bac4b Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Thu, 9 Apr 2020 19:30:40 +0300 Subject: [PATCH 0122/1117] Fix oversight on null --- osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs index e7a18e15c7..99ded4c58e 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs @@ -37,7 +37,8 @@ namespace osu.Game.Screens.Select.Carousel if (LastSelected == null) { var recommendedBeatmapInfo = getRecommendedBeatmap(Children.OfType().Where(b => !b.Filtered.Value).Select(b => b.Beatmap)); - return Children.OfType().First(b => b.Beatmap == recommendedBeatmapInfo); + if (recommendedBeatmapInfo != null) + return Children.OfType().First(b => b.Beatmap == recommendedBeatmapInfo); } return base.GetNextToSelect(); From f115fecb23c13b7a2fbfc99e38b1eef458866199 Mon Sep 17 00:00:00 2001 From: Alchyr Date: Thu, 9 Apr 2020 09:34:40 -0700 Subject: [PATCH 0123/1117] Fix formatting --- osu.Game/Beatmaps/ControlPoints/ControlPoint.cs | 6 +++--- osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs | 3 ++- osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs | 3 ++- osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs | 3 ++- osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs | 6 +++--- 5 files changed, 12 insertions(+), 9 deletions(-) diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs index 411a4441de..9599ad184b 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs @@ -28,10 +28,10 @@ namespace osu.Game.Beatmaps.ControlPoints /// /// Whether this control point results in a meaningful change when placed after another. /// - /// Another control point to compare with. - /// The time this timing point will be placed at. + /// An existing control point to compare with. + /// The time this control point will be placed at if it is added. /// Whether redundant. - public abstract bool IsRedundant(ControlPoint other, double time); + public abstract bool IsRedundant(ControlPoint existing, double time); public bool Equals(ControlPoint other) => Time == other?.Time && EquivalentTo(other); } diff --git a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs index 44522dc927..dc856b0a0a 100644 --- a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs @@ -29,6 +29,7 @@ namespace osu.Game.Beatmaps.ControlPoints public override bool EquivalentTo(ControlPoint other) => other is DifficultyControlPoint otherTyped && otherTyped.SpeedMultiplier.Equals(SpeedMultiplier); - public override bool IsRedundant(ControlPoint other, double time) => EquivalentTo(other); + + public override bool IsRedundant(ControlPoint existing, double time) => EquivalentTo(existing); } } diff --git a/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs index 8066c6b577..d050f44ba4 100644 --- a/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs @@ -38,6 +38,7 @@ namespace osu.Game.Beatmaps.ControlPoints public override bool EquivalentTo(ControlPoint other) => other is EffectControlPoint otherTyped && KiaiMode == otherTyped.KiaiMode && OmitFirstBarLine == otherTyped.OmitFirstBarLine; - public override bool IsRedundant(ControlPoint other, double time) => !OmitFirstBarLine && EquivalentTo(other); + + public override bool IsRedundant(ControlPoint existing, double time) => !OmitFirstBarLine && EquivalentTo(existing); } } diff --git a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs index cf7c842b24..38edbe70da 100644 --- a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs @@ -71,6 +71,7 @@ namespace osu.Game.Beatmaps.ControlPoints public override bool EquivalentTo(ControlPoint other) => other is SampleControlPoint otherTyped && SampleBank == otherTyped.SampleBank && SampleVolume == otherTyped.SampleVolume; - public override bool IsRedundant(ControlPoint other, double time) => EquivalentTo(other); + + public override bool IsRedundant(ControlPoint existing, double time) => EquivalentTo(existing); } } diff --git a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs index d14ac1221b..316c603ece 100644 --- a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs @@ -52,8 +52,8 @@ namespace osu.Game.Beatmaps.ControlPoints other is TimingControlPoint otherTyped && TimeSignature == otherTyped.TimeSignature && BeatLength.Equals(otherTyped.BeatLength); - public override bool IsRedundant(ControlPoint other, double time) => - EquivalentTo(other) - && other.Time == time; + public override bool IsRedundant(ControlPoint existing, double time) => + EquivalentTo(existing) + && existing.Time == time; } } From 518acf03e9b8319a0e533652f7cacadc7a2afa96 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 9 Apr 2020 19:41:35 +0300 Subject: [PATCH 0124/1117] Remove BeatmapSearchSmallFilterRow component --- .../TestSceneBeatmapSearchFilter.cs | 5 ++- .../BeatmapListingSearchSection.cs | 8 ++--- .../BeatmapListing/BeatmapSearchFilterRow.cs | 5 +-- .../BeatmapSearchSmallFilterRow.cs | 32 ------------------- 4 files changed, 9 insertions(+), 41 deletions(-) delete mode 100644 osu.Game/Overlays/BeatmapListing/BeatmapSearchSmallFilterRow.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapSearchFilter.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapSearchFilter.cs index 7b4424e568..fac58a6754 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapSearchFilter.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapSearchFilter.cs @@ -20,8 +20,7 @@ namespace osu.Game.Tests.Visual.UserInterface public override IReadOnlyList RequiredTypes => new[] { typeof(BeatmapSearchFilterRow<>), - typeof(BeatmapSearchRulesetFilterRow), - typeof(BeatmapSearchSmallFilterRow<>), + typeof(BeatmapSearchRulesetFilterRow) }; [Cached] @@ -43,7 +42,7 @@ namespace osu.Game.Tests.Visual.UserInterface { new BeatmapSearchRulesetFilterRow(), new BeatmapSearchFilterRow("Categories"), - new BeatmapSearchSmallFilterRow("Header Name") + new BeatmapSearchFilterRow("Header Name") } }); } diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchSection.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchSection.cs index 501abbf2c8..3f9cc211df 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchSection.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchSection.cs @@ -47,8 +47,8 @@ namespace osu.Game.Overlays.BeatmapListing private readonly BeatmapSearchTextBox textBox; private readonly BeatmapSearchRulesetFilterRow modeFilter; private readonly BeatmapSearchFilterRow categoryFilter; - private readonly BeatmapSearchSmallFilterRow genreFilter; - private readonly BeatmapSearchSmallFilterRow languageFilter; + private readonly BeatmapSearchFilterRow genreFilter; + private readonly BeatmapSearchFilterRow languageFilter; private readonly Box background; private readonly UpdateableBeatmapSetCover beatmapCover; @@ -104,8 +104,8 @@ namespace osu.Game.Overlays.BeatmapListing { modeFilter = new BeatmapSearchRulesetFilterRow(), categoryFilter = new BeatmapSearchFilterRow(@"Categories"), - genreFilter = new BeatmapSearchSmallFilterRow(@"Genre"), - languageFilter = new BeatmapSearchSmallFilterRow(@"Language"), + genreFilter = new BeatmapSearchFilterRow(@"Genre"), + languageFilter = new BeatmapSearchFilterRow(@"Language"), } } } diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapSearchFilterRow.cs b/osu.Game/Overlays/BeatmapListing/BeatmapSearchFilterRow.cs index 467399dd20..bc0a011e31 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapSearchFilterRow.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapSearchFilterRow.cs @@ -17,6 +17,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Online.API.Requests; using osuTK; using osuTK.Graphics; +using Humanizer; namespace osu.Game.Overlays.BeatmapListing { @@ -55,8 +56,8 @@ namespace osu.Game.Overlays.BeatmapListing { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, - Font = OsuFont.GetFont(size: 10), - Text = headerName.ToUpper() + Font = OsuFont.GetFont(size: 13), + Text = headerName.Titleize() }, CreateFilter().With(f => { diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapSearchSmallFilterRow.cs b/osu.Game/Overlays/BeatmapListing/BeatmapSearchSmallFilterRow.cs deleted file mode 100644 index 6daa7cb0e0..0000000000 --- a/osu.Game/Overlays/BeatmapListing/BeatmapSearchSmallFilterRow.cs +++ /dev/null @@ -1,32 +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.UserInterface; - -namespace osu.Game.Overlays.BeatmapListing -{ - public class BeatmapSearchSmallFilterRow : BeatmapSearchFilterRow - { - public BeatmapSearchSmallFilterRow(string headerName) - : base(headerName) - { - } - - protected override BeatmapSearchFilter CreateFilter() => new SmallBeatmapSearchFilter(); - - private class SmallBeatmapSearchFilter : BeatmapSearchFilter - { - protected override TabItem CreateTabItem(T value) => new SmallTabItem(value); - - private class SmallTabItem : FilterTabItem - { - public SmallTabItem(T value) - : base(value) - { - } - - protected override float TextSize => 10; - } - } - } -} From 91b3aa2914427cfa0af3cd1dbc58ea4b25ad9919 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 9 Apr 2020 22:56:10 +0300 Subject: [PATCH 0125/1117] Implement interval list --- osu.Game/Lists/IntervalList.cs | 128 +++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 osu.Game/Lists/IntervalList.cs diff --git a/osu.Game/Lists/IntervalList.cs b/osu.Game/Lists/IntervalList.cs new file mode 100644 index 0000000000..493b6b6e72 --- /dev/null +++ b/osu.Game/Lists/IntervalList.cs @@ -0,0 +1,128 @@ +// 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; +using System.Collections.Generic; +using osu.Framework.Lists; + +namespace osu.Game.Lists +{ + /// + /// Represents a list of intervals that can be used for whether a specific value falls into one of them. + /// + /// The type of interval values. + public class IntervalList : ICollection>, IReadOnlyList> + { + private static readonly IComparer type_comparer = Comparer.Default; + + private readonly SortedList> intervals = new SortedList>((x, y) => type_comparer.Compare(x.Start, y.Start)); + + /// + /// The index of the nearest interval from last call. + /// + protected int NearestIntervalIndex; + + /// + /// Whether the provided value is in any interval added to this list. + /// + /// The value to check for. + public bool IsInAnyInterval(T value) + { + if (intervals.Count == 0) + return false; + + // Clamp the nearest index in case there were intervals + // removed from the list causing the index to go out of range. + NearestIntervalIndex = Math.Clamp(NearestIntervalIndex, 0, Count - 1); + + if (type_comparer.Compare(value, this[NearestIntervalIndex].End) > 0) + { + while (type_comparer.Compare(value, this[NearestIntervalIndex].End) > 0 && NearestIntervalIndex < Count - 1) + NearestIntervalIndex++; + } + else + { + while (type_comparer.Compare(value, this[NearestIntervalIndex].Start) < 0 && NearestIntervalIndex > 0) + NearestIntervalIndex--; + } + + var nearestInterval = this[NearestIntervalIndex]; + + return type_comparer.Compare(value, nearestInterval.Start) >= 0 && + type_comparer.Compare(value, nearestInterval.End) <= 0; + } + + /// + /// Adds a new interval to the list. + /// + /// The start value of the interval. + /// The end value of the interval. + public void Add(T start, T end) => Add(new Interval(start, end)); + + #region ICollection> + + public int Count => intervals.Count; + + bool ICollection>.IsReadOnly => false; + + /// + /// Adds a new interval to the list + /// + /// The interval to add. + public void Add(Interval interval) => intervals.Add(interval); + + /// + /// Removes an existing interval from the list. + /// + /// The interval to remove. + /// Whether the provided interval exists in the list and has been removed. + public bool Remove(Interval interval) => intervals.Remove(interval); + + /// + /// Removes all intervals from the list. + /// + public void Clear() => intervals.Clear(); + + public void CopyTo(Interval[] array, int arrayIndex) => intervals.CopyTo(array, arrayIndex); + + public bool Contains(Interval item) => intervals.Contains(item); + + public IEnumerator> GetEnumerator() => intervals.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + #endregion + + #region IReadOnlyList> + + public Interval this[int index] + { + get => intervals[index]; + set => intervals[index] = value; + } + + #endregion + } + + public readonly struct Interval + { + /// + /// The start value of this interval. + /// + public readonly T Start; + + /// + /// The end value of this interval. + /// + public readonly T End; + + public Interval(T start, T end) + { + bool startLessThanEnd = Comparer.Default.Compare(start, end) < 0; + + Start = startLessThanEnd ? start : end; + End = startLessThanEnd ? end : start; + } + } +} From 38ee5f310302e3d05b240c4d55e1bb4e1bd5f8e7 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 9 Apr 2020 22:57:54 +0300 Subject: [PATCH 0126/1117] Add tests covering most cases of interval list checking --- osu.Game.Tests/Lists/IntervalListTest.cs | 141 +++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 osu.Game.Tests/Lists/IntervalListTest.cs diff --git a/osu.Game.Tests/Lists/IntervalListTest.cs b/osu.Game.Tests/Lists/IntervalListTest.cs new file mode 100644 index 0000000000..1bc1483e15 --- /dev/null +++ b/osu.Game.Tests/Lists/IntervalListTest.cs @@ -0,0 +1,141 @@ +// 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.Lists; + +namespace osu.Game.Tests.Lists +{ + [TestFixture] + public class IntervalListTest + { + // this is intended to be unordered to test adding intervals in unordered way. + private static readonly (double, double)[] test_intervals = + { + (-9.1d, -8.3d), + (-3.4d, 2.1d), + (50.0d, 9.0d), // intentionally reversing interval. + (5.25d, 10.50d), + }; + + [Test] + public void TestCheckValueInsideSingleInterval() + { + var list = new IntervalList { { 1.0d, 2.0d } }; + + Assert.IsTrue(list.IsInAnyInterval(1.0d)); + Assert.IsTrue(list.IsInAnyInterval(1.5d)); + Assert.IsTrue(list.IsInAnyInterval(2.0d)); + } + + [Test] + public void TestCheckValuesInsideIntervals() + { + var list = new IntervalList(); + + foreach (var (start, end) in test_intervals) + list.Add(start, end); + + Assert.IsTrue(list.IsInAnyInterval(-8.75d)); + Assert.IsTrue(list.IsInAnyInterval(1.0d)); + Assert.IsTrue(list.IsInAnyInterval(7.89d)); + Assert.IsTrue(list.IsInAnyInterval(9.8d)); + Assert.IsTrue(list.IsInAnyInterval(15.83d)); + } + + [Test] + public void TestCheckValuesInRandomOrder() + { + var list = new IntervalList(); + + foreach (var (start, end) in test_intervals) + list.Add(start, end); + + Assert.IsTrue(list.IsInAnyInterval(9.8d)); + Assert.IsTrue(list.IsInAnyInterval(7.89d)); + Assert.IsTrue(list.IsInAnyInterval(1.0d)); + Assert.IsTrue(list.IsInAnyInterval(15.83d)); + Assert.IsTrue(list.IsInAnyInterval(-8.75d)); + } + + [Test] + public void TestCheckValuesOutOfIntervals() + { + var list = new IntervalList(); + + foreach (var (start, end) in test_intervals) + list.Add(start, end); + + Assert.IsFalse(list.IsInAnyInterval(-9.2d)); + Assert.IsFalse(list.IsInAnyInterval(2.2d)); + Assert.IsFalse(list.IsInAnyInterval(5.15d)); + Assert.IsFalse(list.IsInAnyInterval(51.2d)); + } + + [Test] + public void TestCheckValueAfterRemovedInterval() + { + var list = new IntervalList { { 50, 100 }, { 150, 200 }, { 250, 300 } }; + + Assert.IsTrue(list.IsInAnyInterval(75)); + Assert.IsTrue(list.IsInAnyInterval(175)); + Assert.IsTrue(list.IsInAnyInterval(275)); + + list.Remove(list[1]); + + Assert.IsFalse(list.IsInAnyInterval(175)); + Assert.IsTrue(list.IsInAnyInterval(75)); + Assert.IsTrue(list.IsInAnyInterval(275)); + } + + [Test] + public void TestCheckValueAfterAddedInterval() + { + var list = new IntervalList { { 50, 100 }, { 250, 300 } }; + + Assert.IsFalse(list.IsInAnyInterval(175)); + Assert.IsTrue(list.IsInAnyInterval(75)); + Assert.IsTrue(list.IsInAnyInterval(275)); + + list.Add(150, 200); + + Assert.IsTrue(list.IsInAnyInterval(175)); + } + + [Test] + public void TestCheckIntervalIndexOnChecks() + { + var list = new TestIntervalList { { 1.0d, 2.0d }, { 3.0d, 4.0d }, { 5.0d, 6.0d }, { 7.0d, 8.0d } }; + + Assert.IsTrue(list.IsInAnyInterval(1.5d)); + Assert.IsTrue(list.NearestIntervalIndex == 0); + + Assert.IsTrue(list.IsInAnyInterval(5.5d)); + Assert.IsTrue(list.NearestIntervalIndex == 2); + + Assert.IsTrue(list.IsInAnyInterval(7.5d)); + Assert.IsTrue(list.NearestIntervalIndex == 3); + } + + [Test] + public void TestCheckIntervalIndexOnOutOfIntervalsChecks() + { + var list = new TestIntervalList { { 1.0d, 2.0d }, { 3.0d, 4.0d }, { 5.0d, 6.0d }, { 7.0d, 8.0d } }; + + Assert.IsFalse(list.IsInAnyInterval(4.5d)); + Assert.IsTrue(list.NearestIntervalIndex == 1 || + list.NearestIntervalIndex == 2); // 4.5 in between 3.0-4.0 and 5.0-6.0 + + Assert.IsFalse(list.IsInAnyInterval(9.0d)); + Assert.IsTrue(list.NearestIntervalIndex == 3); // 9.0 goes above 7.0-8.0 + + Assert.IsFalse(list.IsInAnyInterval(0.0d)); + Assert.IsTrue(list.NearestIntervalIndex == 0); // 0.0 goes below 1.0-2.0 + } + + private class TestIntervalList : IntervalList + { + public new int NearestIntervalIndex => base.NearestIntervalIndex; + } + } +} From 9a29797a5bee661a767d43584af541d6619d5ea5 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 9 Apr 2020 23:00:16 +0300 Subject: [PATCH 0127/1117] Use IntervalList for tracking break periods --- .../Visual/Gameplay/TestSceneAutoplay.cs | 2 +- .../Visual/Gameplay/TestSceneBreakTracker.cs | 4 -- osu.Game/Screens/Play/BreakTracker.cs | 50 ++++++------------- 3 files changed, 16 insertions(+), 40 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs index 4b1c2ec256..0be949650e 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs @@ -52,7 +52,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep($"seek to break {breakIndex}", () => Player.GameplayClockContainer.Seek(destBreak().StartTime)); AddUntilStep("wait for seek to complete", () => Player.HUDOverlay.Progress.ReferenceClock.CurrentTime >= destBreak().StartTime); - BreakPeriod destBreak() => Player.ChildrenOfType().First().Breaks.ElementAt(breakIndex); + BreakPeriod destBreak() => Beatmap.Value.Beatmap.Breaks.ElementAt(breakIndex); } } } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBreakTracker.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBreakTracker.cs index 91d6f2f143..a6f996c30d 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBreakTracker.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBreakTracker.cs @@ -97,8 +97,6 @@ namespace osu.Game.Tests.Visual.Gameplay loadBreaksStep("multiple breaks", testBreaks); seekAndAssertBreak("seek to break start", testBreaks[1].StartTime, true); - AddAssert("is skipped to break #2", () => breakTracker.CurrentBreakIndex == 1); - seekAndAssertBreak("seek to break middle", testBreaks[1].StartTime + testBreaks[1].Duration / 2, true); seekAndAssertBreak("seek to break end", testBreaks[1].EndTime, false); seekAndAssertBreak("seek to break after end", testBreaks[1].EndTime + 500, false); @@ -174,8 +172,6 @@ namespace osu.Game.Tests.Visual.Gameplay private readonly ManualClock manualClock; private IFrameBasedClock originalClock; - public new int CurrentBreakIndex => base.CurrentBreakIndex; - public double ManualClockTime { get => manualClock.CurrentTime; diff --git a/osu.Game/Screens/Play/BreakTracker.cs b/osu.Game/Screens/Play/BreakTracker.cs index 64262d52b5..c2eb069ee6 100644 --- a/osu.Game/Screens/Play/BreakTracker.cs +++ b/osu.Game/Screens/Play/BreakTracker.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Beatmaps.Timing; +using osu.Game.Lists; using osu.Game.Rulesets.Scoring; namespace osu.Game.Screens.Play @@ -20,22 +21,24 @@ namespace osu.Game.Screens.Play /// public IBindable IsBreakTime => isBreakTime; - protected int CurrentBreakIndex; - private readonly BindableBool isBreakTime = new BindableBool(); - private IReadOnlyList breaks; + private readonly IntervalList breakIntervals = new IntervalList(); public IReadOnlyList Breaks { - get => breaks; set { - breaks = value; - - // reset index in case the new breaks list is smaller than last one isBreakTime.Value = false; - CurrentBreakIndex = 0; + breakIntervals.Clear(); + + foreach (var b in value) + { + if (!b.HasEffect) + continue; + + breakIntervals.Add(b.StartTime, b.EndTime - BreakOverlay.BREAK_FADE_DURATION); + } } } @@ -49,34 +52,11 @@ namespace osu.Game.Screens.Play { base.Update(); - isBreakTime.Value = getCurrentBreak()?.HasEffect == true - || Clock.CurrentTime < gameplayStartTime + var time = Clock.CurrentTime; + + isBreakTime.Value = breakIntervals.IsInAnyInterval(time) + || time < gameplayStartTime || scoreProcessor?.HasCompleted == true; } - - private BreakPeriod getCurrentBreak() - { - if (breaks?.Count > 0) - { - var time = Clock.CurrentTime; - - if (time > breaks[CurrentBreakIndex].EndTime) - { - while (time > breaks[CurrentBreakIndex].EndTime && CurrentBreakIndex < breaks.Count - 1) - CurrentBreakIndex++; - } - else - { - while (time < breaks[CurrentBreakIndex].StartTime && CurrentBreakIndex > 0) - CurrentBreakIndex--; - } - - var closest = breaks[CurrentBreakIndex]; - - return closest.Contains(time) ? closest : null; - } - - return null; - } } } From c17e47026623afde64aaf11b8334b5ebcd221696 Mon Sep 17 00:00:00 2001 From: Fire937 Date: Fri, 10 Apr 2020 00:01:35 +0200 Subject: [PATCH 0128/1117] Fix PositionalSound calculation implementation The position used to calculate the stereo balance is now the position of the drawable (as opposed to the position specified in the beatmap file previously). --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index ed9efba89f..30a9106ddc 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -94,7 +94,7 @@ namespace osu.Game.Rulesets.Objects.Drawables /// /// The stereo balance of the samples played if Positional hitsounds is set. /// - protected virtual float PositionalSound => (HitObject is IHasXPosition position) ? (position.X / 512f - 0.5f) * 0.8f : 0; + protected virtual float PositionalSound => (Position.X / 512f - 0.5f) * 0.8f; private BindableList samplesBindable; private Bindable startTimeBindable; @@ -114,7 +114,6 @@ namespace osu.Game.Rulesets.Objects.Drawables protected DrawableHitObject([NotNull] HitObject hitObject) { HitObject = hitObject ?? throw new ArgumentNullException(nameof(hitObject)); - positionalSoundAdjustment.Value = PositionalSound; } [BackgroundDependencyLoader] @@ -377,7 +376,11 @@ namespace osu.Game.Rulesets.Objects.Drawables /// Plays all the hit sounds for this . /// This is invoked automatically when this is hit. /// - public virtual void PlaySamples() => Samples?.Play(); + public virtual void PlaySamples() + { + positionalSoundAdjustment.Value = PositionalSound; + Samples?.Play(); + } protected override void Update() { From 4a87ac784061318026fe650a48e42ca455e7e7f9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 10 Apr 2020 13:53:09 +0900 Subject: [PATCH 0129/1117] Add support for sample changes --- .../Screens/Edit/Compose/Components/SelectionHandler.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index e212979433..764eae1056 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -212,6 +212,8 @@ namespace osu.Game.Screens.Edit.Compose.Components /// The name of the hit sample. public void AddHitSample(string sampleName) { + changeHandler?.BeginChange(); + foreach (var h in SelectedHitObjects) { // Make sure there isn't already an existing sample @@ -220,6 +222,8 @@ namespace osu.Game.Screens.Edit.Compose.Components h.Samples.Add(new HitSampleInfo { Name = sampleName }); } + + changeHandler?.EndChange(); } /// @@ -228,8 +232,12 @@ namespace osu.Game.Screens.Edit.Compose.Components /// The name of the hit sample. public void RemoveHitSample(string sampleName) { + changeHandler?.BeginChange(); + foreach (var h in SelectedHitObjects) h.SamplesBindable.RemoveAll(s => s.Name == sampleName); + + changeHandler?.EndChange(); } #endregion From 1001fcfb94d28787d62fa9e185499188620b4522 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 10 Apr 2020 15:48:00 +0300 Subject: [PATCH 0130/1117] Revert nearest index accessibility back to private Implementation details should not be tested. --- osu.Game/Lists/IntervalList.cs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/osu.Game/Lists/IntervalList.cs b/osu.Game/Lists/IntervalList.cs index 493b6b6e72..6a71d94ea8 100644 --- a/osu.Game/Lists/IntervalList.cs +++ b/osu.Game/Lists/IntervalList.cs @@ -17,11 +17,8 @@ namespace osu.Game.Lists private static readonly IComparer type_comparer = Comparer.Default; private readonly SortedList> intervals = new SortedList>((x, y) => type_comparer.Compare(x.Start, y.Start)); + private int nearestIndex; - /// - /// The index of the nearest interval from last call. - /// - protected int NearestIntervalIndex; /// /// Whether the provided value is in any interval added to this list. @@ -34,20 +31,20 @@ namespace osu.Game.Lists // Clamp the nearest index in case there were intervals // removed from the list causing the index to go out of range. - NearestIntervalIndex = Math.Clamp(NearestIntervalIndex, 0, Count - 1); + nearestIndex = Math.Clamp(nearestIndex, 0, intervals.Count - 1); - if (type_comparer.Compare(value, this[NearestIntervalIndex].End) > 0) + if (type_comparer.Compare(value, this[nearestIndex].End) > 0) { - while (type_comparer.Compare(value, this[NearestIntervalIndex].End) > 0 && NearestIntervalIndex < Count - 1) - NearestIntervalIndex++; + while (type_comparer.Compare(value, this[nearestIndex].End) > 0 && nearestIndex < intervals.Count - 1) + nearestIndex++; } else { - while (type_comparer.Compare(value, this[NearestIntervalIndex].Start) < 0 && NearestIntervalIndex > 0) - NearestIntervalIndex--; + while (type_comparer.Compare(value, this[nearestIndex].Start) < 0 && nearestIndex > 0) + nearestIndex--; } - var nearestInterval = this[NearestIntervalIndex]; + var nearestInterval = this[nearestIndex]; return type_comparer.Compare(value, nearestInterval.Start) >= 0 && type_comparer.Compare(value, nearestInterval.End) <= 0; From b7ed17dfbd16a78561da25516757bc19170e9af2 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 10 Apr 2020 15:49:06 +0300 Subject: [PATCH 0131/1117] Throw if interval with reversed values added May reduce in subtle bugs. --- osu.Game.Tests/Lists/IntervalListTest.cs | 38 ++++-------------------- osu.Game/Lists/IntervalList.cs | 7 +++-- 2 files changed, 10 insertions(+), 35 deletions(-) diff --git a/osu.Game.Tests/Lists/IntervalListTest.cs b/osu.Game.Tests/Lists/IntervalListTest.cs index 1bc1483e15..0958f0fa7c 100644 --- a/osu.Game.Tests/Lists/IntervalListTest.cs +++ b/osu.Game.Tests/Lists/IntervalListTest.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 NUnit.Framework; using osu.Game.Lists; @@ -14,7 +15,7 @@ namespace osu.Game.Tests.Lists { (-9.1d, -8.3d), (-3.4d, 2.1d), - (50.0d, 9.0d), // intentionally reversing interval. + (9.0d, 50.0d), (5.25d, 10.50d), }; @@ -103,39 +104,12 @@ namespace osu.Game.Tests.Lists } [Test] - public void TestCheckIntervalIndexOnChecks() + public void TestReversedIntervalThrows() { - var list = new TestIntervalList { { 1.0d, 2.0d }, { 3.0d, 4.0d }, { 5.0d, 6.0d }, { 7.0d, 8.0d } }; + var list = new IntervalList(); - Assert.IsTrue(list.IsInAnyInterval(1.5d)); - Assert.IsTrue(list.NearestIntervalIndex == 0); - - Assert.IsTrue(list.IsInAnyInterval(5.5d)); - Assert.IsTrue(list.NearestIntervalIndex == 2); - - Assert.IsTrue(list.IsInAnyInterval(7.5d)); - Assert.IsTrue(list.NearestIntervalIndex == 3); - } - - [Test] - public void TestCheckIntervalIndexOnOutOfIntervalsChecks() - { - var list = new TestIntervalList { { 1.0d, 2.0d }, { 3.0d, 4.0d }, { 5.0d, 6.0d }, { 7.0d, 8.0d } }; - - Assert.IsFalse(list.IsInAnyInterval(4.5d)); - Assert.IsTrue(list.NearestIntervalIndex == 1 || - list.NearestIntervalIndex == 2); // 4.5 in between 3.0-4.0 and 5.0-6.0 - - Assert.IsFalse(list.IsInAnyInterval(9.0d)); - Assert.IsTrue(list.NearestIntervalIndex == 3); // 9.0 goes above 7.0-8.0 - - Assert.IsFalse(list.IsInAnyInterval(0.0d)); - Assert.IsTrue(list.NearestIntervalIndex == 0); // 0.0 goes below 1.0-2.0 - } - - private class TestIntervalList : IntervalList - { - public new int NearestIntervalIndex => base.NearestIntervalIndex; + Assert.Throws(() => list.Add(50, 25)); + Assert.Throws(() => list.Add(new Interval(50, 25))); } } } diff --git a/osu.Game/Lists/IntervalList.cs b/osu.Game/Lists/IntervalList.cs index 6a71d94ea8..15d9a349dc 100644 --- a/osu.Game/Lists/IntervalList.cs +++ b/osu.Game/Lists/IntervalList.cs @@ -116,10 +116,11 @@ namespace osu.Game.Lists public Interval(T start, T end) { - bool startLessThanEnd = Comparer.Default.Compare(start, end) < 0; + if (Comparer.Default.Compare(start, end) >= 0) + throw new ArgumentException($"Invalid interval, {nameof(start)} must be less than {nameof(end)}", nameof(start)); - Start = startLessThanEnd ? start : end; - End = startLessThanEnd ? end : start; + Start = start; + End = end; } } } From 5966c9af9ccdd05d84a92921eae77b3cff787712 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 10 Apr 2020 15:50:07 +0300 Subject: [PATCH 0132/1117] Limit generic type to IConvertible structures (common number types) --- osu.Game/Lists/IntervalList.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Lists/IntervalList.cs b/osu.Game/Lists/IntervalList.cs index 15d9a349dc..5a5866ca91 100644 --- a/osu.Game/Lists/IntervalList.cs +++ b/osu.Game/Lists/IntervalList.cs @@ -13,6 +13,7 @@ namespace osu.Game.Lists /// /// The type of interval values. public class IntervalList : ICollection>, IReadOnlyList> + where T : struct, IConvertible { private static readonly IComparer type_comparer = Comparer.Default; @@ -103,6 +104,7 @@ namespace osu.Game.Lists } public readonly struct Interval + where T : struct, IConvertible { /// /// The start value of this interval. From bf124a5cc6d9d99566f099e405f2c03bcdc6ac3f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 10 Apr 2020 15:50:40 +0300 Subject: [PATCH 0133/1117] Simplify IntervalList implementation enough to work for its usages --- osu.Game/Lists/IntervalList.cs | 29 ++++++----------------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/osu.Game/Lists/IntervalList.cs b/osu.Game/Lists/IntervalList.cs index 5a5866ca91..580015bb96 100644 --- a/osu.Game/Lists/IntervalList.cs +++ b/osu.Game/Lists/IntervalList.cs @@ -12,7 +12,7 @@ namespace osu.Game.Lists /// Represents a list of intervals that can be used for whether a specific value falls into one of them. /// /// The type of interval values. - public class IntervalList : ICollection>, IReadOnlyList> + public class IntervalList : IEnumerable> where T : struct, IConvertible { private static readonly IComparer type_comparer = Comparer.Default; @@ -20,6 +20,11 @@ namespace osu.Game.Lists private readonly SortedList> intervals = new SortedList>((x, y) => type_comparer.Compare(x.Start, y.Start)); private int nearestIndex; + public Interval this[int i] + { + get => intervals[i]; + set => intervals[i] = value; + } /// /// Whether the provided value is in any interval added to this list. @@ -58,12 +63,6 @@ namespace osu.Game.Lists /// The end value of the interval. public void Add(T start, T end) => Add(new Interval(start, end)); - #region ICollection> - - public int Count => intervals.Count; - - bool ICollection>.IsReadOnly => false; - /// /// Adds a new interval to the list /// @@ -82,25 +81,9 @@ namespace osu.Game.Lists /// public void Clear() => intervals.Clear(); - public void CopyTo(Interval[] array, int arrayIndex) => intervals.CopyTo(array, arrayIndex); - - public bool Contains(Interval item) => intervals.Contains(item); - public IEnumerator> GetEnumerator() => intervals.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - #endregion - - #region IReadOnlyList> - - public Interval this[int index] - { - get => intervals[index]; - set => intervals[index] = value; - } - - #endregion } public readonly struct Interval From 7fba29113466d5fe6aa43eef0cd284323f1c969a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 11 Apr 2020 13:14:34 +0900 Subject: [PATCH 0134/1117] Change inheritance of taiko hit pieces to better allow for skinning --- .../TestSceneDrawableHit.cs | 54 ++++++++++++++++++ .../Objects/Drawables/DrawableCentreHit.cs | 10 +--- .../Objects/Drawables/DrawableDrumRoll.cs | 10 ++-- .../Objects/Drawables/DrawableDrumRollTick.cs | 3 +- .../Objects/Drawables/DrawableRimHit.cs | 10 +--- .../Objects/Drawables/DrawableSwell.cs | 16 +++--- .../Objects/Drawables/DrawableSwellTick.cs | 4 ++ .../Drawables/DrawableTaikoHitObject.cs | 14 ++--- .../Drawables/Pieces/CentreHitSymbolPiece.cs | 50 +++++++++++------ .../Drawables/Pieces/RimHitCirclePiece.cs | 55 +++++++++++++++++++ .../Drawables/Pieces/RimHitSymbolPiece.cs | 39 ------------- .../Drawables/Pieces/SwellSymbolPiece.cs | 50 +++++++++++------ .../Objects/Drawables/Pieces/TickPiece.cs | 6 +- 13 files changed, 209 insertions(+), 112 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko.Tests/TestSceneDrawableHit.cs create mode 100644 osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/RimHitCirclePiece.cs delete mode 100644 osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/RimHitSymbolPiece.cs diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneDrawableHit.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneDrawableHit.cs new file mode 100644 index 0000000000..b927f0294b --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneDrawableHit.cs @@ -0,0 +1,54 @@ +// 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.Allocation; +using osu.Framework.Graphics; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Taiko.Objects.Drawables; + +namespace osu.Game.Rulesets.Taiko.Tests +{ + [TestFixture] + public class TestSceneDrawableHit : TaikoSkinnableTestScene + { + public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] + { + typeof(DrawableHit), + typeof(DrawableCentreHit), + typeof(DrawableRimHit), + }).ToList(); + + [BackgroundDependencyLoader] + private void load() + { + AddStep("Centre hit", () => SetContents(() => new DrawableCentreHit(createHitAtCurrentTime()) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + })); + AddStep("Rim hit", () => SetContents(() => new DrawableRimHit(createHitAtCurrentTime()) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + })); + } + + private Hit createHitAtCurrentTime() + { + var hit = new Hit + { + StartTime = Time.Current + 3000, + }; + + hit.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); + + return hit; + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs index 4979135f50..22d62442cf 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs @@ -1,8 +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.Allocation; -using osu.Game.Graphics; +using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; namespace osu.Game.Rulesets.Taiko.Objects.Drawables @@ -14,13 +13,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables public DrawableCentreHit(Hit hit) : base(hit) { - MainPiece.Add(new CentreHitSymbolPiece()); } - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - MainPiece.AccentColour = colours.PinkDarker; - } + protected override CompositeDrawable CreateMainPiece() => new CentreHitCirclePiece(); } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs index 5806c90115..0627eb95fd 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs @@ -34,17 +34,19 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables private Color4 colourIdle; private Color4 colourEngaged; + private ElongatedCirclePiece elongatedPiece; + public DrawableDrumRoll(DrumRoll drumRoll) : base(drumRoll) { RelativeSizeAxes = Axes.Y; - MainPiece.Add(tickContainer = new Container { RelativeSizeAxes = Axes.Both }); + elongatedPiece.Add(tickContainer = new Container { RelativeSizeAxes = Axes.Both }); } [BackgroundDependencyLoader] private void load(OsuColour colours) { - MainPiece.AccentColour = colourIdle = colours.YellowDark; + elongatedPiece.AccentColour = colourIdle = colours.YellowDark; colourEngaged = colours.YellowDarker; } @@ -84,7 +86,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables return base.CreateNestedHitObject(hitObject); } - protected override TaikoPiece CreateMainPiece() => new ElongatedCirclePiece(); + protected override CompositeDrawable CreateMainPiece() => elongatedPiece = new ElongatedCirclePiece(); public override bool OnPressed(TaikoAction action) => false; @@ -101,7 +103,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables rollingHits = Math.Clamp(rollingHits, 0, rolling_hits_for_engaged_colour); Color4 newColour = Interpolation.ValueAt((float)rollingHits / rolling_hits_for_engaged_colour, colourIdle, colourEngaged, 0, 1); - MainPiece.FadeAccent(newColour, 100); + (MainPiece as IHasAccentColour)?.FadeAccent(newColour, 100); } protected override void CheckForResult(bool userTriggered, double timeOffset) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs index 25b6141a0e..fea3eea6a9 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs @@ -3,6 +3,7 @@ using System; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; @@ -19,7 +20,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables public override bool DisplayResult => false; - protected override TaikoPiece CreateMainPiece() => new TickPiece + protected override CompositeDrawable CreateMainPiece() => new TickPiece { Filled = HitObject.FirstTick }; diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs index 5a12d71cea..6dad7af907 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs @@ -1,8 +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.Allocation; -using osu.Game.Graphics; +using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; namespace osu.Game.Rulesets.Taiko.Objects.Drawables @@ -14,13 +13,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables public DrawableRimHit(Hit hit) : base(hit) { - MainPiece.Add(new RimHitSymbolPiece()); } - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - MainPiece.AccentColour = colours.BlueDarker; - } + protected override CompositeDrawable CreateMainPiece() => new RimHitCirclePiece(); } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs index fa39819199..3a2e44038f 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs @@ -9,11 +9,11 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; using osuTK.Graphics; using osu.Framework.Graphics.Shapes; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; namespace osu.Game.Rulesets.Taiko.Objects.Drawables { @@ -34,8 +34,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables private readonly CircularContainer targetRing; private readonly CircularContainer expandingRing; - private readonly SwellSymbolPiece symbol; - public DrawableSwell(Swell swell) : base(swell) { @@ -107,18 +105,22 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables }); AddInternal(ticks = new Container { RelativeSizeAxes = Axes.Both }); - - MainPiece.Add(symbol = new SwellSymbolPiece()); } [BackgroundDependencyLoader] private void load(OsuColour colours) { - MainPiece.AccentColour = colours.YellowDark; expandingRing.Colour = colours.YellowLight; targetRing.BorderColour = colours.YellowDark.Opacity(0.25f); } + protected override CompositeDrawable CreateMainPiece() => new SwellCirclePiece + { + // to allow for rotation transform + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }; + protected override void LoadComplete() { base.LoadComplete(); @@ -182,7 +184,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables .Then() .FadeTo(completion / 8, 2000, Easing.OutQuint); - symbol.RotateTo((float)(completion * HitObject.Duration / 8), 4000, Easing.OutQuint); + MainPiece.RotateTo((float)(completion * HitObject.Duration / 8), 4000, Easing.OutQuint); expandingRing.ScaleTo(1f + Math.Min(target_ring_scale - 1f, (target_ring_scale - 1f) * completion * 1.3f), 260, Easing.OutQuint); diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs index ce875ebba8..5a954addfb 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs @@ -2,7 +2,9 @@ // 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.Scoring; +using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; namespace osu.Game.Rulesets.Taiko.Objects.Drawables { @@ -28,5 +30,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables } public override bool OnPressed(TaikoAction action) => false; + + protected override CompositeDrawable CreateMainPiece() => new TickPiece(); } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs index 5f892dd2fa..397888bb11 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs @@ -4,7 +4,6 @@ using osu.Framework.Graphics; using osu.Framework.Input.Bindings; using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; using osuTK; using System.Linq; using osu.Game.Audio; @@ -108,19 +107,19 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables } } - public abstract class DrawableTaikoHitObject : DrawableTaikoHitObject - where TTaikoHit : TaikoHitObject + public abstract class DrawableTaikoHitObject : DrawableTaikoHitObject + where TObject : TaikoHitObject { public override Vector2 OriginPosition => new Vector2(DrawHeight / 2); - public new TTaikoHit HitObject; + public new TObject HitObject; protected readonly Vector2 BaseSize; - protected readonly TaikoPiece MainPiece; + protected readonly CompositeDrawable MainPiece; private readonly Container strongHitContainer; - protected DrawableTaikoHitObject(TTaikoHit hitObject) + protected DrawableTaikoHitObject(TObject hitObject) : base(hitObject) { HitObject = hitObject; @@ -132,7 +131,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables Size = BaseSize = new Vector2(HitObject.IsStrong ? TaikoHitObject.DEFAULT_STRONG_SIZE : TaikoHitObject.DEFAULT_SIZE); Content.Add(MainPiece = CreateMainPiece()); - MainPiece.KiaiMode = HitObject.Kiai; AddInternal(strongHitContainer = new Container()); } @@ -169,7 +167,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables // Normal and clap samples are handled by the drum protected override IEnumerable GetSamples() => HitObject.Samples.Where(s => s.Name != HitSampleInfo.HIT_NORMAL && s.Name != HitSampleInfo.HIT_CLAP); - protected virtual TaikoPiece CreateMainPiece() => new CirclePiece(); + protected abstract CompositeDrawable CreateMainPiece(); /// /// Creates the handler for this 's . diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CentreHitSymbolPiece.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CentreHitSymbolPiece.cs index 7ed61ede96..0509841ba8 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CentreHitSymbolPiece.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CentreHitSymbolPiece.cs @@ -1,36 +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 osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osuTK; using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces { - /// - /// The symbol used for centre hit pieces. - /// - public class CentreHitSymbolPiece : Container + public class CentreHitCirclePiece : CirclePiece { - public CentreHitSymbolPiece() + public CentreHitCirclePiece() { - Anchor = Anchor.Centre; - Origin = Anchor.Centre; + Add(new CentreHitSymbolPiece()); + } - RelativeSizeAxes = Axes.Both; - Size = new Vector2(CirclePiece.SYMBOL_SIZE); - Padding = new MarginPadding(CirclePiece.SYMBOL_BORDER); + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + AccentColour = colours.PinkDarker; + } - Children = new[] + /// + /// The symbol used for centre hit pieces. + /// + public class CentreHitSymbolPiece : Container + { + public CentreHitSymbolPiece() { - new CircularContainer + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + RelativeSizeAxes = Axes.Both; + Size = new Vector2(SYMBOL_SIZE); + Padding = new MarginPadding(SYMBOL_BORDER); + + Children = new[] { - RelativeSizeAxes = Axes.Both, - Masking = true, - Children = new[] { new Box { RelativeSizeAxes = Axes.Both } } - } - }; + new CircularContainer + { + RelativeSizeAxes = Axes.Both, + Masking = true, + Children = new[] { new Box { RelativeSizeAxes = Axes.Both } } + } + }; + } } } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/RimHitCirclePiece.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/RimHitCirclePiece.cs new file mode 100644 index 0000000000..3273ab7fa7 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/RimHitCirclePiece.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 osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces +{ + public class RimHitCirclePiece : CirclePiece + { + public RimHitCirclePiece() + { + Add(new RimHitSymbolPiece()); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + AccentColour = colours.BlueDarker; + } + + /// + /// The symbol used for rim hit pieces. + /// + public class RimHitSymbolPiece : CircularContainer + { + public RimHitSymbolPiece() + { + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + RelativeSizeAxes = Axes.Both; + Size = new Vector2(SYMBOL_SIZE); + + BorderThickness = SYMBOL_BORDER; + BorderColour = Color4.White; + Masking = true; + Children = new[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true + } + }; + } + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/RimHitSymbolPiece.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/RimHitSymbolPiece.cs deleted file mode 100644 index e4c964a884..0000000000 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/RimHitSymbolPiece.cs +++ /dev/null @@ -1,39 +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; -using osuTK; -using osuTK.Graphics; -using osu.Framework.Graphics.Shapes; - -namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces -{ - /// - /// The symbol used for rim hit pieces. - /// - public class RimHitSymbolPiece : CircularContainer - { - public RimHitSymbolPiece() - { - Anchor = Anchor.Centre; - Origin = Anchor.Centre; - - RelativeSizeAxes = Axes.Both; - Size = new Vector2(CirclePiece.SYMBOL_SIZE); - - BorderThickness = CirclePiece.SYMBOL_BORDER; - BorderColour = Color4.White; - Masking = true; - Children = new[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - AlwaysPresent = true - } - }; - } - } -} diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/SwellSymbolPiece.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/SwellSymbolPiece.cs index 0ed9923924..a8f9f0b94d 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/SwellSymbolPiece.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/SwellSymbolPiece.cs @@ -1,36 +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 osu.Framework.Allocation; using osuTK; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces { - /// - /// The symbol used for swell pieces. - /// - public class SwellSymbolPiece : Container + public class SwellCirclePiece : CirclePiece { - public SwellSymbolPiece() + public SwellCirclePiece() { - Anchor = Anchor.Centre; - Origin = Anchor.Centre; + Add(new SwellSymbolPiece()); + } - RelativeSizeAxes = Axes.Both; - Size = new Vector2(CirclePiece.SYMBOL_SIZE); - Padding = new MarginPadding(CirclePiece.SYMBOL_BORDER); + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + AccentColour = colours.YellowDark; + } - Children = new[] + /// + /// The symbol used for swell pieces. + /// + public class SwellSymbolPiece : Container + { + public SwellSymbolPiece() { - new SpriteIcon + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + RelativeSizeAxes = Axes.Both; + Size = new Vector2(SYMBOL_SIZE); + Padding = new MarginPadding(SYMBOL_BORDER); + + Children = new[] { - RelativeSizeAxes = Axes.Both, - Icon = FontAwesome.Solid.Asterisk, - Shadow = false - } - }; + new SpriteIcon + { + RelativeSizeAxes = Axes.Both, + Icon = FontAwesome.Solid.Asterisk, + Shadow = false + } + }; + } } } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/TickPiece.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/TickPiece.cs index 83cf7a64ec..0648bcebcd 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/TickPiece.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/TickPiece.cs @@ -9,7 +9,7 @@ using osu.Framework.Graphics.Shapes; namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces { - public class TickPiece : TaikoPiece + public class TickPiece : CompositeDrawable { /// /// Any tick that is not the first for a drumroll is not filled, but is instead displayed @@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces FillMode = FillMode.Fit; Size = new Vector2(tick_size); - Add(new CircularContainer + InternalChild = new CircularContainer { RelativeSizeAxes = Axes.Both, Masking = true, @@ -60,7 +60,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces AlwaysPresent = true } } - }); + }; } } } From ca2df77c7684899cc107d72f646e90461244a8e5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 11 Apr 2020 13:14:41 +0900 Subject: [PATCH 0135/1117] Add default skin test resources --- .../metrics-skin/taikohitcircle@2x.png | Bin 0 -> 13140 bytes .../metrics-skin/taikohitcircleoverlay@2x.png | Bin 0 -> 38217 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/metrics-skin/taikohitcircle@2x.png create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/metrics-skin/taikohitcircleoverlay@2x.png diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/metrics-skin/taikohitcircle@2x.png b/osu.Game.Rulesets.Taiko.Tests/Resources/metrics-skin/taikohitcircle@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..043bfbfae1a77317aec68051b2d917a42d143fb1 GIT binary patch literal 13140 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 R|M{Pbx-w?>O~E?q{{RqUO*sGn literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/metrics-skin/taikohitcircleoverlay@2x.png b/osu.Game.Rulesets.Taiko.Tests/Resources/metrics-skin/taikohitcircleoverlay@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..4233d9bb6e6aa88c7da5c1998439789cc5f9c7b2 GIT binary patch literal 38217 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 literal 0 HcmV?d00001 From 78db83fd0e88ad1217f96ff83cb077c800ad9494 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 11 Apr 2020 13:33:19 +0900 Subject: [PATCH 0136/1117] Remove TaikoPiece class and localise kiai for now --- ...SymbolPiece.cs => CentreHitCirclePiece.cs} | 0 .../Objects/Drawables/Pieces/CirclePiece.cs | 19 ++++++++----- .../Objects/Drawables/Pieces/TaikoPiece.cs | 28 ------------------- 3 files changed, 12 insertions(+), 35 deletions(-) rename osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/{CentreHitSymbolPiece.cs => CentreHitCirclePiece.cs} (100%) delete mode 100644 osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/TaikoPiece.cs diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CentreHitSymbolPiece.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CentreHitCirclePiece.cs similarity index 100% rename from osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CentreHitSymbolPiece.cs rename to osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CentreHitCirclePiece.cs diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs index d9c0664ecd..ce2882656a 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs @@ -10,6 +10,7 @@ using osuTK.Graphics; using osu.Game.Beatmaps.ControlPoints; using osu.Framework.Audio.Track; using osu.Framework.Graphics.Effects; +using osu.Game.Graphics.Containers; namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces { @@ -20,21 +21,23 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces /// for a usage example. /// /// - public class CirclePiece : TaikoPiece + public class CirclePiece : BeatSyncedContainer { public const float SYMBOL_SIZE = 0.45f; public const float SYMBOL_BORDER = 8; private const double pre_beat_transition_time = 80; + private Color4 accentColour; + /// /// The colour of the inner circle and outer glows. /// - public override Color4 AccentColour + public Color4 AccentColour { - get => base.AccentColour; + get => accentColour; set { - base.AccentColour = value; + accentColour = value; background.Colour = AccentColour; @@ -42,15 +45,17 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces } } + private bool kiaiMode; + /// /// Whether Kiai mode effects are enabled for this circle piece. /// - public override bool KiaiMode + public bool KiaiMode { - get => base.KiaiMode; + get => kiaiMode; set { - base.KiaiMode = value; + kiaiMode = value; resetEdgeEffects(); } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/TaikoPiece.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/TaikoPiece.cs deleted file mode 100644 index 8067054f8f..0000000000 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/TaikoPiece.cs +++ /dev/null @@ -1,28 +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.Graphics; -using osuTK.Graphics; -using osu.Game.Graphics.Containers; -using osu.Framework.Graphics; - -namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces -{ - public class TaikoPiece : BeatSyncedContainer, IHasAccentColour - { - /// - /// The colour of the inner circle and outer glows. - /// - public virtual Color4 AccentColour { get; set; } - - /// - /// Whether Kiai mode effects are enabled for this circle piece. - /// - public virtual bool KiaiMode { get; set; } - - public TaikoPiece() - { - RelativeSizeAxes = Axes.Both; - } - } -} From 009b1383648dd267e76ee13f72daac2ee1bb1458 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 11 Apr 2020 13:41:14 +0900 Subject: [PATCH 0137/1117] Prepare for skinnable versions --- .../Objects/Drawables/DrawableCentreHit.cs | 4 +++- osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs | 4 +++- .../Objects/Drawables/Pieces/CirclePiece.cs | 2 ++ osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs | 2 ++ 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs index 22d62442cf..f3f4c59a62 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs @@ -3,6 +3,7 @@ using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; +using osu.Game.Skinning; namespace osu.Game.Rulesets.Taiko.Objects.Drawables { @@ -15,6 +16,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { } - protected override CompositeDrawable CreateMainPiece() => new CentreHitCirclePiece(); + protected override CompositeDrawable CreateMainPiece() => new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.CentreHit), + _ => new CentreHitCirclePiece(), confineMode: ConfineMode.ScaleToFit); } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs index 6dad7af907..463a8b746c 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs @@ -3,6 +3,7 @@ using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; +using osu.Game.Skinning; namespace osu.Game.Rulesets.Taiko.Objects.Drawables { @@ -15,6 +16,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { } - protected override CompositeDrawable CreateMainPiece() => new RimHitCirclePiece(); + protected override CompositeDrawable CreateMainPiece() => new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.RimHit), + _ => new RimHitCirclePiece(), confineMode: ConfineMode.ScaleToFit); } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs index ce2882656a..70fe4b7bb2 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs @@ -71,6 +71,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces public CirclePiece() { + RelativeSizeAxes = Axes.Both; + EarlyActivationMilliseconds = pre_beat_transition_time; AddRangeInternal(new Drawable[] diff --git a/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs b/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs index 6d4581db80..babf21b6a9 100644 --- a/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs +++ b/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs @@ -6,5 +6,7 @@ namespace osu.Game.Rulesets.Taiko public enum TaikoSkinComponents { InputDrum, + CentreHit, + RimHit } } From dc56be0a1d946d08acede69fc6619dcc47298e3c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 11 Apr 2020 14:20:09 +0900 Subject: [PATCH 0138/1117] Add support for skinned hits --- .../TestSceneDrawableHit.cs | 2 + osu.Game.Rulesets.Taiko/Skinning/LegacyHit.cs | 62 +++++++++++++++++++ .../Skinning/TaikoLegacySkinTransformer.cs | 8 +++ 3 files changed, 72 insertions(+) create mode 100644 osu.Game.Rulesets.Taiko/Skinning/LegacyHit.cs diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneDrawableHit.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneDrawableHit.cs index b927f0294b..f2198031db 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneDrawableHit.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneDrawableHit.cs @@ -11,6 +11,7 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Objects.Drawables; +using osu.Game.Rulesets.Taiko.Skinning; namespace osu.Game.Rulesets.Taiko.Tests { @@ -22,6 +23,7 @@ namespace osu.Game.Rulesets.Taiko.Tests typeof(DrawableHit), typeof(DrawableCentreHit), typeof(DrawableRimHit), + typeof(LegacyHit), }).ToList(); [BackgroundDependencyLoader] diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyHit.cs b/osu.Game.Rulesets.Taiko/Skinning/LegacyHit.cs new file mode 100644 index 0000000000..bb76eac865 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Skinning/LegacyHit.cs @@ -0,0 +1,62 @@ +// 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.Animations; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Game.Skinning; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Taiko.Skinning +{ + public class LegacyHit : CompositeDrawable, IHasAccentColour + { + private readonly TaikoSkinComponents component; + + private Drawable backgroundLayer; + + public LegacyHit(TaikoSkinComponents component) + { + this.component = component; + } + + [BackgroundDependencyLoader] + private void load(ISkinSource skin) + { + InternalChildren = new Drawable[] + { + backgroundLayer = skin.GetAnimation("taikohitcircle", true, false), + skin.GetAnimation("taikohitcircleoverlay", true, false), + }; + + // animations in taiko skins are used in a custom way (>150 combo and animating in time with beat). + // for now just stop at first frame for sanity. + foreach (var c in InternalChildren) + { + (c as IFramedAnimation)?.Stop(); + c.Anchor = Anchor.Centre; + c.Origin = Anchor.Centre; + } + + AccentColour = component == TaikoSkinComponents.CentreHit + ? new Color4(235, 69, 44, 255) + : new Color4(67, 142, 172, 255); + } + + private Color4 accentColour; + + public Color4 AccentColour + { + get => accentColour; + set + { + if (value == accentColour) + return; + + backgroundLayer.Colour = accentColour = value; + } + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs index 78eec94590..9cd625c35f 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs @@ -32,6 +32,14 @@ namespace osu.Game.Rulesets.Taiko.Skinning return new LegacyInputDrum(); return null; + + case TaikoSkinComponents.CentreHit: + case TaikoSkinComponents.RimHit: + + if (GetTexture("taikohitcircle") != null) + return new LegacyHit(taikoComponent.Component); + + return null; } return source.GetDrawableComponent(component); From 96bf86099c89444e5328adaa3c169a60d47854c6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 11 Apr 2020 14:43:57 +0900 Subject: [PATCH 0139/1117] Fix scaling of strong hits --- .../TestSceneDrawableHit.cs | 16 +++++++++++++++- osu.Game.Rulesets.Taiko/Skinning/LegacyHit.cs | 16 +++++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneDrawableHit.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneDrawableHit.cs index f2198031db..301295253d 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneDrawableHit.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneDrawableHit.cs @@ -34,17 +34,31 @@ namespace osu.Game.Rulesets.Taiko.Tests Anchor = Anchor.Centre, Origin = Anchor.Centre, })); + + AddStep("Centre hit (strong)", () => SetContents(() => new DrawableCentreHit(createHitAtCurrentTime(true)) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + })); + AddStep("Rim hit", () => SetContents(() => new DrawableRimHit(createHitAtCurrentTime()) { Anchor = Anchor.Centre, Origin = Anchor.Centre, })); + + AddStep("Rim hit (strong)", () => SetContents(() => new DrawableRimHit(createHitAtCurrentTime(true)) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + })); } - private Hit createHitAtCurrentTime() + private Hit createHitAtCurrentTime(bool strong = false) { var hit = new Hit { + IsStrong = strong, StartTime = Time.Current + 3000, }; diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyHit.cs b/osu.Game.Rulesets.Taiko/Skinning/LegacyHit.cs index bb76eac865..af10944ee9 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/LegacyHit.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/LegacyHit.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics.Animations; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; using osu.Game.Skinning; +using osuTK; using osuTK.Graphics; namespace osu.Game.Rulesets.Taiko.Skinning @@ -20,12 +21,14 @@ namespace osu.Game.Rulesets.Taiko.Skinning public LegacyHit(TaikoSkinComponents component) { this.component = component; + + RelativeSizeAxes = Axes.Both; } [BackgroundDependencyLoader] private void load(ISkinSource skin) { - InternalChildren = new Drawable[] + InternalChildren = new[] { backgroundLayer = skin.GetAnimation("taikohitcircle", true, false), skin.GetAnimation("taikohitcircleoverlay", true, false), @@ -36,6 +39,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning foreach (var c in InternalChildren) { (c as IFramedAnimation)?.Stop(); + c.Anchor = Anchor.Centre; c.Origin = Anchor.Centre; } @@ -45,6 +49,16 @@ namespace osu.Game.Rulesets.Taiko.Skinning : new Color4(67, 142, 172, 255); } + protected override void Update() + { + base.Update(); + + // not all skins (including the default osu-stable) have similar sizes for hitcircle and hitcircleoverlay. + // this ensures they are scaled relative to each other but also match the expected DrawableHit size. + foreach (var c in InternalChildren) + c.Scale = new Vector2(DrawWidth / 128); + } + private Color4 accentColour; public Color4 AccentColour From bf938a37e3d374075e0b9f8e2231e41dc2297949 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 11 Apr 2020 14:51:27 +0900 Subject: [PATCH 0140/1117] Add old skin test resources (with "animation") --- .../Resources/old-skin/taikobigcircle.png | Bin 0 -> 3079 bytes .../old-skin/taikobigcircleoverlay-0.png | Bin 0 -> 17018 bytes .../old-skin/taikobigcircleoverlay-1.png | Bin 0 -> 18837 bytes .../Resources/old-skin/taikohitcircle.png | Bin 0 -> 6028 bytes .../old-skin/taikohitcircleoverlay-0.png | Bin 0 -> 20284 bytes .../old-skin/taikohitcircleoverlay-1.png | Bin 0 -> 20333 bytes 6 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/taikobigcircle.png create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/taikobigcircleoverlay-0.png create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/taikobigcircleoverlay-1.png create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/taikohitcircle.png create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/taikohitcircleoverlay-0.png create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/taikohitcircleoverlay-1.png diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/taikobigcircle.png b/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/taikobigcircle.png new file mode 100644 index 0000000000000000000000000000000000000000..63504dd52db36e9da6b3b40a08a9d3ec6e1e34f9 GIT binary patch literal 3079 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 JRBqrF^Iu$8TZ{kz literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/taikohitcircleoverlay-0.png b/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/taikohitcircleoverlay-0.png new file mode 100644 index 0000000000000000000000000000000000000000..272c6bcaf75c18a985b971284b11162a13a2cca0 GIT binary patch literal 20284 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 pu7$$YDiL Date: Sat, 11 Apr 2020 15:01:09 +0900 Subject: [PATCH 0141/1117] Add support for taikobigcircle and fix exception on missing layers --- osu.Game.Rulesets.Taiko/Skinning/LegacyHit.cs | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyHit.cs b/osu.Game.Rulesets.Taiko/Skinning/LegacyHit.cs index af10944ee9..43d45ea1c9 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/LegacyHit.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/LegacyHit.cs @@ -6,6 +6,8 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Animations; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Taiko.Objects.Drawables; using osu.Game.Skinning; using osuTK; using osuTK.Graphics; @@ -26,13 +28,24 @@ namespace osu.Game.Rulesets.Taiko.Skinning } [BackgroundDependencyLoader] - private void load(ISkinSource skin) + private void load(ISkinSource skin, DrawableHitObject drawableHitObject) { - InternalChildren = new[] + Drawable getDrawableFor(string lookup) { - backgroundLayer = skin.GetAnimation("taikohitcircle", true, false), - skin.GetAnimation("taikohitcircleoverlay", true, false), - }; + const string normal_hit = "taikohit"; + const string big_hit = "taikobig"; + + string prefix = ((drawableHitObject as DrawableTaikoHitObject)?.HitObject.IsStrong ?? false) ? big_hit : normal_hit; + + return skin.GetAnimation($"{prefix}{lookup}", true, false) ?? skin.GetAnimation($"{normal_hit}{lookup}", true, false); + } + + // backgroundLayer is guaranteed to exist due to the pre-check in TaikoLegacySkinTransformer + AddInternal(backgroundLayer = getDrawableFor("circle")); + + var foregroundLayer = getDrawableFor("circleoverlay"); + if (foregroundLayer != null) + AddInternal(foregroundLayer); // animations in taiko skins are used in a custom way (>150 combo and animating in time with beat). // for now just stop at first frame for sanity. From 3d5a622db7b0dbf8e4984c1ea57dba56c1d7393f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 11 Apr 2020 15:04:58 +0900 Subject: [PATCH 0142/1117] Tidy up comments --- osu.Game.Rulesets.Taiko/Skinning/LegacyHit.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyHit.cs b/osu.Game.Rulesets.Taiko/Skinning/LegacyHit.cs index 43d45ea1c9..80bf97936d 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/LegacyHit.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/LegacyHit.cs @@ -37,18 +37,20 @@ namespace osu.Game.Rulesets.Taiko.Skinning string prefix = ((drawableHitObject as DrawableTaikoHitObject)?.HitObject.IsStrong ?? false) ? big_hit : normal_hit; - return skin.GetAnimation($"{prefix}{lookup}", true, false) ?? skin.GetAnimation($"{normal_hit}{lookup}", true, false); + return skin.GetAnimation($"{prefix}{lookup}", true, false) ?? + // fallback to regular size if "big" version doesn't exist. + skin.GetAnimation($"{normal_hit}{lookup}", true, false); } - // backgroundLayer is guaranteed to exist due to the pre-check in TaikoLegacySkinTransformer + // backgroundLayer is guaranteed to exist due to the pre-check in TaikoLegacySkinTransformer. AddInternal(backgroundLayer = getDrawableFor("circle")); var foregroundLayer = getDrawableFor("circleoverlay"); if (foregroundLayer != null) AddInternal(foregroundLayer); - // animations in taiko skins are used in a custom way (>150 combo and animating in time with beat). - // for now just stop at first frame for sanity. + // Animations in taiko skins are used in a custom way (>150 combo and animating in time with beat). + // For now just stop at first frame for sanity. foreach (var c in InternalChildren) { (c as IFramedAnimation)?.Stop(); @@ -66,8 +68,8 @@ namespace osu.Game.Rulesets.Taiko.Skinning { base.Update(); - // not all skins (including the default osu-stable) have similar sizes for hitcircle and hitcircleoverlay. - // this ensures they are scaled relative to each other but also match the expected DrawableHit size. + // Not all skins (including the default osu-stable) have similar sizes for "hitcircle" and "hitcircleoverlay". + // This ensures they are scaled relative to each other but also match the expected DrawableHit size. foreach (var c in InternalChildren) c.Scale = new Vector2(DrawWidth / 128); } From a843793957583694bd12bcb765068da79c9388eb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 11 Apr 2020 16:41:11 +0900 Subject: [PATCH 0143/1117] Un-nest class --- osu.Game/Screens/Select/BeatmapCarousel.cs | 4 +- .../Screens/Select/DifficultyRecommender.cs | 75 +++++++++++++++++++ osu.Game/Screens/Select/SongSelect.cs | 62 --------------- 3 files changed, 78 insertions(+), 63 deletions(-) create mode 100644 osu.Game/Screens/Select/DifficultyRecommender.cs diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 3e619a1f80..555c74fb44 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -117,8 +117,10 @@ namespace osu.Game.Screens.Select private readonly Stack randomSelectedBeatmaps = new Stack(); protected List Items = new List(); + private CarouselRoot root; - public SongSelect.DifficultyRecommender DifficultyRecommender; + + public DifficultyRecommender DifficultyRecommender; public BeatmapCarousel() { diff --git a/osu.Game/Screens/Select/DifficultyRecommender.cs b/osu.Game/Screens/Select/DifficultyRecommender.cs new file mode 100644 index 0000000000..d89d505f61 --- /dev/null +++ b/osu.Game/Screens/Select/DifficultyRecommender.cs @@ -0,0 +1,75 @@ +// 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; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Graphics; +using osu.Game.Beatmaps; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Rulesets; + +namespace osu.Game.Screens.Select +{ + public class DifficultyRecommender : Component + { + [Resolved] + private IAPIProvider api { get; set; } + + [Resolved] + private RulesetStore rulesets { get; set; } + + private readonly Dictionary recommendedStarDifficulty = new Dictionary(); + + private int pendingAPIRequests; + + [BackgroundDependencyLoader] + private void load() + { + updateRecommended(); + } + + private void updateRecommended() + { + if (pendingAPIRequests > 0) + return; + if (api.LocalUser.Value is GuestUser) + return; + + rulesets.AvailableRulesets.ForEach(rulesetInfo => + { + var req = new GetUserRequest(api.LocalUser.Value.Id, rulesetInfo); + + req.Success += result => + { + // algorithm taken from https://github.com/ppy/osu-web/blob/e6e2825516449e3d0f3f5e1852c6bdd3428c3437/app/Models/User.php#L1505 + recommendedStarDifficulty[rulesetInfo] = Math.Pow((double)(result.Statistics.PP ?? 0), 0.4) * 0.195; + pendingAPIRequests--; + }; + + req.Failure += _ => pendingAPIRequests--; + + pendingAPIRequests++; + api.Queue(req); + }); + } + + public BeatmapInfo GetRecommendedBeatmap(IEnumerable beatmaps, RulesetInfo currentRuleset) + { + if (!recommendedStarDifficulty.ContainsKey(currentRuleset)) + { + updateRecommended(); + return null; + } + + return beatmaps.OrderBy(b => + { + var difference = b.StarDifficulty - recommendedStarDifficulty[currentRuleset]; + return difference >= 0 ? difference * 2 : difference * -1; // prefer easier over harder + }).FirstOrDefault(); + } + } +} diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index d6bc20df39..9897515615 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -36,9 +36,6 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; using osu.Game.Overlays.Notifications; using osu.Game.Scoring; -using osu.Game.Online.API; -using osu.Framework.Extensions.IEnumerableExtensions; -using osu.Game.Online.API.Requests; namespace osu.Game.Screens.Select { @@ -787,65 +784,6 @@ namespace osu.Game.Screens.Select return base.OnKeyDown(e); } - public class DifficultyRecommender : Component - { - [Resolved] - private IAPIProvider api { get; set; } - - [Resolved] - private RulesetStore rulesets { get; set; } - - private readonly Dictionary recommendedStarDifficulty = new Dictionary(); - - private int pendingAPIRequests; - - [BackgroundDependencyLoader] - private void load() - { - updateRecommended(); - } - - private void updateRecommended() - { - if (pendingAPIRequests > 0) - return; - if (api.LocalUser.Value is GuestUser) - return; - - rulesets.AvailableRulesets.ForEach(rulesetInfo => - { - var req = new GetUserRequest(api.LocalUser.Value.Id, rulesetInfo); - - req.Success += result => - { - // algorithm taken from https://github.com/ppy/osu-web/blob/e6e2825516449e3d0f3f5e1852c6bdd3428c3437/app/Models/User.php#L1505 - recommendedStarDifficulty[rulesetInfo] = Math.Pow((double)(result.Statistics.PP ?? 0), 0.4) * 0.195; - pendingAPIRequests--; - }; - - req.Failure += _ => pendingAPIRequests--; - - pendingAPIRequests++; - api.Queue(req); - }); - } - - public BeatmapInfo GetRecommendedBeatmap(IEnumerable beatmaps, RulesetInfo currentRuleset) - { - if (!recommendedStarDifficulty.ContainsKey(currentRuleset)) - { - updateRecommended(); - return null; - } - - return beatmaps.OrderBy(b => - { - var difference = b.StarDifficulty - recommendedStarDifficulty[currentRuleset]; - return difference >= 0 ? difference * 2 : difference * -1; // prefer easier over harder - }).FirstOrDefault(); - } - } - private class VerticalMaskingContainer : Container { private const float panel_overflow = 1.2f; From 7f753f6b4d79a2bbee5c169cdd10e39f4dd158e0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 11 Apr 2020 16:43:09 +0900 Subject: [PATCH 0144/1117] Remove current ruleset from function call --- osu.Game/Screens/Select/BeatmapCarousel.cs | 2 +- osu.Game/Screens/Select/DifficultyRecommender.cs | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 555c74fb44..7139b804b0 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -588,7 +588,7 @@ namespace osu.Game.Screens.Select BeatmapInfo recommender(IEnumerable beatmaps) { - return DifficultyRecommender?.GetRecommendedBeatmap(beatmaps, decoupledRuleset.Value); + return DifficultyRecommender?.GetRecommendedBeatmap(beatmaps); } var set = new CarouselBeatmapSet(beatmapSet, recommender); diff --git a/osu.Game/Screens/Select/DifficultyRecommender.cs b/osu.Game/Screens/Select/DifficultyRecommender.cs index d89d505f61..fb67d63818 100644 --- a/osu.Game/Screens/Select/DifficultyRecommender.cs +++ b/osu.Game/Screens/Select/DifficultyRecommender.cs @@ -5,6 +5,7 @@ using System; 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.Game.Beatmaps; @@ -22,6 +23,9 @@ namespace osu.Game.Screens.Select [Resolved] private RulesetStore rulesets { get; set; } + [Resolved] + private Bindable ruleset { get; set; } + private readonly Dictionary recommendedStarDifficulty = new Dictionary(); private int pendingAPIRequests; @@ -57,9 +61,9 @@ namespace osu.Game.Screens.Select }); } - public BeatmapInfo GetRecommendedBeatmap(IEnumerable beatmaps, RulesetInfo currentRuleset) + public BeatmapInfo GetRecommendedBeatmap(IEnumerable beatmaps) { - if (!recommendedStarDifficulty.ContainsKey(currentRuleset)) + if (!recommendedStarDifficulty.ContainsKey(ruleset.Value)) { updateRecommended(); return null; @@ -67,7 +71,7 @@ namespace osu.Game.Screens.Select return beatmaps.OrderBy(b => { - var difference = b.StarDifficulty - recommendedStarDifficulty[currentRuleset]; + var difference = b.StarDifficulty - recommendedStarDifficulty[ruleset.Value]; return difference >= 0 ? difference * 2 : difference * -1; // prefer easier over harder }).FirstOrDefault(); } From abea7b5299a5fc38c12742aad9d741dec3be7f3a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 11 Apr 2020 16:58:13 +0900 Subject: [PATCH 0145/1117] Tidy up function passing, naming, ordering etc. --- osu.Game/Screens/Select/BeatmapCarousel.cs | 15 +++---- .../Select/Carousel/CarouselBeatmapSet.cs | 13 +++--- .../Screens/Select/DifficultyRecommender.cs | 42 +++++++++++-------- osu.Game/Screens/Select/SongSelect.cs | 8 ++-- 4 files changed, 42 insertions(+), 36 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 7139b804b0..3e3bb4dbc5 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -49,6 +49,11 @@ namespace osu.Game.Screens.Select /// public BeatmapSetInfo SelectedBeatmapSet => selectedBeatmapSet?.BeatmapSet; + /// + /// A function to optionally decide on a recommended difficulty from a beatmap set. + /// + public Func, BeatmapInfo> GetRecommendedBeatmap; + private CarouselBeatmapSet selectedBeatmapSet; /// @@ -120,8 +125,6 @@ namespace osu.Game.Screens.Select private CarouselRoot root; - public DifficultyRecommender DifficultyRecommender; - public BeatmapCarousel() { root = new CarouselRoot(this); @@ -586,12 +589,10 @@ namespace osu.Game.Screens.Select b.Metadata = beatmapSet.Metadata; } - BeatmapInfo recommender(IEnumerable beatmaps) + var set = new CarouselBeatmapSet(beatmapSet) { - return DifficultyRecommender?.GetRecommendedBeatmap(beatmaps); - } - - var set = new CarouselBeatmapSet(beatmapSet, recommender); + GetRecommendedBeatmap = beatmaps => GetRecommendedBeatmap?.Invoke(beatmaps) + }; foreach (var c in set.Beatmaps) { diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs index 99ded4c58e..92ccfde14b 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs @@ -12,13 +12,13 @@ namespace osu.Game.Screens.Select.Carousel { public class CarouselBeatmapSet : CarouselGroupEagerSelect { - private readonly Func, BeatmapInfo> getRecommendedBeatmap; - public IEnumerable Beatmaps => InternalChildren.OfType(); public BeatmapSetInfo BeatmapSet; - public CarouselBeatmapSet(BeatmapSetInfo beatmapSet, Func, BeatmapInfo> getRecommendedBeatmap) + public Func, BeatmapInfo> GetRecommendedBeatmap; + + public CarouselBeatmapSet(BeatmapSetInfo beatmapSet) { BeatmapSet = beatmapSet ?? throw new ArgumentNullException(nameof(beatmapSet)); @@ -26,8 +26,6 @@ namespace osu.Game.Screens.Select.Carousel .Where(b => !b.Hidden) .Select(b => new CarouselBeatmap(b)) .ForEach(AddChild); - - this.getRecommendedBeatmap = getRecommendedBeatmap; } protected override DrawableCarouselItem CreateDrawableRepresentation() => new DrawableCarouselBeatmapSet(this); @@ -36,9 +34,8 @@ namespace osu.Game.Screens.Select.Carousel { if (LastSelected == null) { - var recommendedBeatmapInfo = getRecommendedBeatmap(Children.OfType().Where(b => !b.Filtered.Value).Select(b => b.Beatmap)); - if (recommendedBeatmapInfo != null) - return Children.OfType().First(b => b.Beatmap == recommendedBeatmapInfo); + if (GetRecommendedBeatmap?.Invoke(Children.OfType().Where(b => !b.Filtered.Value).Select(b => b.Beatmap)) is BeatmapInfo recommended) + return Children.OfType().First(b => b.Beatmap == recommended); } return base.GetNextToSelect(); diff --git a/osu.Game/Screens/Select/DifficultyRecommender.cs b/osu.Game/Screens/Select/DifficultyRecommender.cs index fb67d63818..47838ebd6d 100644 --- a/osu.Game/Screens/Select/DifficultyRecommender.cs +++ b/osu.Game/Screens/Select/DifficultyRecommender.cs @@ -33,10 +33,33 @@ namespace osu.Game.Screens.Select [BackgroundDependencyLoader] private void load() { - updateRecommended(); + calculateRecommendedDifficulties(); } - private void updateRecommended() + /// + /// Find the recommended difficulty from a selection of available difficulties for the current local user. + /// + /// + /// This requires the user to be online for now. + /// + /// A collection of beatmaps to select a difficulty from. + /// The recommended difficulty, or null if a recommendation could not be provided. + public BeatmapInfo GetRecommendedBeatmap(IEnumerable beatmaps) + { + if (!recommendedStarDifficulty.ContainsKey(ruleset.Value)) + { + calculateRecommendedDifficulties(); + return null; + } + + return beatmaps.OrderBy(b => + { + var difference = b.StarDifficulty - recommendedStarDifficulty[ruleset.Value]; + return difference >= 0 ? difference * 2 : difference * -1; // prefer easier over harder + }).FirstOrDefault(); + } + + private void calculateRecommendedDifficulties() { if (pendingAPIRequests > 0) return; @@ -60,20 +83,5 @@ namespace osu.Game.Screens.Select api.Queue(req); }); } - - public BeatmapInfo GetRecommendedBeatmap(IEnumerable beatmaps) - { - if (!recommendedStarDifficulty.ContainsKey(ruleset.Value)) - { - updateRecommended(); - return null; - } - - return beatmaps.OrderBy(b => - { - var difference = b.StarDifficulty - recommendedStarDifficulty[ruleset.Value]; - return difference >= 0 ? difference * 2 : difference * -1; // prefer easier over harder - }).FirstOrDefault(); - } } } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 9897515615..f164056ede 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -80,7 +80,8 @@ namespace osu.Game.Screens.Select protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap.Value); protected BeatmapCarousel Carousel { get; private set; } - private DifficultyRecommender difficultyRecommender; + + private DifficultyRecommender recommender; private BeatmapInfoWedge beatmapInfoWedge; private DialogOverlay dialogOverlay; @@ -108,10 +109,9 @@ namespace osu.Game.Screens.Select // initial value transfer is required for FilterControl (it uses our re-cached bindables in its async load for the initial filter). transferRulesetValue(); - AddInternal(difficultyRecommender = new DifficultyRecommender()); - AddRangeInternal(new Drawable[] { + recommender = new DifficultyRecommender(), new ResetScrollContainer(() => Carousel.ScrollToSelected()) { RelativeSizeAxes = Axes.Y, @@ -159,7 +159,7 @@ namespace osu.Game.Screens.Select RelativeSizeAxes = Axes.Both, SelectionChanged = updateSelectedBeatmap, BeatmapSetsChanged = carouselBeatmapsLoaded, - DifficultyRecommender = difficultyRecommender, + GetRecommendedBeatmap = recommender.GetRecommendedBeatmap, }, } }, From 310cf830d47a4618f33b2bc1fe646aa516823ea8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 11 Apr 2020 17:07:08 +0900 Subject: [PATCH 0146/1117] Simplify api request logic --- .../Screens/Select/DifficultyRecommender.cs | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/osu.Game/Screens/Select/DifficultyRecommender.cs b/osu.Game/Screens/Select/DifficultyRecommender.cs index 47838ebd6d..595bfd6122 100644 --- a/osu.Game/Screens/Select/DifficultyRecommender.cs +++ b/osu.Game/Screens/Select/DifficultyRecommender.cs @@ -15,7 +15,7 @@ using osu.Game.Rulesets; namespace osu.Game.Screens.Select { - public class DifficultyRecommender : Component + public class DifficultyRecommender : Component, IOnlineComponent { [Resolved] private IAPIProvider api { get; set; } @@ -28,12 +28,10 @@ namespace osu.Game.Screens.Select private readonly Dictionary recommendedStarDifficulty = new Dictionary(); - private int pendingAPIRequests; - [BackgroundDependencyLoader] private void load() { - calculateRecommendedDifficulties(); + api.Register(this); } /// @@ -48,7 +46,6 @@ namespace osu.Game.Screens.Select { if (!recommendedStarDifficulty.ContainsKey(ruleset.Value)) { - calculateRecommendedDifficulties(); return null; } @@ -61,11 +58,6 @@ namespace osu.Game.Screens.Select private void calculateRecommendedDifficulties() { - if (pendingAPIRequests > 0) - return; - if (api.LocalUser.Value is GuestUser) - return; - rulesets.AvailableRulesets.ForEach(rulesetInfo => { var req = new GetUserRequest(api.LocalUser.Value.Id, rulesetInfo); @@ -74,14 +66,27 @@ namespace osu.Game.Screens.Select { // algorithm taken from https://github.com/ppy/osu-web/blob/e6e2825516449e3d0f3f5e1852c6bdd3428c3437/app/Models/User.php#L1505 recommendedStarDifficulty[rulesetInfo] = Math.Pow((double)(result.Statistics.PP ?? 0), 0.4) * 0.195; - pendingAPIRequests--; }; - req.Failure += _ => pendingAPIRequests--; - - pendingAPIRequests++; api.Queue(req); }); } + + public void APIStateChanged(IAPIProvider api, APIState state) + { + switch (state) + { + case APIState.Online: + calculateRecommendedDifficulties(); + break; + } + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + api.Unregister(this); + } } } From 7aac0e59a8c02ab765b53b196177a79f43bb294e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 11 Apr 2020 17:08:07 +0900 Subject: [PATCH 0147/1117] Reduce dictionary lookups --- osu.Game/Screens/Select/DifficultyRecommender.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Select/DifficultyRecommender.cs b/osu.Game/Screens/Select/DifficultyRecommender.cs index 595bfd6122..20cdca858a 100644 --- a/osu.Game/Screens/Select/DifficultyRecommender.cs +++ b/osu.Game/Screens/Select/DifficultyRecommender.cs @@ -44,16 +44,16 @@ namespace osu.Game.Screens.Select /// The recommended difficulty, or null if a recommendation could not be provided. public BeatmapInfo GetRecommendedBeatmap(IEnumerable beatmaps) { - if (!recommendedStarDifficulty.ContainsKey(ruleset.Value)) + if (recommendedStarDifficulty.TryGetValue(ruleset.Value, out var stars)) { - return null; + return beatmaps.OrderBy(b => + { + var difference = b.StarDifficulty - stars; + return difference >= 0 ? difference * 2 : difference * -1; // prefer easier over harder + }).FirstOrDefault(); } - return beatmaps.OrderBy(b => - { - var difference = b.StarDifficulty - recommendedStarDifficulty[ruleset.Value]; - return difference >= 0 ? difference * 2 : difference * -1; // prefer easier over harder - }).FirstOrDefault(); + return null; } private void calculateRecommendedDifficulties() From c0c1f2c0235c9e49c8c44541ad1d266a182bb017 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 11 Apr 2020 17:17:18 +0900 Subject: [PATCH 0148/1117] Add test coverage --- .../SongSelect/TestSceneBeatmapCarousel.cs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs index 76a8ee9914..f68ed4154b 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs @@ -54,6 +54,35 @@ namespace osu.Game.Tests.Visual.SongSelect this.rulesets = rulesets; } + [Test] + public void TestRecommendedSelection() + { + loadBeatmaps(); + + AddStep("set recommendation function", () => carousel.GetRecommendedBeatmap = beatmaps => beatmaps.LastOrDefault()); + + // check recommended was selected + advanceSelection(direction: 1, diff: false); + waitForSelection(1, 3); + + // change away from recommended + advanceSelection(direction: -1, diff: true); + waitForSelection(1, 2); + + // next set, check recommended + advanceSelection(direction: 1, diff: false); + waitForSelection(2, 3); + + // next set, check recommended + advanceSelection(direction: 1, diff: false); + waitForSelection(3, 3); + + // go back to first set and ensure user selection was retained + advanceSelection(direction: -1, diff: false); + advanceSelection(direction: -1, diff: false); + waitForSelection(1, 2); + } + /// /// Test keyboard traversal /// From 73a3f1fe65d6eb17ca9eef1a5b3cd8a843f0e3eb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 11 Apr 2020 17:30:34 +0900 Subject: [PATCH 0149/1117] Remove unnecessary DI --- osu.Game/Screens/Select/BeatmapCarousel.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 3e3bb4dbc5..a8225ba1ec 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -23,7 +23,6 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Cursor; using osu.Game.Input.Bindings; using osu.Game.Screens.Select.Carousel; -using osu.Game.Rulesets; namespace osu.Game.Screens.Select { @@ -146,9 +145,6 @@ namespace osu.Game.Screens.Select [Resolved] private BeatmapManager beatmaps { get; set; } - [Resolved] - private Bindable decoupledRuleset { get; set; } - [BackgroundDependencyLoader(permitNulls: true)] private void load(OsuConfigManager config) { From 832822858ca2c17eac82ab669f4c10634978c58c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 11 Apr 2020 17:47:51 +0900 Subject: [PATCH 0150/1117] Add basic request / response support --- .../Online/TestDummyAPIRequestHandling.cs | 39 +++++++++++++++++++ osu.Game/Online/API/APIRequest.cs | 2 + osu.Game/Online/API/DummyAPIAccess.cs | 7 ++++ 3 files changed, 48 insertions(+) create mode 100644 osu.Game.Tests/Online/TestDummyAPIRequestHandling.cs diff --git a/osu.Game.Tests/Online/TestDummyAPIRequestHandling.cs b/osu.Game.Tests/Online/TestDummyAPIRequestHandling.cs new file mode 100644 index 0000000000..bf3e1204d7 --- /dev/null +++ b/osu.Game.Tests/Online/TestDummyAPIRequestHandling.cs @@ -0,0 +1,39 @@ +// 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; +using osu.Game.Online.API.Requests; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Tests.Visual; + +namespace osu.Game.Tests.Online +{ + public class TestDummyAPIRequestHandling : OsuTestScene + { + public TestDummyAPIRequestHandling() + { + AddStep("register request handling", () => ((DummyAPIAccess)API).HandleRequest = req => + { + switch (req) + { + case CommentVoteRequest cRequest: + cRequest.TriggerSuccess(new CommentBundle()); + break; + } + }); + + CommentVoteRequest request = null; + CommentBundle response = null; + + AddStep("fire request", () => + { + response = null; + request = new CommentVoteRequest(1, CommentVoteAction.Vote); + request.Success += res => response = res; + API.Queue(request); + }); + + AddAssert("got response", () => response != null); + } + } +} diff --git a/osu.Game/Online/API/APIRequest.cs b/osu.Game/Online/API/APIRequest.cs index 6a6c7b72a8..1f0eae4965 100644 --- a/osu.Game/Online/API/APIRequest.cs +++ b/osu.Game/Online/API/APIRequest.cs @@ -30,6 +30,8 @@ namespace osu.Game.Online.API /// This will be scheduled to the API's internal scheduler (run on update thread automatically). /// public new event APISuccessHandler Success; + + internal void TriggerSuccess(T result) => Success?.Invoke(result); } /// diff --git a/osu.Game/Online/API/DummyAPIAccess.cs b/osu.Game/Online/API/DummyAPIAccess.cs index a1c3475fd9..fa5ad115d2 100644 --- a/osu.Game/Online/API/DummyAPIAccess.cs +++ b/osu.Game/Online/API/DummyAPIAccess.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.Threading; using System.Threading.Tasks; @@ -30,6 +31,11 @@ namespace osu.Game.Online.API private readonly List components = new List(); + /// + /// Provide handling logic for an arbitrary API request. + /// + public Action HandleRequest; + public APIState State { get => state; @@ -55,6 +61,7 @@ namespace osu.Game.Online.API public virtual void Queue(APIRequest request) { + HandleRequest?.Invoke(request); } public void Perform(APIRequest request) { } From 415adecdf68c07d8882ca7ecdb2c099d6749243a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 11 Apr 2020 18:02:43 +0900 Subject: [PATCH 0151/1117] Add support for Result fetching --- osu.Game.Tests/Online/TestDummyAPIRequestHandling.cs | 8 ++++++-- osu.Game/Online/API/APIRequest.cs | 8 +++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Online/TestDummyAPIRequestHandling.cs b/osu.Game.Tests/Online/TestDummyAPIRequestHandling.cs index bf3e1204d7..5b169cccdf 100644 --- a/osu.Game.Tests/Online/TestDummyAPIRequestHandling.cs +++ b/osu.Game.Tests/Online/TestDummyAPIRequestHandling.cs @@ -10,7 +10,8 @@ namespace osu.Game.Tests.Online { public class TestDummyAPIRequestHandling : OsuTestScene { - public TestDummyAPIRequestHandling() + [Test] + public void TestGenericRequestHandling() { AddStep("register request handling", () => ((DummyAPIAccess)API).HandleRequest = req => { @@ -33,7 +34,10 @@ namespace osu.Game.Tests.Online API.Queue(request); }); - AddAssert("got response", () => response != null); + AddAssert("response event fired", () => response != null); + + AddAssert("request has response", () => request.Result == response); + } } } } diff --git a/osu.Game/Online/API/APIRequest.cs b/osu.Game/Online/API/APIRequest.cs index 1f0eae4965..34b69b3c09 100644 --- a/osu.Game/Online/API/APIRequest.cs +++ b/osu.Game/Online/API/APIRequest.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Diagnostics; using Newtonsoft.Json; using osu.Framework.IO.Network; using osu.Framework.Logging; @@ -98,10 +99,15 @@ namespace osu.Game.Online.API { if (cancelled) return; - Success?.Invoke(); + TriggerSuccess(); }); } + internal void TriggerSuccess() + { + Success?.Invoke(); + } + public void Cancel() => Fail(new OperationCanceledException(@"Request cancelled")); public void Fail(Exception e) From c96df9758674b58df599ca8f9153f9f5c1d3e206 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 11 Apr 2020 18:02:49 +0900 Subject: [PATCH 0152/1117] Add support for non-generic requests --- .../Online/TestDummyAPIRequestHandling.cs | 29 +++++++++++++++++++ osu.Game/Online/API/APIRequest.cs | 15 ++++++---- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Online/TestDummyAPIRequestHandling.cs b/osu.Game.Tests/Online/TestDummyAPIRequestHandling.cs index 5b169cccdf..b00b63f6d5 100644 --- a/osu.Game.Tests/Online/TestDummyAPIRequestHandling.cs +++ b/osu.Game.Tests/Online/TestDummyAPIRequestHandling.cs @@ -1,10 +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 NUnit.Framework; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Online.Chat; using osu.Game.Tests.Visual; +using osu.Game.Users; namespace osu.Game.Tests.Online { @@ -38,6 +41,32 @@ namespace osu.Game.Tests.Online AddAssert("request has response", () => request.Result == response); } + + [Test] + public void TestRequestHandling() + { + AddStep("register request handling", () => ((DummyAPIAccess)API).HandleRequest = req => + { + switch (req) + { + case LeaveChannelRequest cRequest: + cRequest.TriggerSuccess(); + break; + } + }); + + LeaveChannelRequest request; + bool gotResponse = false; + + AddStep("fire request", () => + { + gotResponse = false; + request = new LeaveChannelRequest(new Channel(), new User()); + request.Success += () => gotResponse = true; + API.Queue(request); + }); + + AddAssert("response event fired", () => gotResponse); } } } diff --git a/osu.Game/Online/API/APIRequest.cs b/osu.Game/Online/API/APIRequest.cs index 34b69b3c09..6abb388c01 100644 --- a/osu.Game/Online/API/APIRequest.cs +++ b/osu.Game/Online/API/APIRequest.cs @@ -17,22 +17,27 @@ namespace osu.Game.Online.API { protected override WebRequest CreateWebRequest() => new OsuJsonWebRequest(Uri); - public T Result => ((OsuJsonWebRequest)WebRequest)?.ResponseObject; + public T Result { get; private set; } protected APIRequest() { - base.Success += onSuccess; + base.Success += () => TriggerSuccess(((OsuJsonWebRequest)WebRequest)?.ResponseObject); } - private void onSuccess() => Success?.Invoke(Result); - /// /// Invoked on successful completion of an API request. /// This will be scheduled to the API's internal scheduler (run on update thread automatically). /// public new event APISuccessHandler Success; - internal void TriggerSuccess(T result) => Success?.Invoke(result); + internal void TriggerSuccess(T result) + { + // disallow calling twice + Debug.Assert(Result == null); + + Result = result; + Success?.Invoke(result); + } } /// From 1c0ad13d82bec928abb9f0cdb9e826f0cf23c7f8 Mon Sep 17 00:00:00 2001 From: Tim Oliver Date: Sat, 11 Apr 2020 17:20:37 +0800 Subject: [PATCH 0153/1117] Added ignore hit object --- osu.Game.Rulesets.Taiko/Objects/IgnoreHit.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 osu.Game.Rulesets.Taiko/Objects/IgnoreHit.cs diff --git a/osu.Game.Rulesets.Taiko/Objects/IgnoreHit.cs b/osu.Game.Rulesets.Taiko/Objects/IgnoreHit.cs new file mode 100644 index 0000000000..302f940ef4 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Objects/IgnoreHit.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.Game.Rulesets.Judgements; + +namespace osu.Game.Rulesets.Taiko.Objects +{ + public class IgnoreHit : Hit + { + public override Judgement CreateJudgement() => new IgnoreJudgement(); + } +} From 3ad36c7b84e920a13e48b160888cab9874bac20d Mon Sep 17 00:00:00 2001 From: Tim Oliver Date: Sat, 11 Apr 2020 17:20:52 +0800 Subject: [PATCH 0154/1117] Moved flying objects to use ignore hit judgements --- osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs | 3 ++- osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs index 86e885239f..4468c1e3fb 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; +using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; @@ -35,7 +36,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables } public DrawableFlyingCentreHit(double time, bool isStrong = false) - : base(new Hit { StartTime = time, IsStrong = isStrong }) + : base(new IgnoreHit { StartTime = time, IsStrong = isStrong }) { HitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs index ad9872b21f..e5cfc0562b 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs @@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables } public DrawableFlyingRimHit(double time, bool isStrong = false) - : base(new Hit { StartTime = time, IsStrong = isStrong }) + : base(new IgnoreHit { StartTime = time, IsStrong = isStrong }) { HitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); } From df76636ffc5be7d5d817a7a943ec56a505339855 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Sat, 11 Apr 2020 14:08:16 +0300 Subject: [PATCH 0155/1117] Implement "prefer no video" option --- osu.Game/Configuration/OsuConfigManager.cs | 2 ++ osu.Game/Overlays/Direct/PanelDownloadButton.cs | 10 ++++++---- .../Overlays/Settings/Sections/Online/WebSettings.cs | 5 +++++ 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 41f6747b74..89eb084262 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -49,6 +49,7 @@ namespace osu.Game.Configuration }; Set(OsuSetting.ExternalLinkWarning, true); + Set(OsuSetting.PreferNoVideo, false); // Audio Set(OsuSetting.VolumeInactive, 0.25, 0, 1, 0.01); @@ -212,6 +213,7 @@ namespace osu.Game.Configuration IncreaseFirstObjectVisibility, ScoreDisplayMode, ExternalLinkWarning, + PreferNoVideo, Scaling, ScalingPositionX, ScalingPositionY, diff --git a/osu.Game/Overlays/Direct/PanelDownloadButton.cs b/osu.Game/Overlays/Direct/PanelDownloadButton.cs index 1b3657f010..f09586c571 100644 --- a/osu.Game/Overlays/Direct/PanelDownloadButton.cs +++ b/osu.Game/Overlays/Direct/PanelDownloadButton.cs @@ -4,6 +4,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Beatmaps; +using osu.Game.Configuration; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online; @@ -14,12 +15,12 @@ namespace osu.Game.Overlays.Direct { protected bool DownloadEnabled => button.Enabled.Value; - private readonly bool noVideo; + private readonly bool? noVideo; private readonly ShakeContainer shakeContainer; private readonly DownloadButton button; - public PanelDownloadButton(BeatmapSetInfo beatmapSet, bool noVideo = false) + public PanelDownloadButton(BeatmapSetInfo beatmapSet, bool? noVideo = null) : base(beatmapSet) { this.noVideo = noVideo; @@ -43,7 +44,7 @@ namespace osu.Game.Overlays.Direct } [BackgroundDependencyLoader(true)] - private void load(OsuGame game, BeatmapManager beatmaps) + private void load(OsuGame game, BeatmapManager beatmaps, OsuConfigManager osuConfig) { if (BeatmapSet.Value?.OnlineInfo?.Availability?.DownloadDisabled ?? false) { @@ -66,7 +67,8 @@ namespace osu.Game.Overlays.Direct break; default: - beatmaps.Download(BeatmapSet.Value, noVideo); + var minimiseDownloadSize = noVideo ?? osuConfig.GetBindable(OsuSetting.PreferNoVideo).Value; + beatmaps.Download(BeatmapSet.Value, minimiseDownloadSize); break; } }; diff --git a/osu.Game/Overlays/Settings/Sections/Online/WebSettings.cs b/osu.Game/Overlays/Settings/Sections/Online/WebSettings.cs index a8b3e45a83..da3176aca8 100644 --- a/osu.Game/Overlays/Settings/Sections/Online/WebSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Online/WebSettings.cs @@ -21,6 +21,11 @@ namespace osu.Game.Overlays.Settings.Sections.Online LabelText = "Warn about opening external links", Bindable = config.GetBindable(OsuSetting.ExternalLinkWarning) }, + new SettingsCheckbox + { + LabelText = "Prefer no-video downloads", + Bindable = config.GetBindable(OsuSetting.PreferNoVideo) + }, }; } } From fc1d497a864c48adf3d702beb1c0b7c93d23ddcd Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Sat, 11 Apr 2020 14:21:28 +0300 Subject: [PATCH 0156/1117] Change PlaylistDownloadButton default --- osu.Game/Screens/Multi/DrawableRoomPlaylistItem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Multi/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/Multi/DrawableRoomPlaylistItem.cs index ed3f9af8e2..d58218b6b5 100644 --- a/osu.Game/Screens/Multi/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/Multi/DrawableRoomPlaylistItem.cs @@ -212,7 +212,7 @@ namespace osu.Game.Screens.Multi private class PlaylistDownloadButton : PanelDownloadButton { - public PlaylistDownloadButton(BeatmapSetInfo beatmapSet, bool noVideo = false) + public PlaylistDownloadButton(BeatmapSetInfo beatmapSet, bool? noVideo = null) : base(beatmapSet, noVideo) { Alpha = 0; From f274ec297ce23e79eff1d2a64f745e1396b0c280 Mon Sep 17 00:00:00 2001 From: Fire937 Date: Sun, 12 Apr 2020 01:33:25 +0200 Subject: [PATCH 0157/1117] Add positional sound support for all rulesets The SamplePlaybackBalance is calculated in a way that the balance remains between -0.4 and 0.4. Positional sound is not supported in osu!taiko. --- .../Drawables/DrawableCatchHitObject.cs | 2 ++ .../Drawables/DrawableManiaHitObject.cs | 16 ++++++++++++ .../Objects/Drawables/DrawableOsuHitObject.cs | 2 ++ .../Objects/Drawables/DrawableHitObject.cs | 26 ++++++------------- 4 files changed, 28 insertions(+), 18 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs index 6844be5941..e726d6eff5 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs @@ -70,6 +70,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables public float DisplayRadius => DrawSize.X / 2 * Scale.X * HitObject.Scale; + protected override float SamplePlaybackBalance => 0.8f * HitObject.X - 0.4f; + protected DrawableCatchHitObject(CatchHitObject hitObject) : base(hitObject) { diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs index 5bfa07bd14..76e9695855 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs @@ -5,8 +5,10 @@ using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI.Scrolling; +using osu.Game.Rulesets.Mania.UI; namespace osu.Game.Rulesets.Mania.Objects.Drawables { @@ -24,6 +26,20 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables protected readonly IBindable Direction = new Bindable(); + protected override float SamplePlaybackBalance + { + get + { + CompositeDrawable stage = this; + while (!(stage is Stage)) + stage = stage.Parent; + + var columnCount = ((Stage)stage).Columns.Count; + var columnIndex = HitObject.Column; + return 0.8f * columnIndex / (columnCount - 1) - 0.4f; + } + } + protected DrawableManiaHitObject(ManiaHitObject hitObject) : base(hitObject) { diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index a677cb6a72..c5a4491b2d 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -16,6 +16,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables // Must be set to update IsHovered as it's used in relax mdo to detect osu hit objects. public override bool HandlePositionalInput => true; + protected override float SamplePlaybackBalance => (HitObject.X / 512f - 0.5f) * 0.8f; + protected DrawableOsuHitObject(OsuHitObject hitObject) : base(hitObject) { diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 30a9106ddc..ef32dc1560 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -33,11 +33,6 @@ namespace osu.Game.Rulesets.Objects.Drawables /// public readonly Bindable AccentColour = new Bindable(Color4.Gray); - /// - /// The stereo balance of the samples if the Positional hitsounds setting is set. - /// - private readonly BindableDouble positionalSoundAdjustment = new BindableDouble(); - protected SkinnableSound Samples { get; private set; } protected virtual IEnumerable GetSamples() => HitObject.Samples; @@ -94,7 +89,9 @@ namespace osu.Game.Rulesets.Objects.Drawables /// /// The stereo balance of the samples played if Positional hitsounds is set. /// - protected virtual float PositionalSound => (Position.X / 512f - 0.5f) * 0.8f; + protected virtual float SamplePlaybackBalance => 0; + + private readonly BindableDouble samplePlaybackBalanceAdjustment = new BindableDouble(); private BindableList samplesBindable; private Bindable startTimeBindable; @@ -119,6 +116,7 @@ namespace osu.Game.Rulesets.Objects.Drawables [BackgroundDependencyLoader] private void load(OsuConfigManager config) { + userPositionalHitSounds = config.GetBindable(OsuSetting.PositionalHitSounds); var judgement = HitObject.CreateJudgement(); Result = CreateResult(judgement); @@ -126,16 +124,6 @@ namespace osu.Game.Rulesets.Objects.Drawables throw new InvalidOperationException($"{GetType().ReadableName()} must provide a {nameof(JudgementResult)} through {nameof(CreateResult)}."); loadSamples(); - - userPositionalHitSounds = config.GetBindable(OsuSetting.PositionalHitSounds); - userPositionalHitSounds.BindValueChanged(positional => - { - if (positional.NewValue) - Samples?.AddAdjustment(AdjustableProperty.Balance, positionalSoundAdjustment); - else - Samples?.RemoveAdjustment(AdjustableProperty.Balance, positionalSoundAdjustment); - }); - userPositionalHitSounds.TriggerChange(); } protected override void LoadComplete() @@ -179,7 +167,9 @@ namespace osu.Game.Rulesets.Objects.Drawables + $" This is an indication that {nameof(HitObject.ApplyDefaults)} has not been invoked on {this}."); } - AddInternal(Samples = new SkinnableSound(samples.Select(s => HitObject.SampleControlPoint.ApplyTo(s)))); + Samples = new SkinnableSound(samples.Select(s => HitObject.SampleControlPoint.ApplyTo(s))); + Samples.AddAdjustment(AdjustableProperty.Balance, samplePlaybackBalanceAdjustment); + AddInternal(Samples); } private void onDefaultsApplied() => apply(HitObject); @@ -378,7 +368,7 @@ namespace osu.Game.Rulesets.Objects.Drawables /// public virtual void PlaySamples() { - positionalSoundAdjustment.Value = PositionalSound; + samplePlaybackBalanceAdjustment.Value = userPositionalHitSounds.Value ? SamplePlaybackBalance : 0; Samples?.Play(); } From 162a85042a6e15be69f57388e50669e174f81792 Mon Sep 17 00:00:00 2001 From: Tim Oliver Date: Sun, 12 Apr 2020 10:38:22 +0800 Subject: [PATCH 0158/1117] Removed un-needed using --- osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs index 4468c1e3fb..b6f6f04821 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs @@ -5,7 +5,6 @@ using osu.Framework.Allocation; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; -using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; From 7a9ee907bf4d862c1e49188a8c835a2412977fb9 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 12 Apr 2020 07:34:58 +0300 Subject: [PATCH 0159/1117] Fix incorrect button state in some cases --- osu.Game/Overlays/OverlayScrollContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/OverlayScrollContainer.cs b/osu.Game/Overlays/OverlayScrollContainer.cs index a6c687f28f..a8f2a0ce6c 100644 --- a/osu.Game/Overlays/OverlayScrollContainer.cs +++ b/osu.Game/Overlays/OverlayScrollContainer.cs @@ -60,7 +60,7 @@ namespace osu.Game.Overlays return; currentTarget = Target; - Button.State = Current > button_scroll_position ? Visibility.Visible : Visibility.Hidden; + Button.State = Target > button_scroll_position ? Visibility.Visible : Visibility.Hidden; } public class ScrollToTopButton : OsuHoverContainer From f8b728f9e86e7bb0c8e2a6686859201dc7b26f49 Mon Sep 17 00:00:00 2001 From: Tim Oliver Date: Tue, 7 Apr 2020 16:39:41 +0800 Subject: [PATCH 0160/1117] =?UTF-8?q?Added=20=E2=80=9Cinstant=20fly?= =?UTF-8?q?=E2=80=9D=20variant=20of=20hit=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Objects/Drawables/DrawableCentreHit.cs | 17 +++++++++++++++++ .../Objects/Drawables/DrawableRimHit.cs | 17 +++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs index 4979135f50..08df05e719 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs @@ -2,7 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; +using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; namespace osu.Game.Rulesets.Taiko.Objects.Drawables @@ -23,4 +26,18 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables MainPiece.AccentColour = colours.PinkDarker; } } + + public class DrawableFlyingCentreHit : DrawableCentreHit + { + protected override void CheckForResult(bool userTriggered, double timeOffset) + { + ApplyResult(r => r.Type = HitResult.Good); + } + + public DrawableFlyingCentreHit(double time) + : base(new Hit { StartTime = time }) + { + HitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); + } + } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs index 5a12d71cea..0c2c9fbdef 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs @@ -2,7 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; +using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; namespace osu.Game.Rulesets.Taiko.Objects.Drawables @@ -23,4 +26,18 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables MainPiece.AccentColour = colours.BlueDarker; } } + + public class DrawableFlyingRimHit : DrawableRimHit + { + protected override void CheckForResult(bool userTriggered, double timeOffset) + { + ApplyResult(r => r.Type = HitResult.Good); + } + + public DrawableFlyingRimHit(double time) + : base(new Hit { StartTime = time }) + { + HitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); + } + } } From a52130857bdefc35656ff4357ff8ee3ee1783174 Mon Sep 17 00:00:00 2001 From: Tim Oliver Date: Tue, 7 Apr 2020 16:40:01 +0800 Subject: [PATCH 0161/1117] Added arbitrary hit handler to drum roll object --- .../Objects/Drawables/DrawableDrumRoll.cs | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs index 5806c90115..3e7b6dfd31 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs @@ -34,6 +34,14 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables private Color4 colourIdle; private Color4 colourEngaged; + private bool judgingStarted; + + /// + /// A handler action for when the drumroll has been hit, + /// regardless of any judgement. + /// + public Action OnHit; + public DrawableDrumRoll(DrumRoll drumRoll) : base(drumRoll) { @@ -86,15 +94,27 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables protected override TaikoPiece CreateMainPiece() => new ElongatedCirclePiece(); - public override bool OnPressed(TaikoAction action) => false; + public override bool OnPressed(TaikoAction action) + { + if (judgingStarted) + OnHit.Invoke(action); + + return false; + } private void onNewResult(DrawableHitObject obj, JudgementResult result) { if (!(obj is DrawableDrumRollTick)) return; + DrawableDrumRollTick drumRollTick = (DrawableDrumRollTick)obj; + if (result.Type > HitResult.Miss) + { + OnHit.Invoke(drumRollTick.JudgedAction); + judgingStarted = true; rollingHits++; + } else rollingHits--; @@ -113,8 +133,11 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables return; int countHit = NestedHitObjects.Count(o => o.IsHit); + if (countHit >= HitObject.RequiredGoodHits) + { ApplyResult(r => r.Type = countHit >= HitObject.RequiredGreatHits ? HitResult.Great : HitResult.Good); + } else ApplyResult(r => r.Type = HitResult.Miss); } From 81a514ee6a38b37cb1b9a64e947f2087eba287cd Mon Sep 17 00:00:00 2001 From: Tim Oliver Date: Tue, 7 Apr 2020 16:40:18 +0800 Subject: [PATCH 0162/1117] Added judgement forwarder to drumroll tick object --- .../Objects/Drawables/DrawableDrumRollTick.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs index 25b6141a0e..9961cb6ea2 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs @@ -11,6 +11,12 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { public class DrawableDrumRollTick : DrawableTaikoHitObject { + /// + /// The action type that the user took which caused this tick to + /// have been judged as "hit" + /// + public TaikoAction JudgedAction; + public DrawableDrumRollTick(DrumRollTick tick) : base(tick) { @@ -49,7 +55,11 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables } } - public override bool OnPressed(TaikoAction action) => UpdateResult(true); + public override bool OnPressed(TaikoAction action) + { + JudgedAction = action; + return UpdateResult(true); + } protected override DrawableStrongNestedHit CreateStrongHit(StrongHitObject hitObject) => new StrongNestedHit(hitObject, this); From fbcfc7d278a7ed779839c7ade8a395db9b4bc53a Mon Sep 17 00:00:00 2001 From: Tim Oliver Date: Tue, 7 Apr 2020 16:40:32 +0800 Subject: [PATCH 0163/1117] Added separate scrolling track to display drum roll notes --- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 25 ++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index bde9085c23..b32e7b53da 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -42,6 +42,7 @@ namespace osu.Game.Rulesets.Taiko.UI private readonly Container hitExplosionContainer; private readonly Container kiaiExplosionContainer; private readonly JudgementContainer judgementContainer; + private readonly ScrollingHitObjectContainer drumRollHitContainer; internal readonly HitTarget HitTarget; private readonly ProxyContainer topLevelHitContainer; @@ -135,6 +136,14 @@ namespace osu.Game.Rulesets.Taiko.UI Margin = new MarginPadding { Left = HIT_TARGET_OFFSET }, Blending = BlendingParameters.Additive }, + drumRollHitContainer = new ScrollingHitObjectContainer + { + Name = "Drumroll hit", + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Stretch, + Margin = new MarginPadding { Left = HIT_TARGET_OFFSET }, + Width = 1.0f + } } }, overlayBackgroundContainer = new Container @@ -212,12 +221,28 @@ namespace osu.Game.Rulesets.Taiko.UI barlineContainer.Add(barline.CreateProxy()); break; + case DrawableDrumRoll drumRoll: + drumRoll.OnHit += onDrumrollArbitraryHit; + break; + case DrawableTaikoHitObject taikoObject: topLevelHitContainer.Add(taikoObject.CreateProxiedContent()); break; } } + private void onDrumrollArbitraryHit(TaikoAction action) + { + DrawableHit drawableHit; + + if (action == TaikoAction.LeftRim || action == TaikoAction.RightRim) + drawableHit = new DrawableFlyingRimHit(Time.Current); + else + drawableHit = new DrawableFlyingCentreHit(Time.Current); + + drumRollHitContainer.Add(drawableHit); + } + internal void OnNewResult(DrawableHitObject judgedObject, JudgementResult result) { if (!DisplayJudgements.Value) From c6d996030a83763d4b03ff1e5a819e6958d9f973 Mon Sep 17 00:00:00 2001 From: Tim Oliver Date: Tue, 7 Apr 2020 17:25:47 +0800 Subject: [PATCH 0164/1117] Added logic to allow strong notes --- .../Objects/Drawables/DrawableCentreHit.cs | 4 ++-- .../Objects/Drawables/DrawableDrumRoll.cs | 6 +++--- osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs | 4 ++-- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 6 +++--- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs index 08df05e719..86e885239f 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs @@ -34,8 +34,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables ApplyResult(r => r.Type = HitResult.Good); } - public DrawableFlyingCentreHit(double time) - : base(new Hit { StartTime = time }) + public DrawableFlyingCentreHit(double time, bool isStrong = false) + : base(new Hit { StartTime = time, IsStrong = isStrong }) { HitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs index 3e7b6dfd31..64be870262 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs @@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables /// A handler action for when the drumroll has been hit, /// regardless of any judgement. /// - public Action OnHit; + public Action OnHit; public DrawableDrumRoll(DrumRoll drumRoll) : base(drumRoll) @@ -97,7 +97,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables public override bool OnPressed(TaikoAction action) { if (judgingStarted) - OnHit.Invoke(action); + OnHit.Invoke(action, HitObject.IsStrong); return false; } @@ -111,7 +111,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables if (result.Type > HitResult.Miss) { - OnHit.Invoke(drumRollTick.JudgedAction); + OnHit.Invoke(drumRollTick.JudgedAction, HitObject.IsStrong); judgingStarted = true; rollingHits++; } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs index 0c2c9fbdef..ad9872b21f 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs @@ -34,8 +34,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables ApplyResult(r => r.Type = HitResult.Good); } - public DrawableFlyingRimHit(double time) - : base(new Hit { StartTime = time }) + public DrawableFlyingRimHit(double time, bool isStrong = false) + : base(new Hit { StartTime = time, IsStrong = isStrong }) { HitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); } diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index b32e7b53da..59cf9193b5 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -231,14 +231,14 @@ namespace osu.Game.Rulesets.Taiko.UI } } - private void onDrumrollArbitraryHit(TaikoAction action) + private void onDrumrollArbitraryHit(TaikoAction action, bool isStrong) { DrawableHit drawableHit; if (action == TaikoAction.LeftRim || action == TaikoAction.RightRim) - drawableHit = new DrawableFlyingRimHit(Time.Current); + drawableHit = new DrawableFlyingRimHit(Time.Current, isStrong); else - drawableHit = new DrawableFlyingCentreHit(Time.Current); + drawableHit = new DrawableFlyingCentreHit(Time.Current, isStrong); drumRollHitContainer.Add(drawableHit); } From 9d0d2ef68aaff860b62c2c3de247f2a34ecb2961 Mon Sep 17 00:00:00 2001 From: Tim Oliver Date: Wed, 8 Apr 2020 12:12:59 +0800 Subject: [PATCH 0165/1117] Added content proxying to drull roll elements --- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 59cf9193b5..e947795fe5 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -241,6 +241,7 @@ namespace osu.Game.Rulesets.Taiko.UI drawableHit = new DrawableFlyingCentreHit(Time.Current, isStrong); drumRollHitContainer.Add(drawableHit); + topLevelHitContainer.Add(drawableHit.CreateProxiedContent()); } internal void OnNewResult(DrawableHitObject judgedObject, JudgementResult result) From 78492dbba8a23fba4e07606afce6b3f1ae336227 Mon Sep 17 00:00:00 2001 From: Tim Oliver Date: Sat, 11 Apr 2020 17:20:37 +0800 Subject: [PATCH 0166/1117] Added ignore hit object --- osu.Game.Rulesets.Taiko/Objects/IgnoreHit.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 osu.Game.Rulesets.Taiko/Objects/IgnoreHit.cs diff --git a/osu.Game.Rulesets.Taiko/Objects/IgnoreHit.cs b/osu.Game.Rulesets.Taiko/Objects/IgnoreHit.cs new file mode 100644 index 0000000000..302f940ef4 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Objects/IgnoreHit.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.Game.Rulesets.Judgements; + +namespace osu.Game.Rulesets.Taiko.Objects +{ + public class IgnoreHit : Hit + { + public override Judgement CreateJudgement() => new IgnoreJudgement(); + } +} From 940d85cfa6f22df86ce1a55a752959579998028b Mon Sep 17 00:00:00 2001 From: Tim Oliver Date: Sat, 11 Apr 2020 17:20:52 +0800 Subject: [PATCH 0167/1117] Moved flying objects to use ignore hit judgements --- osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs | 3 ++- osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs index 86e885239f..4468c1e3fb 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; +using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; @@ -35,7 +36,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables } public DrawableFlyingCentreHit(double time, bool isStrong = false) - : base(new Hit { StartTime = time, IsStrong = isStrong }) + : base(new IgnoreHit { StartTime = time, IsStrong = isStrong }) { HitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs index ad9872b21f..e5cfc0562b 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs @@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables } public DrawableFlyingRimHit(double time, bool isStrong = false) - : base(new Hit { StartTime = time, IsStrong = isStrong }) + : base(new IgnoreHit { StartTime = time, IsStrong = isStrong }) { HitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); } From 442992dc5bd9bb1e4fcdbeee0ab39a170af263f5 Mon Sep 17 00:00:00 2001 From: Tim Oliver Date: Sun, 12 Apr 2020 10:38:22 +0800 Subject: [PATCH 0168/1117] Removed un-needed using --- osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs index 4468c1e3fb..b6f6f04821 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs @@ -5,7 +5,6 @@ using osu.Framework.Allocation; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; -using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; From c3f0475748f910172c5161db4dbd62afc3eb0c28 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 12 Apr 2020 17:40:22 +0900 Subject: [PATCH 0169/1117] Make CirclePiece abstract --- .../Objects/Drawables/Pieces/CirclePiece.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs index 70fe4b7bb2..6ca77e666d 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces /// for a usage example. /// /// - public class CirclePiece : BeatSyncedContainer + public abstract class CirclePiece : BeatSyncedContainer { public const float SYMBOL_SIZE = 0.45f; public const float SYMBOL_BORDER = 8; @@ -69,7 +69,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces public Box FlashBox; - public CirclePiece() + protected CirclePiece() { RelativeSizeAxes = Axes.Both; From c5d6c7728a512ab6a85857e391f7841afedb255f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 12 Apr 2020 18:29:25 +0900 Subject: [PATCH 0170/1117] Update resources --- 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 5b200ee104..723844155f 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 7cf1272611..0732e6090d 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -23,7 +23,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index c58a431e80..d7006761be 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -71,7 +71,7 @@ - + From 63a1686dfbe8e984cc8e3e5ad32ce1bfa8931e41 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Sun, 12 Apr 2020 12:42:52 +0300 Subject: [PATCH 0171/1117] Scroll to screen middle --- 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 59dddc2baa..a5379e9649 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -632,7 +632,7 @@ namespace osu.Game.Screens.Select case DrawableCarouselBeatmap beatmap: { if (beatmap.Item.State.Value == CarouselItemState.Selected) - scrollTarget = currentY + beatmap.DrawHeight / 2 - DrawHeight / 2; + scrollTarget = currentY + beatmap.DrawHeight / 2 - (Parent.DrawHeight / 2 - Parent.Padding.Top); void performMove(float y, float? startY = null) { From 07dc2773218e4e65e6c388e7210a49a8a6a8f8ac Mon Sep 17 00:00:00 2001 From: TheWildTree Date: Sun, 12 Apr 2020 14:55:42 +0200 Subject: [PATCH 0172/1117] Remove unused changelog comments class --- .../Online/TestSceneChangelogOverlay.cs | 1 - osu.Game/Overlays/Changelog/Comments.cs | 79 ------------------- 2 files changed, 80 deletions(-) delete mode 100644 osu.Game/Overlays/Changelog/Comments.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs index 864fd31a0f..22d20f7098 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs @@ -24,7 +24,6 @@ namespace osu.Game.Tests.Visual.Online typeof(ChangelogListing), typeof(ChangelogSingleBuild), typeof(ChangelogBuild), - typeof(Comments), }; protected override bool UseOnlineAPI => true; diff --git a/osu.Game/Overlays/Changelog/Comments.cs b/osu.Game/Overlays/Changelog/Comments.cs deleted file mode 100644 index 4cf39e7b44..0000000000 --- a/osu.Game/Overlays/Changelog/Comments.cs +++ /dev/null @@ -1,79 +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.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.Requests.Responses; -using osuTK.Graphics; - -namespace osu.Game.Overlays.Changelog -{ - public class Comments : CompositeDrawable - { - private readonly APIChangelogBuild build; - - public Comments(APIChangelogBuild build) - { - this.build = build; - - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - - Padding = new MarginPadding - { - Horizontal = 50, - Vertical = 20, - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - LinkFlowContainer text; - - InternalChildren = new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Both, - Masking = true, - CornerRadius = 10, - Child = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colours.GreyVioletDarker - }, - }, - text = new LinkFlowContainer(t => - { - t.Colour = colours.PinkLighter; - t.Font = OsuFont.Default.With(size: 14); - }) - { - Padding = new MarginPadding(20), - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - } - }; - - text.AddParagraph("Got feedback?", t => - { - t.Colour = Color4.White; - t.Font = OsuFont.Default.With(italics: true, size: 20); - t.Padding = new MarginPadding { Bottom = 20 }; - }); - - text.AddParagraph("We would love to hear what you think of this update! "); - text.AddIcon(FontAwesome.Regular.GrinHearts); - - text.AddParagraph("Please visit the "); - text.AddLink("web version", $"{build.Url}#comments"); - text.AddText(" of this changelog to leave any comments."); - } - } -} From ecd25e567d8803c548e273b11aac15f333e55da6 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Sun, 12 Apr 2020 16:00:05 +0300 Subject: [PATCH 0173/1117] Present selected difficulty --- osu.Game/OsuGame.cs | 12 ++++++++---- osu.Game/Overlays/BeatmapSet/Header.cs | 3 ++- osu.Game/Overlays/Direct/PanelDownloadButton.cs | 10 +++++++++- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 1b2fd658f4..113e9bbe24 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -315,8 +315,12 @@ namespace osu.Game /// The user should have already requested this interactively. /// /// The beatmap to select. - public void PresentBeatmap(BeatmapSetInfo beatmap) + /// Predicate used to find a difficulty to select + public void PresentBeatmap(BeatmapSetInfo beatmap, Predicate findPredicate = null) { + // Use this predicate if non was provided. This will try to find some difficulty from current ruleset so we wouldn't have to change rulesets + findPredicate ??= b => b.Ruleset.Equals(Ruleset.Value); + var databasedSet = beatmap.OnlineBeatmapSetID != null ? BeatmapManager.QueryBeatmapSet(s => s.OnlineBeatmapSetID == beatmap.OnlineBeatmapSetID) : BeatmapManager.QueryBeatmapSet(s => s.Hash == beatmap.Hash); @@ -334,13 +338,13 @@ namespace osu.Game menuScreen.LoadToSolo(); // we might even already be at the song - if (Beatmap.Value.BeatmapSetInfo.Hash == databasedSet.Hash) + if (Beatmap.Value.BeatmapSetInfo.Hash == databasedSet.Hash && findPredicate(Beatmap.Value.BeatmapInfo)) { return; } - // Use first beatmap available for current ruleset, else switch ruleset. - var first = databasedSet.Beatmaps.Find(b => b.Ruleset.Equals(Ruleset.Value)) ?? databasedSet.Beatmaps.First(); + // Find first beatmap that matches our predicate. + var first = databasedSet.Beatmaps.Find(findPredicate) ?? databasedSet.Beatmaps.First(); Ruleset.Value = first.Ruleset; Beatmap.Value = BeatmapManager.GetWorkingBeatmap(first); diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs index 29c259b7f8..a60b9e04b1 100644 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ b/osu.Game/Overlays/BeatmapSet/Header.cs @@ -277,7 +277,8 @@ namespace osu.Game.Overlays.BeatmapSet downloadButtonsContainer.Child = new PanelDownloadButton(BeatmapSet.Value) { Width = 50, - RelativeSizeAxes = Axes.Y + RelativeSizeAxes = Axes.Y, + CurrentBeatmap = Picker.Beatmap.GetBoundCopy() }; break; diff --git a/osu.Game/Overlays/Direct/PanelDownloadButton.cs b/osu.Game/Overlays/Direct/PanelDownloadButton.cs index 1b3657f010..eae2f3353c 100644 --- a/osu.Game/Overlays/Direct/PanelDownloadButton.cs +++ b/osu.Game/Overlays/Direct/PanelDownloadButton.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 osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Graphics.Containers; @@ -16,6 +18,8 @@ namespace osu.Game.Overlays.Direct private readonly bool noVideo; + public Bindable CurrentBeatmap = new Bindable(); + private readonly ShakeContainer shakeContainer; private readonly DownloadButton button; @@ -62,7 +66,11 @@ namespace osu.Game.Overlays.Direct break; case DownloadState.LocallyAvailable: - game?.PresentBeatmap(BeatmapSet.Value); + Predicate findPredicate = null; + if (CurrentBeatmap.Value != null) + findPredicate = b => b.OnlineBeatmapID == CurrentBeatmap.Value.OnlineBeatmapID; + + game?.PresentBeatmap(BeatmapSet.Value, findPredicate); break; default: From ed28e8c8f5f6d92f7f06034800fd9df82f688ef7 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Sun, 12 Apr 2020 19:38:09 +0300 Subject: [PATCH 0174/1117] Rename param --- osu.Game/OsuGame.cs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 113e9bbe24..5e93d760e3 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -315,11 +315,14 @@ namespace osu.Game /// The user should have already requested this interactively. /// /// The beatmap to select. - /// Predicate used to find a difficulty to select - public void PresentBeatmap(BeatmapSetInfo beatmap, Predicate findPredicate = null) + /// + /// Optional predicate used to try and find a difficulty to select. + /// If omitted, this will try to present the first beatmap from the current ruleset. + /// In case of failure the first difficulty of the set will be presented, ignoring the predicate. + /// + public void PresentBeatmap(BeatmapSetInfo beatmap, Predicate difficultyCriteria = null) { - // Use this predicate if non was provided. This will try to find some difficulty from current ruleset so we wouldn't have to change rulesets - findPredicate ??= b => b.Ruleset.Equals(Ruleset.Value); + difficultyCriteria ??= b => b.Ruleset.Equals(Ruleset.Value); var databasedSet = beatmap.OnlineBeatmapSetID != null ? BeatmapManager.QueryBeatmapSet(s => s.OnlineBeatmapSetID == beatmap.OnlineBeatmapSetID) @@ -338,13 +341,13 @@ namespace osu.Game menuScreen.LoadToSolo(); // we might even already be at the song - if (Beatmap.Value.BeatmapSetInfo.Hash == databasedSet.Hash && findPredicate(Beatmap.Value.BeatmapInfo)) + if (Beatmap.Value.BeatmapSetInfo.Hash == databasedSet.Hash && difficultyCriteria(Beatmap.Value.BeatmapInfo)) { return; } // Find first beatmap that matches our predicate. - var first = databasedSet.Beatmaps.Find(findPredicate) ?? databasedSet.Beatmaps.First(); + var first = databasedSet.Beatmaps.Find(difficultyCriteria) ?? databasedSet.Beatmaps.First(); Ruleset.Value = first.Ruleset; Beatmap.Value = BeatmapManager.GetWorkingBeatmap(first); From 3b9e0fa67def20a8e9177088147cbfff4536cc07 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Sun, 12 Apr 2020 19:42:28 +0300 Subject: [PATCH 0175/1117] Use readonly IBindable --- osu.Game/Overlays/BeatmapSet/Header.cs | 7 ++++--- osu.Game/Overlays/Direct/PanelDownloadButton.cs | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs index a60b9e04b1..4e57bfa688 100644 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ b/osu.Game/Overlays/BeatmapSet/Header.cs @@ -274,12 +274,13 @@ namespace osu.Game.Overlays.BeatmapSet { case DownloadState.LocallyAvailable: // temporary for UX until new design is implemented. - downloadButtonsContainer.Child = new PanelDownloadButton(BeatmapSet.Value) + PanelDownloadButton panelButton; + downloadButtonsContainer.Child = panelButton = new PanelDownloadButton(BeatmapSet.Value) { Width = 50, - RelativeSizeAxes = Axes.Y, - CurrentBeatmap = Picker.Beatmap.GetBoundCopy() + RelativeSizeAxes = Axes.Y }; + panelButton.CurrentBeatmap.BindTo(Picker.Beatmap); break; case DownloadState.Downloading: diff --git a/osu.Game/Overlays/Direct/PanelDownloadButton.cs b/osu.Game/Overlays/Direct/PanelDownloadButton.cs index eae2f3353c..6fe174438b 100644 --- a/osu.Game/Overlays/Direct/PanelDownloadButton.cs +++ b/osu.Game/Overlays/Direct/PanelDownloadButton.cs @@ -18,7 +18,7 @@ namespace osu.Game.Overlays.Direct private readonly bool noVideo; - public Bindable CurrentBeatmap = new Bindable(); + public readonly IBindable CurrentBeatmap = new Bindable(); private readonly ShakeContainer shakeContainer; private readonly DownloadButton button; From 1cf240b5fff1c269f9e668970a4046d963521b41 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Sun, 12 Apr 2020 20:04:25 +0300 Subject: [PATCH 0176/1117] Test new predicate behaviour --- .../Navigation/TestScenePresentBeatmap.cs | 57 +++++++++++++++---- 1 file changed, 45 insertions(+), 12 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePresentBeatmap.cs b/osu.Game.Tests/Visual/Navigation/TestScenePresentBeatmap.cs index 909409835c..27f5b29738 100644 --- a/osu.Game.Tests/Visual/Navigation/TestScenePresentBeatmap.cs +++ b/osu.Game.Tests/Visual/Navigation/TestScenePresentBeatmap.cs @@ -20,26 +20,30 @@ namespace osu.Game.Tests.Visual.Navigation public void TestFromMainMenu() { var firstImport = importBeatmap(1); + var secondimport = importBeatmap(3); + presentAndConfirm(firstImport); - - AddStep("return to menu", () => Game.ScreenStack.CurrentScreen.Exit()); - AddUntilStep("wait for menu", () => Game.ScreenStack.CurrentScreen is MainMenu); - - var secondimport = importBeatmap(2); + returnToMenu(); presentAndConfirm(secondimport); + returnToMenu(); + presentSecondDifficultyAndConfirm(firstImport, 1); + returnToMenu(); + presentSecondDifficultyAndConfirm(secondimport, 3); } [Test] public void TestFromMainMenuDifferentRuleset() { var firstImport = importBeatmap(1); + var secondimport = importBeatmap(3, new ManiaRuleset().RulesetInfo); + presentAndConfirm(firstImport); - - AddStep("return to menu", () => Game.ScreenStack.CurrentScreen.Exit()); - AddUntilStep("wait for menu", () => Game.ScreenStack.CurrentScreen is MainMenu); - - var secondimport = importBeatmap(2, new ManiaRuleset().RulesetInfo); + returnToMenu(); presentAndConfirm(secondimport); + returnToMenu(); + presentSecondDifficultyAndConfirm(firstImport, 1); + returnToMenu(); + presentSecondDifficultyAndConfirm(secondimport, 3); } [Test] @@ -48,8 +52,11 @@ namespace osu.Game.Tests.Visual.Navigation var firstImport = importBeatmap(1); presentAndConfirm(firstImport); - var secondimport = importBeatmap(2); + var secondimport = importBeatmap(3); presentAndConfirm(secondimport); + + presentSecondDifficultyAndConfirm(firstImport, 1); + presentSecondDifficultyAndConfirm(secondimport, 3); } [Test] @@ -58,8 +65,17 @@ namespace osu.Game.Tests.Visual.Navigation var firstImport = importBeatmap(1); presentAndConfirm(firstImport); - var secondimport = importBeatmap(2, new ManiaRuleset().RulesetInfo); + var secondimport = importBeatmap(3, new ManiaRuleset().RulesetInfo); presentAndConfirm(secondimport); + + presentSecondDifficultyAndConfirm(firstImport, 1); + presentSecondDifficultyAndConfirm(secondimport, 3); + } + + private void returnToMenu() + { + AddStep("return to menu", () => Game.ScreenStack.CurrentScreen.Exit()); + AddUntilStep("wait for menu", () => Game.ScreenStack.CurrentScreen is MainMenu); } private Func importBeatmap(int i, RulesetInfo ruleset = null) @@ -89,6 +105,13 @@ namespace osu.Game.Tests.Visual.Navigation BaseDifficulty = difficulty, Ruleset = ruleset ?? new OsuRuleset().RulesetInfo }, + new BeatmapInfo + { + OnlineBeatmapID = i * 2048, + Metadata = metadata, + BaseDifficulty = difficulty, + Ruleset = ruleset ?? new OsuRuleset().RulesetInfo + }, } }).Result; }); @@ -106,5 +129,15 @@ namespace osu.Game.Tests.Visual.Navigation AddUntilStep("correct beatmap displayed", () => Game.Beatmap.Value.BeatmapSetInfo.ID == getImport().ID); AddAssert("correct ruleset selected", () => Game.Ruleset.Value.ID == getImport().Beatmaps.First().Ruleset.ID); } + + private void presentSecondDifficultyAndConfirm(Func getImport, int importedID) + { + Predicate pred = b => b.OnlineBeatmapID == importedID * 2048; + AddStep("present difficulty", () => Game.PresentBeatmap(getImport(), pred)); + + AddUntilStep("wait for song select", () => Game.ScreenStack.CurrentScreen is Screens.Select.SongSelect); + AddUntilStep("correct beatmap displayed", () => Game.Beatmap.Value.BeatmapInfo.OnlineBeatmapID == importedID * 2048); + AddAssert("correct ruleset selected", () => Game.Ruleset.Value.ID == getImport().Beatmaps.First().Ruleset.ID); + } } } From b475316a4e0a34161450ab8eed126d4087866cab Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Sun, 12 Apr 2020 20:40:08 +0300 Subject: [PATCH 0177/1117] Simplify and comment --- osu.Game/Screens/Select/BeatmapCarousel.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index a5379e9649..e13511a02c 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -632,7 +632,11 @@ namespace osu.Game.Screens.Select case DrawableCarouselBeatmap beatmap: { if (beatmap.Item.State.Value == CarouselItemState.Selected) - scrollTarget = currentY + beatmap.DrawHeight / 2 - (Parent.DrawHeight / 2 - Parent.Padding.Top); + // scroll position at currentY makes the set panel appear at the very top of the carousel in screen space + // move down by half of parent height (which is the height of the carousel's visible extent, including semi-transparent areas) + // then reapply parent's padding from the top by adding it + // and finally add half of the panel's own height to achieve vertical centering of the panel itself + scrollTarget = currentY - Parent.DrawHeight / 2 + Parent.Padding.Top + beatmap.DrawHeight / 2; void performMove(float y, float? startY = null) { From 3efb4aba25803c36a2f50401808a389f3b082f29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 12 Apr 2020 19:48:15 +0200 Subject: [PATCH 0178/1117] Use BindTarget --- osu.Game/Overlays/BeatmapSet/Header.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs index 4e57bfa688..a03613f955 100644 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ b/osu.Game/Overlays/BeatmapSet/Header.cs @@ -274,13 +274,12 @@ namespace osu.Game.Overlays.BeatmapSet { case DownloadState.LocallyAvailable: // temporary for UX until new design is implemented. - PanelDownloadButton panelButton; - downloadButtonsContainer.Child = panelButton = new PanelDownloadButton(BeatmapSet.Value) + downloadButtonsContainer.Child = new PanelDownloadButton(BeatmapSet.Value) { Width = 50, - RelativeSizeAxes = Axes.Y + RelativeSizeAxes = Axes.Y, + CurrentBeatmap = { BindTarget = Picker.Beatmap } }; - panelButton.CurrentBeatmap.BindTo(Picker.Beatmap); break; case DownloadState.Downloading: From 633b969017515da564a14036e7ad3eb0cbb5a141 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Sun, 12 Apr 2020 21:57:35 +0300 Subject: [PATCH 0179/1117] Apply review suggestions --- .../Visual/Online/TestSceneDirectDownloadButton.cs | 4 ++-- osu.Game/Overlays/Direct/PanelDownloadButton.cs | 13 ++++++------- .../Settings/Sections/Online/WebSettings.cs | 3 ++- osu.Game/Screens/Multi/DrawableRoomPlaylistItem.cs | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs b/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs index 5b0c2d3c67..f612992bf6 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs @@ -149,8 +149,8 @@ namespace osu.Game.Tests.Visual.Online public DownloadState DownloadState => State.Value; - public TestDownloadButton(BeatmapSetInfo beatmapSet, bool noVideo = false) - : base(beatmapSet, noVideo) + public TestDownloadButton(BeatmapSetInfo beatmapSet) + : base(beatmapSet) { } } diff --git a/osu.Game/Overlays/Direct/PanelDownloadButton.cs b/osu.Game/Overlays/Direct/PanelDownloadButton.cs index f09586c571..51f5b2ae4f 100644 --- a/osu.Game/Overlays/Direct/PanelDownloadButton.cs +++ b/osu.Game/Overlays/Direct/PanelDownloadButton.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.Game.Beatmaps; using osu.Game.Configuration; @@ -15,16 +16,13 @@ namespace osu.Game.Overlays.Direct { protected bool DownloadEnabled => button.Enabled.Value; - private readonly bool? noVideo; - private readonly ShakeContainer shakeContainer; private readonly DownloadButton button; + private readonly BindableBool noVideoSetting = new BindableBool(); - public PanelDownloadButton(BeatmapSetInfo beatmapSet, bool? noVideo = null) + public PanelDownloadButton(BeatmapSetInfo beatmapSet) : base(beatmapSet) { - this.noVideo = noVideo; - InternalChild = shakeContainer = new ShakeContainer { RelativeSizeAxes = Axes.Both, @@ -53,6 +51,8 @@ namespace osu.Game.Overlays.Direct return; } + noVideoSetting.BindTo(osuConfig.GetBindable(OsuSetting.PreferNoVideo)); + button.Action = () => { switch (State.Value) @@ -67,8 +67,7 @@ namespace osu.Game.Overlays.Direct break; default: - var minimiseDownloadSize = noVideo ?? osuConfig.GetBindable(OsuSetting.PreferNoVideo).Value; - beatmaps.Download(BeatmapSet.Value, minimiseDownloadSize); + beatmaps.Download(BeatmapSet.Value, noVideoSetting.Value); break; } }; diff --git a/osu.Game/Overlays/Settings/Sections/Online/WebSettings.cs b/osu.Game/Overlays/Settings/Sections/Online/WebSettings.cs index da3176aca8..23513eade8 100644 --- a/osu.Game/Overlays/Settings/Sections/Online/WebSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Online/WebSettings.cs @@ -23,7 +23,8 @@ namespace osu.Game.Overlays.Settings.Sections.Online }, new SettingsCheckbox { - LabelText = "Prefer no-video downloads", + LabelText = "Prefer downloads without video", + Keywords = new[] { "no-video" }, Bindable = config.GetBindable(OsuSetting.PreferNoVideo) }, }; diff --git a/osu.Game/Screens/Multi/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/Multi/DrawableRoomPlaylistItem.cs index d58218b6b5..d7dcca9809 100644 --- a/osu.Game/Screens/Multi/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/Multi/DrawableRoomPlaylistItem.cs @@ -212,8 +212,8 @@ namespace osu.Game.Screens.Multi private class PlaylistDownloadButton : PanelDownloadButton { - public PlaylistDownloadButton(BeatmapSetInfo beatmapSet, bool? noVideo = null) - : base(beatmapSet, noVideo) + public PlaylistDownloadButton(BeatmapSetInfo beatmapSet) + : base(beatmapSet) { Alpha = 0; } From 65b96079a05f46de50dd1f0f191bed8389cdf62f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 13 Apr 2020 13:00:03 +0900 Subject: [PATCH 0180/1117] Move dampening to base implementation and change range to 0..1 --- .../Objects/Drawables/DrawableCatchHitObject.cs | 2 +- .../Objects/Drawables/DrawableManiaHitObject.cs | 2 +- .../Objects/Drawables/DrawableOsuHitObject.cs | 3 ++- .../Objects/Drawables/DrawableHitObject.cs | 16 +++++++++++----- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs index e726d6eff5..b12cdd4ccb 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs @@ -70,7 +70,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables public float DisplayRadius => DrawSize.X / 2 * Scale.X * HitObject.Scale; - protected override float SamplePlaybackBalance => 0.8f * HitObject.X - 0.4f; + protected override float SamplePlaybackPosition => HitObject.X; protected DrawableCatchHitObject(CatchHitObject hitObject) : base(hitObject) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs index 76e9695855..ce56fd222c 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables protected readonly IBindable Direction = new Bindable(); - protected override float SamplePlaybackBalance + protected override float SamplePlaybackPosition { get { diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index 068d666c8e..8308c0c576 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Graphics.Containers; +using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Objects.Drawables @@ -18,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables // Must be set to update IsHovered as it's used in relax mdo to detect osu hit objects. public override bool HandlePositionalInput => true; - protected override float SamplePlaybackBalance => (HitObject.X / 512f - 0.5f) * 0.8f; + protected override float SamplePlaybackPosition => HitObject.X / OsuPlayfield.BASE_SIZE.X; /// /// Whether this can be hit. diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index d6e231424b..b14927bcd5 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -87,11 +87,15 @@ namespace osu.Game.Rulesets.Objects.Drawables public JudgementResult Result { get; private set; } /// - /// The stereo balance of the samples played if Positional hitsounds is set. + /// The relative X position of this hit object for sample playback balance adjustment. /// - protected virtual float SamplePlaybackBalance => 0; + /// + /// This is a range of 0..1 (0 for far-left, 0.5 for centre, 1 for far-right). + /// Dampening is post-applied to ensure the effect is not too intense. + /// + protected virtual float SamplePlaybackPosition => 0.5f; - private readonly BindableDouble samplePlaybackBalanceAdjustment = new BindableDouble(); + private readonly BindableDouble balanceAdjust = new BindableDouble(); private BindableList samplesBindable; private Bindable startTimeBindable; @@ -168,7 +172,7 @@ namespace osu.Game.Rulesets.Objects.Drawables } Samples = new SkinnableSound(samples.Select(s => HitObject.SampleControlPoint.ApplyTo(s))); - Samples.AddAdjustment(AdjustableProperty.Balance, samplePlaybackBalanceAdjustment); + Samples.AddAdjustment(AdjustableProperty.Balance, balanceAdjust); AddInternal(Samples); } @@ -368,7 +372,9 @@ namespace osu.Game.Rulesets.Objects.Drawables /// public virtual void PlaySamples() { - samplePlaybackBalanceAdjustment.Value = userPositionalHitSounds.Value ? SamplePlaybackBalance : 0; + const float balance_adjust_amount = 0.4f; + + balanceAdjust.Value = balance_adjust_amount * (userPositionalHitSounds.Value ? SamplePlaybackPosition - 0.5f : 0); Samples?.Play(); } From cdff6060d3eccdf3ed36df6ce0d3aa26dc411a17 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 13 Apr 2020 13:01:27 +0900 Subject: [PATCH 0181/1117] Remove recursive hierarchy traversal for mania sample balance --- .../Objects/Drawables/DrawableManiaHitObject.cs | 16 +++++++++------- osu.Game/Rulesets/UI/Playfield.cs | 1 + 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs index ce56fd222c..a708adb493 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs @@ -5,10 +5,10 @@ using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Rulesets.Mania.UI; +using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Mania.Objects.Drawables { @@ -26,17 +26,19 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables protected readonly IBindable Direction = new Bindable(); + [Resolved(canBeNull: true)] + private Playfield playfield { get; set; } + protected override float SamplePlaybackPosition { get { - CompositeDrawable stage = this; - while (!(stage is Stage)) - stage = stage.Parent; + var columns = (playfield as ManiaPlayfield)?.TotalColumns; - var columnCount = ((Stage)stage).Columns.Count; - var columnIndex = HitObject.Column; - return 0.8f * columnIndex / (columnCount - 1) - 0.4f; + if (columns == null) + return base.SamplePlaybackPosition; + + return (float)HitObject.Column / columns.Value; } } diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs index c52183f3f2..fc6906560b 100644 --- a/osu.Game/Rulesets/UI/Playfield.cs +++ b/osu.Game/Rulesets/UI/Playfield.cs @@ -15,6 +15,7 @@ using osuTK; namespace osu.Game.Rulesets.UI { + [Cached] public abstract class Playfield : CompositeDrawable { /// From c51bad0e35e4a08d3e0e159e103884bbf709e85d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 13 Apr 2020 13:42:21 +0900 Subject: [PATCH 0182/1117] Cache ManiaPlayfield instead --- .../Objects/Drawables/DrawableManiaHitObject.cs | 9 +++------ osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs | 2 ++ osu.Game/Rulesets/UI/Playfield.cs | 1 - 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs index a708adb493..88888001b4 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs @@ -8,7 +8,6 @@ using osu.Framework.Graphics; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Rulesets.Mania.UI; -using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Mania.Objects.Drawables { @@ -27,18 +26,16 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables protected readonly IBindable Direction = new Bindable(); [Resolved(canBeNull: true)] - private Playfield playfield { get; set; } + private ManiaPlayfield playfield { get; set; } protected override float SamplePlaybackPosition { get { - var columns = (playfield as ManiaPlayfield)?.TotalColumns; - - if (columns == null) + if (playfield == null) return base.SamplePlaybackPosition; - return (float)HitObject.Column / columns.Value; + return (float)HitObject.Column / playfield.TotalColumns; } } diff --git a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs index c2eb48b774..2dec468654 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs @@ -6,6 +6,7 @@ using osu.Framework.Graphics.Containers; using System; using System.Collections.Generic; using System.Linq; +using osu.Framework.Allocation; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Objects.Drawables; @@ -14,6 +15,7 @@ using osuTK; namespace osu.Game.Rulesets.Mania.UI { + [Cached] public class ManiaPlayfield : ScrollingPlayfield { private readonly List stages = new List(); diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs index fc6906560b..c52183f3f2 100644 --- a/osu.Game/Rulesets/UI/Playfield.cs +++ b/osu.Game/Rulesets/UI/Playfield.cs @@ -15,7 +15,6 @@ using osuTK; namespace osu.Game.Rulesets.UI { - [Cached] public abstract class Playfield : CompositeDrawable { /// From f38b64d20177c2010b415f8a3bae39fbf29e0303 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 13 Apr 2020 13:57:15 +0900 Subject: [PATCH 0183/1117] Fix placement blueprints handling double clicks --- osu.Game/Rulesets/Edit/PlacementBlueprint.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs index ea77a6091a..fb1eb7adbf 100644 --- a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs +++ b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs @@ -106,6 +106,9 @@ namespace osu.Game.Rulesets.Edit case ScrollEvent _: return false; + case DoubleClickEvent _: + return false; + case MouseButtonEvent _: return true; From e17d5bdbaf3a6ad45b4a60af9ab9a168cf0d0406 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 13 Apr 2020 13:57:40 +0900 Subject: [PATCH 0184/1117] Improve red slider control point placement logic --- .../Sliders/SliderPlacementBlueprint.cs | 46 +++++++++++-------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index a780653796..be43515269 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -1,10 +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.Diagnostics; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Input; using osu.Framework.Input.Events; +using osu.Framework.Logging; using osu.Game.Graphics; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; @@ -23,6 +26,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private SliderBodyPiece bodyPiece; private HitCirclePiece headCirclePiece; private HitCirclePiece tailCirclePiece; + private PathControlPointVisualiser controlPointVisualiser; private InputManager inputManager; @@ -51,7 +55,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders bodyPiece = new SliderBodyPiece(), headCirclePiece = new HitCirclePiece(), tailCirclePiece = new HitCirclePiece(), - new PathControlPointVisualiser(HitObject, false) + controlPointVisualiser = new PathControlPointVisualiser(HitObject, false) }; setState(PlacementState.Initial); @@ -91,17 +95,29 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders break; case PlacementState.Body: - switch (e.Button) - { - case MouseButton.Left: - ensureCursor(); + if (e.Button != MouseButton.Left) + break; - // Detatch the cursor - cursor = null; - break; + // Find the last non-cursor control point and the respective drawable piece + var lastPoint = HitObject.Path.ControlPoints.LastOrDefault(p => p != cursor); + var lastPiece = controlPointVisualiser.Pieces.Single(p => p.ControlPoint == lastPoint); + + if (lastPiece?.IsHovered == true) + { + Debug.Assert(lastPoint != null); + + segmentStart = lastPoint; + segmentStart.Type.Value = PathType.Linear; + + currentSegmentLength = 1; + } + else + { + ensureCursor(); + cursor = null; // Detatch the cursor } - break; + return true; } return true; @@ -114,16 +130,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders base.OnMouseUp(e); } - protected override bool OnDoubleClick(DoubleClickEvent e) - { - // Todo: This should all not occur on double click, but rather if the previous control point is hovered. - segmentStart = HitObject.Path.ControlPoints[^1]; - segmentStart.Type.Value = PathType.Linear; - - currentSegmentLength = 1; - return true; - } - private void beginCurve() { BeginPlacement(commitStart: true); @@ -169,6 +175,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders currentSegmentLength++; updatePathType(); + + Logger.Log("Set cursor"); } } From 99fa1458470fe7fc031e3ae4e422cf2f1ad0d73b Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 13 Apr 2020 08:38:34 +0300 Subject: [PATCH 0185/1117] Add test for potential failing case --- .../UserInterface/TestSceneOverlayScrollContainer.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayScrollContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayScrollContainer.cs index 4205d65100..fd3b6ed3ab 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayScrollContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayScrollContainer.cs @@ -32,7 +32,7 @@ namespace osu.Game.Tests.Visual.UserInterface [SetUp] public void SetUp() => Schedule(() => { - Add(scroll = new OverlayScrollContainer + Child = scroll = new OverlayScrollContainer { RelativeSizeAxes = Axes.Both, Child = new Container @@ -45,7 +45,7 @@ namespace osu.Game.Tests.Visual.UserInterface Colour = Color4.Gray } } - }); + }; invocationCount = 0; @@ -62,6 +62,10 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("scroll to start", () => scroll.ScrollToStart(false)); AddAssert("button is hidden", () => scroll.Button.State == Visibility.Hidden); + + AddStep("scroll to 250", () => scroll.ScrollTo(500)); + AddUntilStep("scrolled back to start", () => Precision.AlmostEquals(scroll.Current, 500, 0.1f)); + AddAssert("button is visible", () => scroll.Button.State == Visibility.Visible); } [Test] From 142cddfb10d46a1392dda590ac5fb1864c82bf7f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 13 Apr 2020 15:13:35 +0900 Subject: [PATCH 0186/1117] Rename CurrentBeatmap to SelectedBeatmap --- osu.Game/Overlays/BeatmapSet/Header.cs | 2 +- osu.Game/Overlays/Direct/PanelDownloadButton.cs | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs index a03613f955..11dc424183 100644 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ b/osu.Game/Overlays/BeatmapSet/Header.cs @@ -278,7 +278,7 @@ namespace osu.Game.Overlays.BeatmapSet { Width = 50, RelativeSizeAxes = Axes.Y, - CurrentBeatmap = { BindTarget = Picker.Beatmap } + SelectedBeatmap = { BindTarget = Picker.Beatmap } }; break; diff --git a/osu.Game/Overlays/Direct/PanelDownloadButton.cs b/osu.Game/Overlays/Direct/PanelDownloadButton.cs index 6fe174438b..08e3ed9b38 100644 --- a/osu.Game/Overlays/Direct/PanelDownloadButton.cs +++ b/osu.Game/Overlays/Direct/PanelDownloadButton.cs @@ -18,7 +18,10 @@ namespace osu.Game.Overlays.Direct private readonly bool noVideo; - public readonly IBindable CurrentBeatmap = new Bindable(); + /// + /// Currently selected beatmap. Used to present the correct difficulty after completing a download. + /// + public readonly IBindable SelectedBeatmap = new Bindable(); private readonly ShakeContainer shakeContainer; private readonly DownloadButton button; @@ -67,8 +70,8 @@ namespace osu.Game.Overlays.Direct case DownloadState.LocallyAvailable: Predicate findPredicate = null; - if (CurrentBeatmap.Value != null) - findPredicate = b => b.OnlineBeatmapID == CurrentBeatmap.Value.OnlineBeatmapID; + if (SelectedBeatmap.Value != null) + findPredicate = b => b.OnlineBeatmapID == SelectedBeatmap.Value.OnlineBeatmapID; game?.PresentBeatmap(BeatmapSet.Value, findPredicate); break; From 2c20328a70cfd3f5f2722b226346feeabad633ec Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 13 Apr 2020 15:31:46 +0900 Subject: [PATCH 0187/1117] Rework control point placement for better progression --- .../Sliders/SliderPlacementBlueprint.cs | 69 +++++++++++++------ 1 file changed, 47 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 be43515269..9af972dbce 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -3,11 +3,11 @@ using System.Diagnostics; using System.Linq; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Input; using osu.Framework.Input.Events; -using osu.Framework.Logging; using osu.Game.Graphics; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; @@ -77,11 +77,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders break; case PlacementState.Body: - ensureCursor(); - - // The given screen-space position may have been externally snapped, but the unsnapped position from the input manager - // is used instead since snapping control points doesn't make much sense - cursor.Position.Value = ToLocalSpace(inputManager.CurrentState.Mouse.Position) - HitObject.Position; + updateCursor(); break; } } @@ -98,12 +94,15 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders if (e.Button != MouseButton.Left) break; - // Find the last non-cursor control point and the respective drawable piece - var lastPoint = HitObject.Path.ControlPoints.LastOrDefault(p => p != cursor); - var lastPiece = controlPointVisualiser.Pieces.Single(p => p.ControlPoint == lastPoint); - - if (lastPiece?.IsHovered == true) + if (canPlaceNewControlPoint(out var lastPoint)) { + // Place a new point by detatching the current cursor. + updateCursor(); + cursor = null; + } + else + { + // Transform the last point into a new segment. Debug.Assert(lastPoint != null); segmentStart = lastPoint; @@ -111,11 +110,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders currentSegmentLength = 1; } - else - { - ensureCursor(); - cursor = null; // Detatch the cursor - } return true; } @@ -167,17 +161,48 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders } } - private void ensureCursor() + private void updateCursor() { - if (cursor == null) + if (canPlaceNewControlPoint(out _)) { - HitObject.Path.ControlPoints.Add(cursor = new PathControlPoint { Position = { Value = Vector2.Zero } }); - currentSegmentLength++; + // The cursor does not overlap a previous control point, so it can be added if not already existing. + if (cursor == null) + { + HitObject.Path.ControlPoints.Add(cursor = new PathControlPoint { Position = { Value = Vector2.Zero } }); - updatePathType(); + // The path type should be adjusted in the progression of updatePathType() (Linear -> PC -> Bezier). + currentSegmentLength++; + updatePathType(); + } - Logger.Log("Set cursor"); + // Update the cursor position. + cursor.Position.Value = ToLocalSpace(inputManager.CurrentState.Mouse.Position) - HitObject.Position; } + else if (cursor != null) + { + // The cursor overlaps a previous control point, so it's removed. + HitObject.Path.ControlPoints.Remove(cursor); + cursor = null; + + // The path type should be adjusted in the reverse progression of updatePathType() (Bezier -> PC -> Linear). + currentSegmentLength--; + updatePathType(); + } + } + + /// + /// Whether a new control point can be placed at the current mouse position. + /// + /// The last-placed control point. May be null, but is not null if false is returned. + /// Whether a new control point can be placed at the current position. + private bool canPlaceNewControlPoint([CanBeNull] out PathControlPoint lastPoint) + { + // We cannot rely on the ordering of drawable pieces, so find the respective drawable piece by searching for the last non-cursor control point. + var last = HitObject.Path.ControlPoints.LastOrDefault(p => p != cursor); + var lastPiece = controlPointVisualiser.Pieces.Single(p => p.ControlPoint == last); + + lastPoint = last; + return lastPiece?.IsHovered != true; } private void updateSlider() From bde0b259c1c03d2022124c6125dd2cc45a292807 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 13 Apr 2020 15:31:54 +0900 Subject: [PATCH 0188/1117] Improve slider placement test scene --- .../TestSceneSliderPlacementBlueprint.cs | 267 ++++++++++++++++++ .../Visual/PlacementBlueprintTestScene.cs | 28 +- 2 files changed, 282 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderPlacementBlueprint.cs index 0522260150..9fc479953e 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderPlacementBlueprint.cs @@ -1,18 +1,285 @@ // 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.Rulesets.Edit; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Tests.Visual; +using osuTK; +using osuTK.Input; namespace osu.Game.Rulesets.Osu.Tests { public class TestSceneSliderPlacementBlueprint : PlacementBlueprintTestScene { + [SetUp] + public void Setup() => Schedule(() => + { + HitObjectContainer.Clear(); + ResetPlacement(); + }); + + [Test] + public void TestBeginPlacementWithoutFinishing() + { + addMovementStep(new Vector2(200)); + addClickStep(MouseButton.Left); + + assertPlaced(false); + } + + [Test] + public void TestPlaceWithoutMovingMouse() + { + addMovementStep(new Vector2(200)); + addClickStep(MouseButton.Left); + addClickStep(MouseButton.Right); + + assertPlaced(true); + assertLength(0); + assertControlPointType(0, PathType.Linear); + } + + [Test] + public void TestPlaceWithMouseMovement() + { + addMovementStep(new Vector2(200)); + addClickStep(MouseButton.Left); + + addMovementStep(new Vector2(400, 200)); + addClickStep(MouseButton.Right); + + assertPlaced(true); + assertLength(200); + assertControlPointCount(2); + assertControlPointType(0, PathType.Linear); + } + + [Test] + public void TestPlaceNormalControlPoint() + { + addMovementStep(new Vector2(200)); + addClickStep(MouseButton.Left); + + addMovementStep(new Vector2(300, 200)); + addClickStep(MouseButton.Left); + + addMovementStep(new Vector2(300)); + addClickStep(MouseButton.Right); + + assertPlaced(true); + assertControlPointCount(3); + assertControlPointPosition(1, new Vector2(100, 0)); + assertControlPointType(0, PathType.PerfectCurve); + } + + [Test] + public void TestPlaceTwoNormalControlPoints() + { + addMovementStep(new Vector2(200)); + addClickStep(MouseButton.Left); + + addMovementStep(new Vector2(300, 200)); + addClickStep(MouseButton.Left); + + addMovementStep(new Vector2(300)); + addClickStep(MouseButton.Left); + + addMovementStep(new Vector2(400, 300)); + addClickStep(MouseButton.Right); + + assertPlaced(true); + assertControlPointCount(4); + assertControlPointPosition(1, new Vector2(100, 0)); + assertControlPointPosition(2, new Vector2(100, 100)); + assertControlPointType(0, PathType.Bezier); + } + + [Test] + public void TestPlaceSegmentControlPoint() + { + addMovementStep(new Vector2(200)); + addClickStep(MouseButton.Left); + + addMovementStep(new Vector2(300, 200)); + addClickStep(MouseButton.Left); + addClickStep(MouseButton.Left); + + addMovementStep(new Vector2(300)); + addClickStep(MouseButton.Right); + + assertPlaced(true); + assertControlPointCount(3); + assertControlPointPosition(1, new Vector2(100, 0)); + assertControlPointType(0, PathType.Linear); + assertControlPointType(1, PathType.Linear); + } + + [Test] + public void TestMoveToPerfectCurveThenPlaceLinear() + { + addMovementStep(new Vector2(200)); + addClickStep(MouseButton.Left); + + addMovementStep(new Vector2(300, 200)); + addClickStep(MouseButton.Left); + + addMovementStep(new Vector2(300)); + addMovementStep(new Vector2(300, 200)); + addClickStep(MouseButton.Right); + + assertPlaced(true); + assertControlPointCount(2); + assertControlPointType(0, PathType.Linear); + assertLength(100); + } + + [Test] + public void TestMoveToBezierThenPlacePerfectCurve() + { + addMovementStep(new Vector2(200)); + addClickStep(MouseButton.Left); + + addMovementStep(new Vector2(300, 200)); + addClickStep(MouseButton.Left); + + addMovementStep(new Vector2(300)); + addClickStep(MouseButton.Left); + + addMovementStep(new Vector2(400, 300)); + addMovementStep(new Vector2(300)); + addClickStep(MouseButton.Right); + + assertPlaced(true); + assertControlPointCount(3); + assertControlPointType(0, PathType.PerfectCurve); + } + + [Test] + public void TestMoveToFourthOrderBezierThenPlaceThirdOrderBezier() + { + addMovementStep(new Vector2(200)); + addClickStep(MouseButton.Left); + + addMovementStep(new Vector2(300, 200)); + addClickStep(MouseButton.Left); + + addMovementStep(new Vector2(300)); + addClickStep(MouseButton.Left); + + addMovementStep(new Vector2(400, 300)); + addClickStep(MouseButton.Left); + + addMovementStep(new Vector2(400)); + addMovementStep(new Vector2(400, 300)); + addClickStep(MouseButton.Right); + + assertPlaced(true); + assertControlPointCount(4); + assertControlPointType(0, PathType.Bezier); + } + + [Test] + public void TestPlaceLinearSegmentThenPlaceLinearSegment() + { + addMovementStep(new Vector2(200)); + addClickStep(MouseButton.Left); + + addMovementStep(new Vector2(300, 200)); + addClickStep(MouseButton.Left); + addClickStep(MouseButton.Left); + + addMovementStep(new Vector2(300, 300)); + addClickStep(MouseButton.Right); + + assertPlaced(true); + assertControlPointCount(3); + assertControlPointPosition(1, new Vector2(100, 0)); + assertControlPointPosition(2, new Vector2(100)); + assertControlPointType(0, PathType.Linear); + assertControlPointType(1, PathType.Linear); + } + + [Test] + public void TestPlaceLinearSegmentThenPlacePerfectCurveSegment() + { + addMovementStep(new Vector2(200)); + addClickStep(MouseButton.Left); + + addMovementStep(new Vector2(300, 200)); + addClickStep(MouseButton.Left); + addClickStep(MouseButton.Left); + + addMovementStep(new Vector2(300, 300)); + addClickStep(MouseButton.Left); + + addMovementStep(new Vector2(400, 300)); + addClickStep(MouseButton.Right); + + assertPlaced(true); + assertControlPointCount(4); + assertControlPointPosition(1, new Vector2(100, 0)); + assertControlPointPosition(2, new Vector2(100)); + assertControlPointType(0, PathType.Linear); + assertControlPointType(1, PathType.PerfectCurve); + } + + [Test] + public void TestPlacePerfectCurveSegmentThenPlacePerfectCurveSegment() + { + addMovementStep(new Vector2(200)); + addClickStep(MouseButton.Left); + + addMovementStep(new Vector2(300, 200)); + addClickStep(MouseButton.Left); + + addMovementStep(new Vector2(300, 300)); + addClickStep(MouseButton.Left); + addClickStep(MouseButton.Left); + + addMovementStep(new Vector2(400, 300)); + addClickStep(MouseButton.Left); + + addMovementStep(new Vector2(400)); + addClickStep(MouseButton.Right); + + assertPlaced(true); + assertControlPointCount(5); + assertControlPointPosition(1, new Vector2(100, 0)); + assertControlPointPosition(2, new Vector2(100)); + assertControlPointPosition(3, new Vector2(200, 100)); + assertControlPointPosition(4, new Vector2(200)); + assertControlPointType(0, PathType.PerfectCurve); + assertControlPointType(2, PathType.PerfectCurve); + } + + private void addMovementStep(Vector2 position) => AddStep($"move mouse to {position}", () => InputManager.MoveMouseTo(InputManager.ToScreenSpace(position))); + + private void addClickStep(MouseButton button) + { + AddStep($"press {button}", () => InputManager.PressButton(button)); + AddStep($"release {button}", () => InputManager.ReleaseButton(button)); + } + + 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 assertControlPointCount(int expected) => AddAssert($"has {expected} control points", () => getSlider().Path.ControlPoints.Count == expected); + + private void assertControlPointType(int index, PathType type) => AddAssert($"control point {index} is {type}", () => getSlider().Path.ControlPoints[index].Type.Value == type); + + private void assertControlPointPosition(int index, Vector2 position) => + AddAssert($"control point {index} at {position}", () => Precision.AlmostEquals(position, getSlider().Path.ControlPoints[index].Position.Value, 1)); + + private Slider getSlider() => HitObjectContainer.Count > 0 ? (Slider)((DrawableSlider)HitObjectContainer[0]).HitObject : null; + protected override DrawableHitObject CreateHitObject(HitObject hitObject) => new DrawableSlider((Slider)hitObject); protected override PlacementBlueprint CreateBlueprint() => new SliderPlacementBlueprint(); } diff --git a/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs b/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs index ce95dfa62f..dc67d28f63 100644 --- a/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs +++ b/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs @@ -4,8 +4,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Input; -using osu.Framework.Input.Events; using osu.Framework.Timing; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; @@ -15,13 +13,11 @@ using osu.Game.Screens.Edit.Compose; namespace osu.Game.Tests.Visual { [Cached(Type = typeof(IPlacementHandler))] - public abstract class PlacementBlueprintTestScene : OsuTestScene, IPlacementHandler + public abstract class PlacementBlueprintTestScene : OsuManualInputManagerTestScene, IPlacementHandler { - protected Container HitObjectContainer; + protected readonly Container HitObjectContainer; private PlacementBlueprint currentBlueprint; - private InputManager inputManager; - protected PlacementBlueprintTestScene() { Add(HitObjectContainer = CreateHitObjectContainer().With(c => c.Clock = new FramedClock(new StopwatchClock()))); @@ -45,8 +41,7 @@ namespace osu.Game.Tests.Visual { base.LoadComplete(); - inputManager = GetContainingInputManager(); - Add(currentBlueprint = CreateBlueprint()); + ResetPlacement(); } public void BeginPlacement(HitObject hitObject) @@ -58,7 +53,13 @@ namespace osu.Game.Tests.Visual if (commit) AddHitObject(CreateHitObject(hitObject)); - Remove(currentBlueprint); + ResetPlacement(); + } + + protected void ResetPlacement() + { + if (currentBlueprint != null) + Remove(currentBlueprint); Add(currentBlueprint = CreateBlueprint()); } @@ -66,10 +67,11 @@ namespace osu.Game.Tests.Visual { } - protected override bool OnMouseMove(MouseMoveEvent e) + protected override void Update() { - currentBlueprint.UpdatePosition(e.ScreenSpaceMousePosition); - return true; + base.Update(); + + currentBlueprint.UpdatePosition(InputManager.CurrentState.Mouse.Position); } public override void Add(Drawable drawable) @@ -79,7 +81,7 @@ namespace osu.Game.Tests.Visual if (drawable is PlacementBlueprint blueprint) { blueprint.Show(); - blueprint.UpdatePosition(inputManager.CurrentState.Mouse.Position); + blueprint.UpdatePosition(InputManager.CurrentState.Mouse.Position); } } From 0eaff00787293a93f945bdf0f5866b5285e2626f Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 13 Apr 2020 09:45:49 +0300 Subject: [PATCH 0189/1117] Fix typo in test --- .../Visual/UserInterface/TestSceneOverlayScrollContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayScrollContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayScrollContainer.cs index fd3b6ed3ab..3ef0adcd9d 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayScrollContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayScrollContainer.cs @@ -63,7 +63,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("scroll to start", () => scroll.ScrollToStart(false)); AddAssert("button is hidden", () => scroll.Button.State == Visibility.Hidden); - AddStep("scroll to 250", () => scroll.ScrollTo(500)); + AddStep("scroll to 500", () => scroll.ScrollTo(500)); AddUntilStep("scrolled back to start", () => Precision.AlmostEquals(scroll.Current, 500, 0.1f)); AddAssert("button is visible", () => scroll.Button.State == Visibility.Visible); } From bdce79ed5b85b01de2f49e24e22aa8156a518f3c Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 13 Apr 2020 09:57:05 +0300 Subject: [PATCH 0190/1117] Fix incorrect test step name --- .../Visual/UserInterface/TestSceneOverlayScrollContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayScrollContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayScrollContainer.cs index 3ef0adcd9d..6a09fecc0a 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayScrollContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayScrollContainer.cs @@ -64,7 +64,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("button is hidden", () => scroll.Button.State == Visibility.Hidden); AddStep("scroll to 500", () => scroll.ScrollTo(500)); - AddUntilStep("scrolled back to start", () => Precision.AlmostEquals(scroll.Current, 500, 0.1f)); + AddUntilStep("scrolled to 500", () => Precision.AlmostEquals(scroll.Current, 500, 0.1f)); AddAssert("button is visible", () => scroll.Button.State == Visibility.Visible); } From 9a65aa18d78407bbba9658e0fc98810fc2940c69 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 13 Apr 2020 16:13:14 +0900 Subject: [PATCH 0191/1117] Fix connections hidden due to overlapping controlpoints --- .../TestScenePathControlPointVisualiser.cs | 64 +++++++++++++++++++ .../PathControlPointConnectionPiece.cs | 16 +++-- .../Components/PathControlPointVisualiser.cs | 57 +++++++++-------- 3 files changed, 103 insertions(+), 34 deletions(-) create mode 100644 osu.Game.Rulesets.Osu.Tests/TestScenePathControlPointVisualiser.cs diff --git a/osu.Game.Rulesets.Osu.Tests/TestScenePathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu.Tests/TestScenePathControlPointVisualiser.cs new file mode 100644 index 0000000000..cbe14ff4d2 --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/TestScenePathControlPointVisualiser.cs @@ -0,0 +1,64 @@ +// 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 Humanizer; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Tests.Visual; +using osuTK; + +namespace osu.Game.Rulesets.Osu.Tests +{ + public class TestScenePathControlPointVisualiser : OsuTestScene + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(StringHumanizeExtensions), + typeof(PathControlPointPiece), + typeof(PathControlPointConnectionPiece) + }; + + private Slider slider; + private PathControlPointVisualiser visualiser; + + [SetUp] + public void Setup() => Schedule(() => + { + slider = new Slider(); + slider.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); + }); + + [Test] + public void TestAddOverlappingControlPoints() + { + createVisualiser(true); + + addControlPointStep(new Vector2(200)); + addControlPointStep(new Vector2(300)); + addControlPointStep(new Vector2(300)); + addControlPointStep(new Vector2(500, 300)); + + AddAssert("last connection displayed", () => + { + var lastConnection = visualiser.Connections.Last(c => c.ControlPoint.Position.Value == new Vector2(300)); + return lastConnection.DrawWidth > 50; + }); + } + + private void createVisualiser(bool allowSelection) => AddStep("create visualiser", () => Child = visualiser = new PathControlPointVisualiser(slider, allowSelection) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre + }); + + private void addControlPointStep(Vector2 position) => AddStep($"add control point {position}", () => slider.Path.ControlPoints.Add(new PathControlPoint(position))); + } +} 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 0fc441fec6..9c620ecb2f 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs @@ -16,22 +16,25 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components /// public class PathControlPointConnectionPiece : CompositeDrawable { - public PathControlPoint ControlPoint; + public readonly PathControlPoint ControlPoint; private readonly Path path; private readonly Slider slider; + private readonly int controlPointIndex; private IBindable sliderPosition; private IBindable pathVersion; - public PathControlPointConnectionPiece(Slider slider, PathControlPoint controlPoint) + public PathControlPointConnectionPiece(Slider slider, int controlPointIndex) { this.slider = slider; - ControlPoint = controlPoint; + this.controlPointIndex = controlPointIndex; Origin = Anchor.Centre; AutoSizeAxes = Axes.Both; + ControlPoint = slider.Path.ControlPoints[controlPointIndex]; + InternalChild = path = new SmoothPath { Anchor = Anchor.Centre, @@ -61,13 +64,12 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components path.ClearVertices(); - int index = slider.Path.ControlPoints.IndexOf(ControlPoint) + 1; - - if (index == 0 || index == slider.Path.ControlPoints.Count) + int nextIndex = controlPointIndex + 1; + if (nextIndex == 0 || nextIndex == slider.Path.ControlPoints.Count) return; path.AddVertex(Vector2.Zero); - path.AddVertex(slider.Path.ControlPoints[index].Position.Value - ControlPoint.Position.Value); + path.AddVertex(slider.Path.ControlPoints[nextIndex].Position.Value - ControlPoint.Position.Value); path.OriginPosition = path.PositionInBoundingBox(Vector2.Zero); } 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 e293eba9d7..f6354bc612 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Collections.Specialized; using System.Linq; using Humanizer; using osu.Framework.Bindables; @@ -24,17 +25,14 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components public class PathControlPointVisualiser : CompositeDrawable, IKeyBindingHandler, IHasContextMenu { internal readonly Container Pieces; + internal readonly Container Connections; - private readonly Container connections; - + private readonly IBindableList controlPoints = new BindableList(); private readonly Slider slider; - private readonly bool allowSelection; private InputManager inputManager; - private IBindableList controlPoints; - public Action> RemoveControlPointsRequested; public PathControlPointVisualiser(Slider slider, bool allowSelection) @@ -46,7 +44,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components InternalChildren = new Drawable[] { - connections = new Container { RelativeSizeAxes = Axes.Both }, + Connections = new Container { RelativeSizeAxes = Axes.Both }, Pieces = new Container { RelativeSizeAxes = Axes.Both } }; } @@ -57,33 +55,38 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components inputManager = GetContainingInputManager(); - controlPoints = slider.Path.ControlPoints.GetBoundCopy(); - controlPoints.ItemsAdded += addControlPoints; - controlPoints.ItemsRemoved += removeControlPoints; - - addControlPoints(controlPoints); + controlPoints.CollectionChanged += onControlPointsChanged; + controlPoints.BindTo(slider.Path.ControlPoints); } - private void addControlPoints(IEnumerable controlPoints) + private void onControlPointsChanged(object sender, NotifyCollectionChangedEventArgs e) { - foreach (var point in controlPoints) + switch (e.Action) { - Pieces.Add(new PathControlPointPiece(slider, point).With(d => - { - if (allowSelection) - d.RequestSelection = selectPiece; - })); + case NotifyCollectionChangedAction.Add: + for (int i = 0; i < e.NewItems.Count; i++) + { + var point = (PathControlPoint)e.NewItems[i]; - connections.Add(new PathControlPointConnectionPiece(slider, point)); - } - } + Pieces.Add(new PathControlPointPiece(slider, point).With(d => + { + if (allowSelection) + d.RequestSelection = selectPiece; + })); - private void removeControlPoints(IEnumerable controlPoints) - { - foreach (var point in controlPoints) - { - Pieces.RemoveAll(p => p.ControlPoint == point); - connections.RemoveAll(c => c.ControlPoint == point); + Connections.Add(new PathControlPointConnectionPiece(slider, e.NewStartingIndex + i)); + } + + break; + + case NotifyCollectionChangedAction.Remove: + foreach (var point in e.OldItems.Cast()) + { + Pieces.RemoveAll(p => p.ControlPoint == point); + Connections.RemoveAll(c => c.ControlPoint == point); + } + + break; } } From 29dd2252054ed45fbac8be5f43318bdae45552f5 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 13 Apr 2020 10:45:15 +0300 Subject: [PATCH 0192/1117] Make button protected --- .../UserInterface/TestSceneOverlayScrollContainer.cs | 9 +++++++-- osu.Game/Overlays/OverlayScrollContainer.cs | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayScrollContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayScrollContainer.cs index 6a09fecc0a..e9e63613c0 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayScrollContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayScrollContainer.cs @@ -25,14 +25,14 @@ namespace osu.Game.Tests.Visual.UserInterface [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); - private OverlayScrollContainer scroll; + private TestScrollContainer scroll; private int invocationCount; [SetUp] public void SetUp() => Schedule(() => { - Child = scroll = new OverlayScrollContainer + Child = scroll = new TestScrollContainer { RelativeSizeAxes = Axes.Both, Child = new Container @@ -104,5 +104,10 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("invocation count is 1", () => invocationCount == 1); } + + private class TestScrollContainer : OverlayScrollContainer + { + public new ScrollToTopButton Button => base.Button; + } } } diff --git a/osu.Game/Overlays/OverlayScrollContainer.cs b/osu.Game/Overlays/OverlayScrollContainer.cs index a8f2a0ce6c..9af09f0f6a 100644 --- a/osu.Game/Overlays/OverlayScrollContainer.cs +++ b/osu.Game/Overlays/OverlayScrollContainer.cs @@ -26,7 +26,7 @@ namespace osu.Game.Overlays /// private const int button_scroll_position = 200; - public ScrollToTopButton Button { get; } + protected readonly ScrollToTopButton Button; private float currentTarget; From b8ecc41667301cccff0d3e81bccdc95f6946a69f Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 13 Apr 2020 10:52:34 +0300 Subject: [PATCH 0193/1117] Add comment --- osu.Game/Overlays/OverlayScrollContainer.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/OverlayScrollContainer.cs b/osu.Game/Overlays/OverlayScrollContainer.cs index 9af09f0f6a..e95a6379f1 100644 --- a/osu.Game/Overlays/OverlayScrollContainer.cs +++ b/osu.Game/Overlays/OverlayScrollContainer.cs @@ -56,6 +56,7 @@ namespace osu.Game.Overlays return; } + // Clicking on button should immediately cause it's disappearance, so we don't want to override it's state until we have a new target. if (Target == currentTarget) return; From 1e3251e3e936f0ce0eca7a77dd7ae5dbffab27eb Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 13 Apr 2020 10:59:53 +0300 Subject: [PATCH 0194/1117] Remove excessive logic --- osu.Game/Overlays/OverlayScrollContainer.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/osu.Game/Overlays/OverlayScrollContainer.cs b/osu.Game/Overlays/OverlayScrollContainer.cs index e95a6379f1..e7415e6f74 100644 --- a/osu.Game/Overlays/OverlayScrollContainer.cs +++ b/osu.Game/Overlays/OverlayScrollContainer.cs @@ -28,8 +28,6 @@ namespace osu.Game.Overlays protected readonly ScrollToTopButton Button; - private float currentTarget; - public OverlayScrollContainer() { AddInternal(Button = new ScrollToTopButton @@ -40,7 +38,6 @@ namespace osu.Game.Overlays Action = () => { ScrollToStart(); - currentTarget = Target; Button.State = Visibility.Hidden; } }); @@ -56,11 +53,6 @@ namespace osu.Game.Overlays return; } - // Clicking on button should immediately cause it's disappearance, so we don't want to override it's state until we have a new target. - if (Target == currentTarget) - return; - - currentTarget = Target; Button.State = Target > button_scroll_position ? Visibility.Visible : Visibility.Hidden; } From bb53f96c717e9034c5a2e1b2411c3474d076fc9a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 13 Apr 2020 17:18:50 +0900 Subject: [PATCH 0195/1117] Store states as byte[] instead of Streams --- .../Editor/LegacyEditorBeatmapDifferTest.cs | 19 +++++++-------- osu.Game/Screens/Edit/EditorChangeHandler.cs | 18 ++++++++------ .../Screens/Edit/LegacyEditorBeatmapDiffer.cs | 24 +++++++------------ 3 files changed, 29 insertions(+), 32 deletions(-) diff --git a/osu.Game.Tests/Editor/LegacyEditorBeatmapDifferTest.cs b/osu.Game.Tests/Editor/LegacyEditorBeatmapDifferTest.cs index d70a112b7f..ecd3799cb1 100644 --- a/osu.Game.Tests/Editor/LegacyEditorBeatmapDifferTest.cs +++ b/osu.Game.Tests/Editor/LegacyEditorBeatmapDifferTest.cs @@ -315,26 +315,25 @@ namespace osu.Game.Tests.Editor differ.Patch(encode(current), encode(patch)); // Convert beatmaps to strings for assertion purposes. - string currentStr = Encoding.ASCII.GetString(encode(current).ToArray()); - string patchStr = Encoding.ASCII.GetString(encode(patch).ToArray()); + string currentStr = Encoding.ASCII.GetString(encode(current)); + string patchStr = Encoding.ASCII.GetString(encode(patch)); Assert.That(currentStr, Is.EqualTo(patchStr)); } - private MemoryStream encode(IBeatmap beatmap) + private byte[] encode(IBeatmap beatmap) { - var encoded = new MemoryStream(); - + using (var encoded = new MemoryStream()) using (var sw = new StreamWriter(encoded, leaveOpen: true)) + { new LegacyBeatmapEncoder(beatmap).Encode(sw); - - return encoded; + return encoded.ToArray(); + } } - private IBeatmap decode(Stream stream) + private IBeatmap decode(byte[] state) { - stream.Seek(0, SeekOrigin.Begin); - + using (var stream = new MemoryStream(state)) using (var reader = new LineBufferedReader(stream, true)) return Decoder.GetDecoder(reader).Decode(reader); } diff --git a/osu.Game/Screens/Edit/EditorChangeHandler.cs b/osu.Game/Screens/Edit/EditorChangeHandler.cs index 7e372926ba..22f076d939 100644 --- a/osu.Game/Screens/Edit/EditorChangeHandler.cs +++ b/osu.Game/Screens/Edit/EditorChangeHandler.cs @@ -16,7 +16,9 @@ namespace osu.Game.Screens.Edit public class EditorChangeHandler : IEditorChangeHandler { private readonly LegacyEditorBeatmapDiffer differ; - private readonly List savedStates = new List(); + + private readonly List savedStates = new List(); + private int currentState = -1; private readonly EditorBeatmap editorBeatmap; @@ -69,15 +71,17 @@ namespace osu.Game.Screens.Edit if (isRestoring) return; - var stream = new MemoryStream(); - - using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) - new LegacyBeatmapEncoder(editorBeatmap).Encode(sw); - if (currentState < savedStates.Count - 1) savedStates.RemoveRange(currentState + 1, savedStates.Count - currentState - 1); - savedStates.Add(stream); + using (var stream = new MemoryStream()) + { + using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) + new LegacyBeatmapEncoder(editorBeatmap).Encode(sw); + + savedStates.Add(stream.ToArray()); + } + currentState = savedStates.Count - 1; } diff --git a/osu.Game/Screens/Edit/LegacyEditorBeatmapDiffer.cs b/osu.Game/Screens/Edit/LegacyEditorBeatmapDiffer.cs index 8d2f577a1d..c62f21bb39 100644 --- a/osu.Game/Screens/Edit/LegacyEditorBeatmapDiffer.cs +++ b/osu.Game/Screens/Edit/LegacyEditorBeatmapDiffer.cs @@ -4,12 +4,13 @@ using System; using System.Collections.Generic; using System.IO; +using System.Text; using DiffPlex; using osu.Framework.Audio.Track; using osu.Framework.Graphics.Textures; using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Formats; using osu.Game.IO; +using Decoder = osu.Game.Beatmaps.Formats.Decoder; namespace osu.Game.Screens.Edit { @@ -22,7 +23,7 @@ namespace osu.Game.Screens.Edit this.editorBeatmap = editorBeatmap; } - public void Patch(Stream currentState, Stream newState) + public void Patch(byte[] currentState, byte[] newState) { // Diff the beatmaps var result = new Differ().CreateLineDiffs(readString(currentState), readString(newState), true, false); @@ -36,7 +37,7 @@ namespace osu.Game.Screens.Edit foreach (var block in result.DiffBlocks) { - // Removed hitobject + // Removed hitobjects for (int i = 0; i < block.DeleteCountA; i++) { int hoIndex = block.DeleteStartA + i - oldHitObjectsIndex - 1; @@ -47,7 +48,7 @@ namespace osu.Game.Screens.Edit toRemove.Add(hoIndex); } - // Added hitobject + // Added hitobjects for (int i = 0; i < block.InsertCountB; i++) { int hoIndex = block.InsertStartB + i - newHitObjectsIndex - 1; @@ -74,18 +75,11 @@ namespace osu.Game.Screens.Edit } } - private string readString(Stream stream) + private string readString(byte[] state) => Encoding.UTF8.GetString(state); + + private IBeatmap readBeatmap(byte[] state) { - stream.Seek(0, SeekOrigin.Begin); - - using (var sr = new StreamReader(stream, System.Text.Encoding.UTF8, true, 1024, true)) - return sr.ReadToEnd(); - } - - private IBeatmap readBeatmap(Stream stream) - { - stream.Seek(0, SeekOrigin.Begin); - + using (var stream = new MemoryStream(state)) using (var reader = new LineBufferedReader(stream, true)) return new PassThroughWorkingBeatmap(Decoder.GetDecoder(reader).Decode(reader)).GetPlayableBeatmap(editorBeatmap.BeatmapInfo.Ruleset); } From 6aab19413c793bce5650fa1aa6a95a82a3048e4c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 13 Apr 2020 17:20:01 +0900 Subject: [PATCH 0196/1117] Rename differ to patcher, add xmldoc --- ...mapDifferTest.cs => LegacyEditorBeatmapPatcherTest.cs} | 8 ++++---- osu.Game/Screens/Edit/EditorChangeHandler.cs | 6 +++--- ...itorBeatmapDiffer.cs => LegacyEditorBeatmapPatcher.cs} | 7 +++++-- 3 files changed, 12 insertions(+), 9 deletions(-) rename osu.Game.Tests/Editor/{LegacyEditorBeatmapDifferTest.cs => LegacyEditorBeatmapPatcherTest.cs} (97%) rename osu.Game/Screens/Edit/{LegacyEditorBeatmapDiffer.cs => LegacyEditorBeatmapPatcher.cs} (93%) diff --git a/osu.Game.Tests/Editor/LegacyEditorBeatmapDifferTest.cs b/osu.Game.Tests/Editor/LegacyEditorBeatmapPatcherTest.cs similarity index 97% rename from osu.Game.Tests/Editor/LegacyEditorBeatmapDifferTest.cs rename to osu.Game.Tests/Editor/LegacyEditorBeatmapPatcherTest.cs index ecd3799cb1..11c6399d19 100644 --- a/osu.Game.Tests/Editor/LegacyEditorBeatmapDifferTest.cs +++ b/osu.Game.Tests/Editor/LegacyEditorBeatmapPatcherTest.cs @@ -20,15 +20,15 @@ using Decoder = osu.Game.Beatmaps.Formats.Decoder; namespace osu.Game.Tests.Editor { [TestFixture] - public class LegacyEditorBeatmapDifferTest + public class LegacyEditorBeatmapPatcherTest { - private LegacyEditorBeatmapDiffer differ; + private LegacyEditorBeatmapPatcher patcher; private EditorBeatmap current; [SetUp] public void Setup() { - differ = new LegacyEditorBeatmapDiffer(current = new EditorBeatmap(new OsuBeatmap + patcher = new LegacyEditorBeatmapPatcher(current = new EditorBeatmap(new OsuBeatmap { BeatmapInfo = { @@ -312,7 +312,7 @@ namespace osu.Game.Tests.Editor patch = decode(encode(patch)); // Apply the patch. - differ.Patch(encode(current), encode(patch)); + patcher.Patch(encode(current), encode(patch)); // Convert beatmaps to strings for assertion purposes. string currentStr = Encoding.ASCII.GetString(encode(current)); diff --git a/osu.Game/Screens/Edit/EditorChangeHandler.cs b/osu.Game/Screens/Edit/EditorChangeHandler.cs index 22f076d939..00a27801f4 100644 --- a/osu.Game/Screens/Edit/EditorChangeHandler.cs +++ b/osu.Game/Screens/Edit/EditorChangeHandler.cs @@ -15,7 +15,7 @@ namespace osu.Game.Screens.Edit /// public class EditorChangeHandler : IEditorChangeHandler { - private readonly LegacyEditorBeatmapDiffer differ; + private readonly LegacyEditorBeatmapPatcher patcher; private readonly List savedStates = new List(); @@ -37,7 +37,7 @@ namespace osu.Game.Screens.Edit editorBeatmap.HitObjectRemoved += hitObjectRemoved; editorBeatmap.HitObjectUpdated += hitObjectUpdated; - differ = new LegacyEditorBeatmapDiffer(editorBeatmap); + patcher = new LegacyEditorBeatmapPatcher(editorBeatmap); // Initial state. SaveState(); @@ -103,7 +103,7 @@ namespace osu.Game.Screens.Edit isRestoring = true; - differ.Patch(savedStates[currentState], savedStates[newState]); + patcher.Patch(savedStates[currentState], savedStates[newState]); currentState = newState; isRestoring = false; diff --git a/osu.Game/Screens/Edit/LegacyEditorBeatmapDiffer.cs b/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs similarity index 93% rename from osu.Game/Screens/Edit/LegacyEditorBeatmapDiffer.cs rename to osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs index c62f21bb39..17eba87076 100644 --- a/osu.Game/Screens/Edit/LegacyEditorBeatmapDiffer.cs +++ b/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs @@ -14,11 +14,14 @@ using Decoder = osu.Game.Beatmaps.Formats.Decoder; namespace osu.Game.Screens.Edit { - public class LegacyEditorBeatmapDiffer + /// + /// Patches an based on the difference between two legacy (.osu) states. + /// + public class LegacyEditorBeatmapPatcher { private readonly EditorBeatmap editorBeatmap; - public LegacyEditorBeatmapDiffer(EditorBeatmap editorBeatmap) + public LegacyEditorBeatmapPatcher(EditorBeatmap editorBeatmap) { this.editorBeatmap = editorBeatmap; } From dd949a3fe09ebaa45143be64075efe89aafc08f7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 13 Apr 2020 17:52:04 +0900 Subject: [PATCH 0197/1117] Fix test writer flush happening too late --- osu.Game.Tests/Editor/LegacyEditorBeatmapPatcherTest.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Editor/LegacyEditorBeatmapPatcherTest.cs b/osu.Game.Tests/Editor/LegacyEditorBeatmapPatcherTest.cs index 11c6399d19..c24418d688 100644 --- a/osu.Game.Tests/Editor/LegacyEditorBeatmapPatcherTest.cs +++ b/osu.Game.Tests/Editor/LegacyEditorBeatmapPatcherTest.cs @@ -324,9 +324,10 @@ namespace osu.Game.Tests.Editor private byte[] encode(IBeatmap beatmap) { using (var encoded = new MemoryStream()) - using (var sw = new StreamWriter(encoded, leaveOpen: true)) { - new LegacyBeatmapEncoder(beatmap).Encode(sw); + using (var sw = new StreamWriter(encoded)) + new LegacyBeatmapEncoder(beatmap).Encode(sw); + return encoded.ToArray(); } } @@ -334,7 +335,7 @@ namespace osu.Game.Tests.Editor private IBeatmap decode(byte[] state) { using (var stream = new MemoryStream(state)) - using (var reader = new LineBufferedReader(stream, true)) + using (var reader = new LineBufferedReader(stream)) return Decoder.GetDecoder(reader).Decode(reader); } } From 409cda3cc0d5a8edeb8470a88648499bab9d511d Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2020 08:53:21 +0000 Subject: [PATCH 0198/1117] Bump BenchmarkDotNet from 0.12.0 to 0.12.1 Bumps [BenchmarkDotNet](https://github.com/dotnet/BenchmarkDotNet) from 0.12.0 to 0.12.1. - [Release notes](https://github.com/dotnet/BenchmarkDotNet/releases) - [Commits](https://github.com/dotnet/BenchmarkDotNet/compare/v0.12.0...v0.12.1) Signed-off-by: dependabot-preview[bot] --- osu.Game.Benchmarks/osu.Game.Benchmarks.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj b/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj index f2e1c0ec3b..88fe8f1150 100644 --- a/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj +++ b/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj @@ -7,7 +7,7 @@ - + From b741e359cd5abf64d3428c3ff0da0e2ae1f1e436 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 13 Apr 2020 12:23:28 +0300 Subject: [PATCH 0199/1117] Use OverlayScrollContainer for overlays --- .../Graphics/Containers/SectionsContainer.cs | 27 ++++++++++++------- osu.Game/Overlays/BeatmapListingOverlay.cs | 2 +- osu.Game/Overlays/BeatmapSetOverlay.cs | 4 +-- osu.Game/Overlays/ChangelogOverlay.cs | 2 +- osu.Game/Overlays/NewsOverlay.cs | 2 +- osu.Game/Overlays/RankingsOverlay.cs | 4 +-- .../SearchableList/SearchableListOverlay.cs | 2 +- osu.Game/Overlays/UserProfileOverlay.cs | 2 ++ 8 files changed, 27 insertions(+), 18 deletions(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 07a50c39e1..24c61ad11c 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -3,6 +3,7 @@ using System; using System.Linq; +using JetBrains.Annotations; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -19,7 +20,7 @@ namespace osu.Game.Graphics.Containers private Drawable expandableHeader, fixedHeader, footer, headerBackground; private readonly OsuScrollContainer scrollContainer; private readonly Container headerBackgroundContainer; - private readonly FlowContainer scrollContentContainer; + private FlowContainer scrollContentContainer; protected override Container Content => scrollContentContainer; @@ -125,20 +126,26 @@ namespace osu.Game.Graphics.Containers public SectionsContainer() { - AddInternal(scrollContainer = new OsuScrollContainer + AddRangeInternal(new Drawable[] { - RelativeSizeAxes = Axes.Both, - Masking = true, - ScrollbarVisible = false, - Children = new Drawable[] { scrollContentContainer = CreateScrollContentContainer() } - }); - AddInternal(headerBackgroundContainer = new Container - { - RelativeSizeAxes = Axes.X + scrollContainer = CreateScrollContainer().With(s => + { + s.RelativeSizeAxes = Axes.Both; + s.Masking = true; + s.ScrollbarVisible = false; + s.Children = new Drawable[] { scrollContentContainer = CreateScrollContentContainer() }; + }), + headerBackgroundContainer = new Container + { + RelativeSizeAxes = Axes.X + } }); originalSectionsMargin = scrollContentContainer.Margin; } + [NotNull] + protected virtual OsuScrollContainer CreateScrollContainer() => new OsuScrollContainer(); + public void ScrollTo(Drawable section) => scrollContainer.ScrollTo(scrollContainer.GetChildPosInContent(section) - (FixedHeader?.BoundingBox.Height ?? 0)); public void ScrollToTop() => scrollContainer.ScrollTo(0); diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index 5bac5a5402..b450f33ee1 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -54,7 +54,7 @@ namespace osu.Game.Overlays RelativeSizeAxes = Axes.Both, Colour = ColourProvider.Background6 }, - new BasicScrollContainer + new OverlayScrollContainer { RelativeSizeAxes = Axes.Both, ScrollbarVisible = false, diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index 0d16c4842d..3e23442023 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -39,7 +39,7 @@ namespace osu.Game.Overlays public BeatmapSetOverlay() : base(OverlayColourScheme.Blue) { - OsuScrollContainer scroll; + OverlayScrollContainer scroll; Info info; CommentsSection comments; @@ -49,7 +49,7 @@ namespace osu.Game.Overlays { RelativeSizeAxes = Axes.Both }, - scroll = new OsuScrollContainer + scroll = new OverlayScrollContainer { RelativeSizeAxes = Axes.Both, ScrollbarVisible = false, diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index d13ac5c2de..726be9e194 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -50,7 +50,7 @@ namespace osu.Game.Overlays RelativeSizeAxes = Axes.Both, Colour = ColourProvider.Background4, }, - new OsuScrollContainer + new OverlayScrollContainer { RelativeSizeAxes = Axes.Both, ScrollbarVisible = false, diff --git a/osu.Game/Overlays/NewsOverlay.cs b/osu.Game/Overlays/NewsOverlay.cs index 71c205ff63..6c9477cbc4 100644 --- a/osu.Game/Overlays/NewsOverlay.cs +++ b/osu.Game/Overlays/NewsOverlay.cs @@ -36,7 +36,7 @@ namespace osu.Game.Overlays RelativeSizeAxes = Axes.Both, Colour = colours.PurpleDarkAlternative }, - new OsuScrollContainer + new OverlayScrollContainer { RelativeSizeAxes = Axes.Both, Child = new FillFlowContainer diff --git a/osu.Game/Overlays/RankingsOverlay.cs b/osu.Game/Overlays/RankingsOverlay.cs index afb23883ac..7b200d4226 100644 --- a/osu.Game/Overlays/RankingsOverlay.cs +++ b/osu.Game/Overlays/RankingsOverlay.cs @@ -23,7 +23,7 @@ namespace osu.Game.Overlays protected Bindable Scope => header.Current; - private readonly BasicScrollContainer scrollFlow; + private readonly OverlayScrollContainer scrollFlow; private readonly Container contentContainer; private readonly LoadingLayer loading; private readonly Box background; @@ -44,7 +44,7 @@ namespace osu.Game.Overlays { RelativeSizeAxes = Axes.Both }, - scrollFlow = new BasicScrollContainer + scrollFlow = new OverlayScrollContainer { RelativeSizeAxes = Axes.Both, ScrollbarVisible = false, diff --git a/osu.Game/Overlays/SearchableList/SearchableListOverlay.cs b/osu.Game/Overlays/SearchableList/SearchableListOverlay.cs index d6174e0733..ebd12913f5 100644 --- a/osu.Game/Overlays/SearchableList/SearchableListOverlay.cs +++ b/osu.Game/Overlays/SearchableList/SearchableListOverlay.cs @@ -72,7 +72,7 @@ namespace osu.Game.Overlays.SearchableList { RelativeSizeAxes = Axes.Both, Masking = true, - Child = new OsuScrollContainer + Child = new OverlayScrollContainer { RelativeSizeAxes = Axes.Both, ScrollbarVisible = false, diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs index 6ec30f7707..b4c8a2d3ca 100644 --- a/osu.Game/Overlays/UserProfileOverlay.cs +++ b/osu.Game/Overlays/UserProfileOverlay.cs @@ -195,6 +195,8 @@ namespace osu.Game.Overlays RelativeSizeAxes = Axes.Both; } + protected override OsuScrollContainer CreateScrollContainer() => new OverlayScrollContainer(); + protected override FlowContainer CreateScrollContentContainer() => new FillFlowContainer { Direction = FillDirection.Vertical, From 4c5d01a611ddc88cd847a2d91db27ff57bc3e037 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 13 Apr 2020 12:34:51 +0300 Subject: [PATCH 0200/1117] Remove unused usings --- osu.Game/Overlays/NewsOverlay.cs | 1 - osu.Game/Overlays/SearchableList/SearchableListOverlay.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/osu.Game/Overlays/NewsOverlay.cs b/osu.Game/Overlays/NewsOverlay.cs index 6c9477cbc4..46d692d44d 100644 --- a/osu.Game/Overlays/NewsOverlay.cs +++ b/osu.Game/Overlays/NewsOverlay.cs @@ -8,7 +8,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; -using osu.Game.Graphics.Containers; using osu.Game.Overlays.News; namespace osu.Game.Overlays diff --git a/osu.Game/Overlays/SearchableList/SearchableListOverlay.cs b/osu.Game/Overlays/SearchableList/SearchableListOverlay.cs index ebd12913f5..4ab2de06b6 100644 --- a/osu.Game/Overlays/SearchableList/SearchableListOverlay.cs +++ b/osu.Game/Overlays/SearchableList/SearchableListOverlay.cs @@ -8,7 +8,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Game.Graphics.Backgrounds; -using osu.Game.Graphics.Containers; using osu.Game.Graphics.Cursor; namespace osu.Game.Overlays.SearchableList From cee4b005e6a5ceb3e4af6380f73321205b9d9403 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 13 Apr 2020 20:00:06 +0900 Subject: [PATCH 0201/1117] Fix custom sample set 0 not falling back to default samples --- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 2 +- .../Objects/Legacy/ConvertHitObjectParser.cs | 2 +- osu.Game/Skinning/LegacyBeatmapSkin.cs | 13 +++++++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index 561707f9ef..90a5d0dcba 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -168,7 +168,7 @@ namespace osu.Game.Beatmaps.Formats { var baseInfo = base.ApplyTo(hitSampleInfo); - if (string.IsNullOrEmpty(baseInfo.Suffix) && CustomSampleBank > 1) + if (string.IsNullOrEmpty(baseInfo.Suffix) && CustomSampleBank > 0) baseInfo.Suffix = CustomSampleBank.ToString(); return baseInfo; diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 8d3ad5984f..8580cdede3 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -415,7 +415,7 @@ namespace osu.Game.Rulesets.Objects.Legacy { set { - if (value > 1) + if (value > 0) Suffix = value.ToString(); } } diff --git a/osu.Game/Skinning/LegacyBeatmapSkin.cs b/osu.Game/Skinning/LegacyBeatmapSkin.cs index 1190a330fe..c4636f46f5 100644 --- a/osu.Game/Skinning/LegacyBeatmapSkin.cs +++ b/osu.Game/Skinning/LegacyBeatmapSkin.cs @@ -2,8 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.IO.Stores; +using osu.Game.Audio; using osu.Game.Beatmaps; namespace osu.Game.Skinning @@ -33,6 +35,17 @@ namespace osu.Game.Skinning return base.GetConfig(lookup); } + public override SampleChannel GetSample(ISampleInfo sampleInfo) + { + if (sampleInfo is HitSampleInfo hsi && string.IsNullOrEmpty(hsi.Suffix)) + { + // When no custom sample set is provided, always fall-back to the default samples. + return null; + } + + return base.GetSample(sampleInfo); + } + private static SkinInfo createSkinInfo(BeatmapInfo beatmap) => new SkinInfo { Name = beatmap.ToString(), Creator = beatmap.Metadata.Author.ToString() }; } From 58a7313091812597a622ff0f616fcd23a271febd Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 13 Apr 2020 20:09:17 +0900 Subject: [PATCH 0202/1117] Fix fallback for file hit samples --- osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 8580cdede3..1dca4a5c02 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -425,6 +425,12 @@ namespace osu.Game.Rulesets.Objects.Legacy { public string Filename; + public FileHitSampleInfo() + { + // Has no effect since LookupNames is overridden, however prompts LegacyBeatmapSkin to not fallback. + Suffix = "0"; + } + public override IEnumerable LookupNames => new[] { Filename, From 0be2dc9b2dee3eecb9fdac94a9f8e64745230d72 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 13 Apr 2020 20:12:51 +0900 Subject: [PATCH 0203/1117] Tidy up SectionsContainer class layout/ordering --- .../Graphics/Containers/SectionsContainer.cs | 82 ++++++++++--------- 1 file changed, 43 insertions(+), 39 deletions(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 24c61ad11c..a3125614aa 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -17,12 +17,7 @@ namespace osu.Game.Graphics.Containers public class SectionsContainer : Container where T : Drawable { - private Drawable expandableHeader, fixedHeader, footer, headerBackground; - private readonly OsuScrollContainer scrollContainer; - private readonly Container headerBackgroundContainer; - private FlowContainer scrollContentContainer; - - protected override Container Content => scrollContentContainer; + public Bindable SelectedSection { get; } = new Bindable(); public Drawable ExpandableHeader { @@ -84,6 +79,7 @@ namespace osu.Game.Graphics.Containers headerBackgroundContainer.Clear(); headerBackground = value; + if (value == null) return; headerBackgroundContainer.Add(headerBackground); @@ -92,37 +88,17 @@ namespace osu.Game.Graphics.Containers } } - public Bindable SelectedSection { get; } = new Bindable(); + protected override Container Content => scrollContentContainer; - protected virtual FlowContainer CreateScrollContentContainer() - => new FillFlowContainer - { - Direction = FillDirection.Vertical, - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - }; - - public override void Add(T drawable) - { - base.Add(drawable); - lastKnownScroll = float.NaN; - headerHeight = float.NaN; - footerHeight = float.NaN; - } + private readonly OsuScrollContainer scrollContainer; + private readonly Container headerBackgroundContainer; + private readonly MarginPadding originalSectionsMargin; + private Drawable expandableHeader, fixedHeader, footer, headerBackground; + private FlowContainer scrollContentContainer; private float headerHeight, footerHeight; - private readonly MarginPadding originalSectionsMargin; - private void updateSectionsMargin() - { - if (!Children.Any()) return; - - var newMargin = originalSectionsMargin; - newMargin.Top += headerHeight; - newMargin.Bottom += footerHeight; - - scrollContentContainer.Margin = newMargin; - } + private float lastKnownScroll; public SectionsContainer() { @@ -133,22 +109,41 @@ namespace osu.Game.Graphics.Containers s.RelativeSizeAxes = Axes.Both; s.Masking = true; s.ScrollbarVisible = false; - s.Children = new Drawable[] { scrollContentContainer = CreateScrollContentContainer() }; + s.Child = scrollContentContainer = CreateScrollContentContainer(); }), headerBackgroundContainer = new Container { RelativeSizeAxes = Axes.X } }); + originalSectionsMargin = scrollContentContainer.Margin; } + public override void Add(T drawable) + { + base.Add(drawable); + lastKnownScroll = float.NaN; + headerHeight = float.NaN; + footerHeight = float.NaN; + } + + public void ScrollTo(Drawable section) => + scrollContainer.ScrollTo(scrollContainer.GetChildPosInContent(section) - (FixedHeader?.BoundingBox.Height ?? 0)); + + public void ScrollToTop() => scrollContainer.ScrollTo(0); + [NotNull] protected virtual OsuScrollContainer CreateScrollContainer() => new OsuScrollContainer(); - public void ScrollTo(Drawable section) => scrollContainer.ScrollTo(scrollContainer.GetChildPosInContent(section) - (FixedHeader?.BoundingBox.Height ?? 0)); - - public void ScrollToTop() => scrollContainer.ScrollTo(0); + [NotNull] + protected virtual FlowContainer CreateScrollContentContainer() => + new FillFlowContainer + { + Direction = FillDirection.Vertical, + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + }; protected override bool OnInvalidate(Invalidation invalidation, InvalidationSource source) { @@ -163,8 +158,6 @@ namespace osu.Game.Graphics.Containers return result; } - private float lastKnownScroll; - protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); @@ -215,5 +208,16 @@ namespace osu.Game.Graphics.Containers SelectedSection.Value = bestMatch; } } + + private void updateSectionsMargin() + { + if (!Children.Any()) return; + + var newMargin = originalSectionsMargin; + newMargin.Top += headerHeight; + newMargin.Bottom += footerHeight; + + scrollContentContainer.Margin = newMargin; + } } } From 2388799acfb8a781894d2c41de06c7f25b92bccb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 13 Apr 2020 20:34:18 +0900 Subject: [PATCH 0204/1117] Limit upper number of editor beatmap states saved to 50 --- .../Editor/EditorChangeHandlerTest.cs | 71 +++++++++++++++++++ osu.Game/Screens/Edit/EditorChangeHandler.cs | 7 ++ 2 files changed, 78 insertions(+) create mode 100644 osu.Game.Tests/Editor/EditorChangeHandlerTest.cs diff --git a/osu.Game.Tests/Editor/EditorChangeHandlerTest.cs b/osu.Game.Tests/Editor/EditorChangeHandlerTest.cs new file mode 100644 index 0000000000..ef16976130 --- /dev/null +++ b/osu.Game.Tests/Editor/EditorChangeHandlerTest.cs @@ -0,0 +1,71 @@ +// 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.Screens.Edit; + +namespace osu.Game.Tests.Editor +{ + [TestFixture] + public class EditorChangeHandlerTest + { + [Test] + public void TestSaveRestoreState() + { + var handler = new EditorChangeHandler(new EditorBeatmap(new Beatmap())); + + Assert.That(handler.HasUndoState, Is.False); + + handler.SaveState(); + + Assert.That(handler.HasUndoState, Is.True); + + handler.RestoreState(-1); + + Assert.That(handler.HasUndoState, Is.False); + } + + [Test] + public void TestMaxStatesSaved() + { + var handler = new EditorChangeHandler(new EditorBeatmap(new Beatmap())); + + Assert.That(handler.HasUndoState, Is.False); + + for (int i = 0; i < EditorChangeHandler.MAX_SAVED_STATES; i++) + handler.SaveState(); + + Assert.That(handler.HasUndoState, Is.True); + + for (int i = 0; i < EditorChangeHandler.MAX_SAVED_STATES; i++) + { + Assert.That(handler.HasUndoState, Is.True); + handler.RestoreState(-1); + } + + Assert.That(handler.HasUndoState, Is.False); + } + + [Test] + public void TestMaxStatesExceeded() + { + var handler = new EditorChangeHandler(new EditorBeatmap(new Beatmap())); + + Assert.That(handler.HasUndoState, Is.False); + + for (int i = 0; i < EditorChangeHandler.MAX_SAVED_STATES * 2; i++) + handler.SaveState(); + + Assert.That(handler.HasUndoState, Is.True); + + for (int i = 0; i < EditorChangeHandler.MAX_SAVED_STATES; i++) + { + Assert.That(handler.HasUndoState, Is.True); + handler.RestoreState(-1); + } + + Assert.That(handler.HasUndoState, Is.False); + } + } +} diff --git a/osu.Game/Screens/Edit/EditorChangeHandler.cs b/osu.Game/Screens/Edit/EditorChangeHandler.cs index 00a27801f4..a8204715cd 100644 --- a/osu.Game/Screens/Edit/EditorChangeHandler.cs +++ b/osu.Game/Screens/Edit/EditorChangeHandler.cs @@ -25,6 +25,8 @@ namespace osu.Game.Screens.Edit private int bulkChangesStarted; private bool isRestoring; + public const int MAX_SAVED_STATES = 50; + /// /// Creates a new . /// @@ -43,6 +45,8 @@ namespace osu.Game.Screens.Edit SaveState(); } + public bool HasUndoState => currentState > 0; + private void hitObjectAdded(HitObject obj) => SaveState(); private void hitObjectRemoved(HitObject obj) => SaveState(); @@ -74,6 +78,9 @@ namespace osu.Game.Screens.Edit if (currentState < savedStates.Count - 1) savedStates.RemoveRange(currentState + 1, savedStates.Count - currentState - 1); + if (savedStates.Count > MAX_SAVED_STATES) + savedStates.RemoveAt(0); + using (var stream = new MemoryStream()) { using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) From 1c8a71b2842dd90ff1cdfc1c4d4cd3595a618925 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 13 Apr 2020 21:24:47 +0900 Subject: [PATCH 0205/1117] Exception instead of assert --- osu.Game/Online/API/APIRequest.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/API/APIRequest.cs b/osu.Game/Online/API/APIRequest.cs index 6abb388c01..47600e4f68 100644 --- a/osu.Game/Online/API/APIRequest.cs +++ b/osu.Game/Online/API/APIRequest.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Diagnostics; using Newtonsoft.Json; using osu.Framework.IO.Network; using osu.Framework.Logging; @@ -32,8 +31,8 @@ namespace osu.Game.Online.API internal void TriggerSuccess(T result) { - // disallow calling twice - Debug.Assert(Result == null); + if (Result != null) + throw new InvalidOperationException("Attempted to trigger success more than once"); Result = result; Success?.Invoke(result); From 89d806358809b62ca0c8b81ec77abbc412ae8cbe Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 13 Apr 2020 21:35:35 +0900 Subject: [PATCH 0206/1117] Add support for Perform/PerformAsync --- .../Online/TestDummyAPIRequestHandling.cs | 63 ++++++++++++++++--- osu.Game/Online/API/DummyAPIAccess.cs | 8 ++- 2 files changed, 59 insertions(+), 12 deletions(-) diff --git a/osu.Game.Tests/Online/TestDummyAPIRequestHandling.cs b/osu.Game.Tests/Online/TestDummyAPIRequestHandling.cs index b00b63f6d5..5ef01d5702 100644 --- a/osu.Game.Tests/Online/TestDummyAPIRequestHandling.cs +++ b/osu.Game.Tests/Online/TestDummyAPIRequestHandling.cs @@ -43,17 +43,9 @@ namespace osu.Game.Tests.Online } [Test] - public void TestRequestHandling() + public void TestQueueRequestHandling() { - AddStep("register request handling", () => ((DummyAPIAccess)API).HandleRequest = req => - { - switch (req) - { - case LeaveChannelRequest cRequest: - cRequest.TriggerSuccess(); - break; - } - }); + registerHandler(); LeaveChannelRequest request; bool gotResponse = false; @@ -68,5 +60,56 @@ namespace osu.Game.Tests.Online AddAssert("response event fired", () => gotResponse); } + + [Test] + public void TestPerformRequestHandling() + { + registerHandler(); + + LeaveChannelRequest request; + bool gotResponse = false; + + AddStep("fire request", () => + { + gotResponse = false; + request = new LeaveChannelRequest(new Channel(), new User()); + request.Success += () => gotResponse = true; + API.Perform(request); + }); + + AddAssert("response event fired", () => gotResponse); + } + + [Test] + public void TestPerformAsyncRequestHandling() + { + registerHandler(); + + LeaveChannelRequest request; + bool gotResponse = false; + + AddStep("fire request", () => + { + gotResponse = false; + request = new LeaveChannelRequest(new Channel(), new User()); + request.Success += () => gotResponse = true; + API.PerformAsync(request); + }); + + AddAssert("response event fired", () => gotResponse); + } + + private void registerHandler() + { + AddStep("register request handling", () => ((DummyAPIAccess)API).HandleRequest = req => + { + switch (req) + { + case LeaveChannelRequest cRequest: + cRequest.TriggerSuccess(); + break; + } + }); + } } } diff --git a/osu.Game/Online/API/DummyAPIAccess.cs b/osu.Game/Online/API/DummyAPIAccess.cs index fa5ad115d2..7800241904 100644 --- a/osu.Game/Online/API/DummyAPIAccess.cs +++ b/osu.Game/Online/API/DummyAPIAccess.cs @@ -64,9 +64,13 @@ namespace osu.Game.Online.API HandleRequest?.Invoke(request); } - public void Perform(APIRequest request) { } + public void Perform(APIRequest request) => HandleRequest?.Invoke(request); - public Task PerformAsync(APIRequest request) => Task.CompletedTask; + public Task PerformAsync(APIRequest request) + { + HandleRequest?.Invoke(request); + return Task.CompletedTask; + } public void Register(IOnlineComponent component) { From 4cfc6866835d4d92f2c6f03cd4bfeaa47c845c76 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 13 Apr 2020 21:41:18 +0900 Subject: [PATCH 0207/1117] Fix excption with 0 control points --- .../Sliders/Components/PathControlPointConnectionPiece.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 9c620ecb2f..ba1d35c35c 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs @@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components path.ClearVertices(); int nextIndex = controlPointIndex + 1; - if (nextIndex == 0 || nextIndex == slider.Path.ControlPoints.Count) + if (nextIndex == 0 || nextIndex >= slider.Path.ControlPoints.Count) return; path.AddVertex(Vector2.Zero); From 2b2ab2bf1929d3b6d730d579b73b1bc39e2220e8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 13 Apr 2020 21:59:23 +0900 Subject: [PATCH 0208/1117] Show new segments as red points even when hovered --- .../Blueprints/Sliders/Components/PathControlPointPiece.cs | 7 ++++++- 1 file changed, 6 insertions(+), 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 092a13cca5..fed149b5c5 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -4,6 +4,7 @@ using System; 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.Shapes; @@ -51,6 +52,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components this.slider = slider; ControlPoint = controlPoint; + controlPoint.Type.BindValueChanged(_ => updateMarkerDisplay()); + Origin = Anchor.Centre; AutoSizeAxes = Axes.Both; @@ -183,8 +186,10 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components markerRing.Alpha = IsSelected.Value ? 1 : 0; Color4 colour = ControlPoint.Type.Value != null ? colours.Red : colours.Yellow; + if (IsHovered || IsSelected.Value) - colour = Color4.White; + colour = colour.Lighten(1); + marker.Colour = colour; } } From 13812fef4c1af988e1d292e43d96e20dc17b0784 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Mon, 13 Apr 2020 17:28:02 +0300 Subject: [PATCH 0209/1117] Replace BindTo with setting the bindable --- osu.Game/Overlays/Direct/PanelDownloadButton.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Direct/PanelDownloadButton.cs b/osu.Game/Overlays/Direct/PanelDownloadButton.cs index 51f5b2ae4f..dfcaf5ded6 100644 --- a/osu.Game/Overlays/Direct/PanelDownloadButton.cs +++ b/osu.Game/Overlays/Direct/PanelDownloadButton.cs @@ -18,7 +18,7 @@ namespace osu.Game.Overlays.Direct private readonly ShakeContainer shakeContainer; private readonly DownloadButton button; - private readonly BindableBool noVideoSetting = new BindableBool(); + private Bindable noVideoSetting; public PanelDownloadButton(BeatmapSetInfo beatmapSet) : base(beatmapSet) @@ -51,7 +51,7 @@ namespace osu.Game.Overlays.Direct return; } - noVideoSetting.BindTo(osuConfig.GetBindable(OsuSetting.PreferNoVideo)); + noVideoSetting = osuConfig.GetBindable(OsuSetting.PreferNoVideo); button.Action = () => { From 3e48c26bc24eedfdc2fe0a8a6ac01f5377648ca4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 14 Apr 2020 00:54:02 +0200 Subject: [PATCH 0210/1117] Add failing tests --- .../Rulesets/Scoring/ScoreProcessorTest.cs | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs diff --git a/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs b/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs new file mode 100644 index 0000000000..64d1024efb --- /dev/null +++ b/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs @@ -0,0 +1,57 @@ +// 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.Utils; +using osu.Game.Beatmaps; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu.Judgements; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Scoring; +using osu.Game.Tests.Beatmaps; + +namespace osu.Game.Tests.Rulesets.Scoring +{ + public class ScoreProcessorTest + { + private ScoreProcessor scoreProcessor; + private IBeatmap beatmap; + + [SetUp] + public void SetUp() + { + scoreProcessor = new ScoreProcessor(); + beatmap = new TestBeatmap(new RulesetInfo()) + { + HitObjects = new List + { + new HitCircle() + } + }; + } + + [TestCase(ScoringMode.Standardised, HitResult.Meh, 750_000)] + [TestCase(ScoringMode.Standardised, HitResult.Good, 800_000)] + [TestCase(ScoringMode.Standardised, HitResult.Great, 1_000_000)] + [TestCase(ScoringMode.Classic, HitResult.Meh, 50)] + [TestCase(ScoringMode.Classic, HitResult.Good, 100)] + [TestCase(ScoringMode.Classic, HitResult.Great, 300)] + public void TestSingleOsuHit(ScoringMode scoringMode, HitResult hitResult, int expectedScore) + { + scoreProcessor.Mode.Value = scoringMode; + scoreProcessor.ApplyBeatmap(beatmap); + + var judgementResult = new JudgementResult(beatmap.HitObjects.Single(), new OsuJudgement()) + { + Type = hitResult + }; + scoreProcessor.ApplyResult(judgementResult); + + Assert.IsTrue(Precision.AlmostEquals(expectedScore, scoreProcessor.TotalScore.Value)); + } + } +} From 13c81db0cf90fcb1db1909a20aa753c23884faea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 14 Apr 2020 00:56:37 +0200 Subject: [PATCH 0211/1117] Fix incorrect classic score formula Upon closer inspection the classic score formula was subtly wrong. The version given in the wiki is: Score = Hit Value + (Hit Value * ((Combo multiplier * Difficulty multiplier * Mod multiplier) / 25)) The code previously used: bonusScore + baseScore * ((1 + Math.Max(0, HighestCombo.Value - 1) * scoreMultiplier) / 25); which is not equivalent to the version on the wiki. The error is in the 1 factor, as in the above version it is being divided by 25, while it should be outside the division to keep parity with the previous formula. The tests attached in the previous commit demonstrate that this change causes a single hit without combo to increase total score by its exact numeric value. --- 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 8eafaa88ec..1f40f44dce 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -174,7 +174,7 @@ namespace osu.Game.Rulesets.Scoring case ScoringMode.Classic: // should emulate osu-stable's scoring as closely as we can (https://osu.ppy.sh/help/wiki/Score/ScoreV1) - return bonusScore + baseScore * ((1 + Math.Max(0, HighestCombo.Value - 1) * scoreMultiplier) / 25); + return bonusScore + baseScore * (1 + Math.Max(0, HighestCombo.Value - 1) * scoreMultiplier / 25); } } From 5f13dc81bed4d90e9fd43c5a3e96573de00951dd Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 14 Apr 2020 04:38:18 +0300 Subject: [PATCH 0212/1117] Remove no longer necessary extensions --- .../Skinning/CatchSkinExtensions.cs | 20 ------------------- .../Skinning/LegacyFruitPiece.cs | 9 ++++++--- 2 files changed, 6 insertions(+), 23 deletions(-) delete mode 100644 osu.Game.Rulesets.Catch/Skinning/CatchSkinExtensions.cs diff --git a/osu.Game.Rulesets.Catch/Skinning/CatchSkinExtensions.cs b/osu.Game.Rulesets.Catch/Skinning/CatchSkinExtensions.cs deleted file mode 100644 index 718b22a0fb..0000000000 --- a/osu.Game.Rulesets.Catch/Skinning/CatchSkinExtensions.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 JetBrains.Annotations; -using osu.Framework.Bindables; -using osu.Game.Rulesets.Catch.UI; -using osu.Game.Skinning; -using osuTK.Graphics; - -namespace osu.Game.Rulesets.Catch.Skinning -{ - internal static class CatchSkinExtensions - { - [NotNull] - public static IBindable GetHyperDashFruitColour(this ISkin skin) - => skin.GetConfig(CatchSkinColour.HyperDashFruit) ?? - skin.GetConfig(CatchSkinColour.HyperDash) ?? - new Bindable(Catcher.DEFAULT_HYPER_DASH_COLOUR); - } -} diff --git a/osu.Game.Rulesets.Catch/Skinning/LegacyFruitPiece.cs b/osu.Game.Rulesets.Catch/Skinning/LegacyFruitPiece.cs index 470c12559e..5be54d3882 100644 --- a/osu.Game.Rulesets.Catch/Skinning/LegacyFruitPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/LegacyFruitPiece.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Game.Rulesets.Catch.Objects.Drawables; +using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Skinning; using osuTK; @@ -55,14 +56,16 @@ namespace osu.Game.Rulesets.Catch.Skinning { var hyperDash = new Sprite { - Texture = skin.GetTexture(lookupName), - Colour = skin.GetHyperDashFruitColour().Value, Anchor = Anchor.Centre, Origin = Anchor.Centre, Blending = BlendingParameters.Additive, Depth = 1, Alpha = 0.7f, - Scale = new Vector2(1.2f) + Scale = new Vector2(1.2f), + Texture = skin.GetTexture(lookupName), + Colour = skin.GetConfig(CatchSkinColour.HyperDashFruit)?.Value ?? + skin.GetConfig(CatchSkinColour.HyperDash)?.Value ?? + Catcher.DEFAULT_HYPER_DASH_COLOUR, }; AddInternal(hyperDash); From c5f8bbb25fe55fd89af091ce27b8f5ba27aaa9f9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Apr 2020 11:56:37 +0900 Subject: [PATCH 0213/1117] Fix beatmap background not displaying when video is present --- osu.Game/Storyboards/Storyboard.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Storyboards/Storyboard.cs b/osu.Game/Storyboards/Storyboard.cs index a1ddafbacf..d13c874ee2 100644 --- a/osu.Game/Storyboards/Storyboard.cs +++ b/osu.Game/Storyboards/Storyboard.cs @@ -47,9 +47,6 @@ namespace osu.Game.Storyboards if (backgroundPath == null) return false; - if (GetLayer("Video").Elements.Any()) - return true; - return GetLayer("Background").Elements.Any(e => e.Path.ToLowerInvariant() == backgroundPath); } } From 3c5fb7982351a86964b3fa65c515c61f36b4d9e8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 14 Apr 2020 14:51:09 +0900 Subject: [PATCH 0214/1117] Mark dummy api test scene as headless --- osu.Game.Tests/Online/TestDummyAPIRequestHandling.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Online/TestDummyAPIRequestHandling.cs b/osu.Game.Tests/Online/TestDummyAPIRequestHandling.cs index 5ef01d5702..1e77d50115 100644 --- a/osu.Game.Tests/Online/TestDummyAPIRequestHandling.cs +++ b/osu.Game.Tests/Online/TestDummyAPIRequestHandling.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using NUnit.Framework; +using osu.Framework.Testing; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; @@ -11,6 +12,7 @@ using osu.Game.Users; namespace osu.Game.Tests.Online { + [HeadlessTest] public class TestDummyAPIRequestHandling : OsuTestScene { [Test] From 9619fb9f6a5fa140b3bbf0226cf12c879619c66e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 14 Apr 2020 15:00:36 +0900 Subject: [PATCH 0215/1117] Remove bind in Player --- osu.Game/Screens/Play/Player.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index f1df69c5db..4597ae760c 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -27,7 +27,6 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Scoring; -using osu.Game.Screens.Play.HUD; using osu.Game.Scoring.Legacy; using osu.Game.Screens.Ranking; using osu.Game.Skinning; @@ -207,9 +206,6 @@ namespace osu.Game.Screens.Play foreach (var mod in Mods.Value.OfType()) mod.ApplyToHealthProcessor(HealthProcessor); - foreach (var overlay in DrawableRuleset.Overlays.OfType()) - overlay.BindHealthProcessor(HealthProcessor); - breakTracker.IsBreakTime.BindValueChanged(onBreakTimeChanged, true); } From 7d2d0785fd0cda708e8cfe0ecf867c7eb6214bb6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 14 Apr 2020 15:07:32 +0900 Subject: [PATCH 0216/1117] Fix potential unsafe ordering of binds --- osu.Game/Screens/Play/HUD/FailingLayer.cs | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/FailingLayer.cs b/osu.Game/Screens/Play/HUD/FailingLayer.cs index a1188343ac..aa15c1fd45 100644 --- a/osu.Game/Screens/Play/HUD/FailingLayer.cs +++ b/osu.Game/Screens/Play/HUD/FailingLayer.cs @@ -26,7 +26,8 @@ namespace osu.Game.Screens.Play.HUD private const int fade_time = 400; - private Bindable enabled; + private readonly Bindable enabled = new Bindable(); + private Bindable configEnabled; /// /// The threshold under which the current player life should be considered low and the layer should start fading in. @@ -36,6 +37,7 @@ namespace osu.Game.Screens.Play.HUD private const float gradient_size = 0.3f; private readonly Container boxes; + private HealthProcessor healthProcessor; public FailingLayer() { @@ -73,16 +75,29 @@ namespace osu.Game.Screens.Play.HUD { boxes.Colour = color.Red; - enabled = config.GetBindable(OsuSetting.FadePlayfieldWhenHealthLow); + configEnabled = config.GetBindable(OsuSetting.FadePlayfieldWhenHealthLow); enabled.BindValueChanged(e => this.FadeTo(e.NewValue ? 1 : 0, fade_time, Easing.OutQuint), true); + + updateBindings(); } public override void BindHealthProcessor(HealthProcessor processor) { base.BindHealthProcessor(processor); - // don't display ever if the ruleset is not using a draining health display. - if (!(processor is DrainingHealthProcessor)) + healthProcessor = processor; + updateBindings(); + } + + private void updateBindings() + { + if (configEnabled == null || healthProcessor == null) + return; + + // Don't display ever if the ruleset is not using a draining health display. + if (healthProcessor is DrainingHealthProcessor) + enabled.BindTo(configEnabled); + else { enabled.UnbindBindings(); enabled.Value = false; From 3183827329e477c8edb138f0f3930fcc450bf8a8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 14 Apr 2020 15:09:31 +0900 Subject: [PATCH 0217/1117] Reorder fields --- osu.Game/Screens/Play/HUD/FailingLayer.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/FailingLayer.cs b/osu.Game/Screens/Play/HUD/FailingLayer.cs index aa15c1fd45..cea85af112 100644 --- a/osu.Game/Screens/Play/HUD/FailingLayer.cs +++ b/osu.Game/Screens/Play/HUD/FailingLayer.cs @@ -23,20 +23,18 @@ namespace osu.Game.Screens.Play.HUD public class FailingLayer : HealthDisplay { private const float max_alpha = 0.4f; - private const int fade_time = 400; - - private readonly Bindable enabled = new Bindable(); - private Bindable configEnabled; + private const float gradient_size = 0.3f; /// /// The threshold under which the current player life should be considered low and the layer should start fading in. /// public double LowHealthThreshold = 0.20f; - private const float gradient_size = 0.3f; - + private readonly Bindable enabled = new Bindable(); private readonly Container boxes; + + private Bindable configEnabled; private HealthProcessor healthProcessor; public FailingLayer() From b8b334ca27d853b81bf86eeb93d29a91d3ca4f34 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 14 Apr 2020 15:21:56 +0900 Subject: [PATCH 0218/1117] Always unbind bindings --- osu.Game/Screens/Play/HUD/FailingLayer.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/FailingLayer.cs b/osu.Game/Screens/Play/HUD/FailingLayer.cs index cea85af112..cb8b5c1a9d 100644 --- a/osu.Game/Screens/Play/HUD/FailingLayer.cs +++ b/osu.Game/Screens/Play/HUD/FailingLayer.cs @@ -92,14 +92,13 @@ namespace osu.Game.Screens.Play.HUD if (configEnabled == null || healthProcessor == null) return; + enabled.UnbindBindings(); + // Don't display ever if the ruleset is not using a draining health display. if (healthProcessor is DrainingHealthProcessor) enabled.BindTo(configEnabled); else - { - enabled.UnbindBindings(); enabled.Value = false; - } } protected override void Update() From 59728ffebddcdb47c121d7eaf3b1f80687f0e63a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 14 Apr 2020 15:24:34 +0900 Subject: [PATCH 0219/1117] Fix up/improve test scene --- .../Visual/Gameplay/TestSceneFailingLayer.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs index d831ea1835..a95e806862 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs @@ -20,7 +20,12 @@ namespace osu.Game.Tests.Visual.Gameplay [SetUpSteps] public void SetUpSteps() { - AddStep("create layer", () => Child = layer = new FailingLayer()); + AddStep("create layer", () => + { + Child = layer = new FailingLayer(); + layer.BindHealthProcessor(new DrainingHealthProcessor(1)); + }); + AddStep("enable layer", () => config.Set(OsuSetting.FadePlayfieldWhenHealthLow, true)); AddUntilStep("layer is visible", () => layer.IsPresent); } @@ -44,6 +49,7 @@ namespace osu.Game.Tests.Visual.Gameplay public void TestLayerDisabledViaConfig() { AddStep("disable layer", () => config.Set(OsuSetting.FadePlayfieldWhenHealthLow, false)); + AddStep("set health to 0.10", () => layer.Current.Value = 0.1); AddUntilStep("layer is not visible", () => !layer.IsPresent); } @@ -51,6 +57,7 @@ namespace osu.Game.Tests.Visual.Gameplay public void TestLayerVisibilityWithAccumulatingProcessor() { AddStep("bind accumulating processor", () => layer.BindHealthProcessor(new AccumulatingHealthProcessor(1))); + AddStep("set health to 0.10", () => layer.Current.Value = 0.1); AddUntilStep("layer is not visible", () => !layer.IsPresent); } @@ -58,6 +65,7 @@ namespace osu.Game.Tests.Visual.Gameplay public void TestLayerVisibilityWithDrainingProcessor() { AddStep("bind accumulating processor", () => layer.BindHealthProcessor(new DrainingHealthProcessor(1))); + AddStep("set health to 0.10", () => layer.Current.Value = 0.1); AddWaitStep("wait for potential fade", 10); AddAssert("layer is still visible", () => layer.IsPresent); } From f3dbddd75ca735dfb75fb34efb15a2f14370a440 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 14 Apr 2020 15:52:38 +0900 Subject: [PATCH 0220/1117] Update bindings in LoadComplete() --- osu.Game/Screens/Play/HUD/FailingLayer.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/FailingLayer.cs b/osu.Game/Screens/Play/HUD/FailingLayer.cs index cb8b5c1a9d..a49aa89a7c 100644 --- a/osu.Game/Screens/Play/HUD/FailingLayer.cs +++ b/osu.Game/Screens/Play/HUD/FailingLayer.cs @@ -75,7 +75,11 @@ namespace osu.Game.Screens.Play.HUD configEnabled = config.GetBindable(OsuSetting.FadePlayfieldWhenHealthLow); enabled.BindValueChanged(e => this.FadeTo(e.NewValue ? 1 : 0, fade_time, Easing.OutQuint), true); + } + protected override void LoadComplete() + { + base.LoadComplete(); updateBindings(); } @@ -89,7 +93,7 @@ namespace osu.Game.Screens.Play.HUD private void updateBindings() { - if (configEnabled == null || healthProcessor == null) + if (LoadState < LoadState.Ready) return; enabled.UnbindBindings(); From 7f95418262d84096ea2afb38896d674170f9942e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Apr 2020 16:52:17 +0900 Subject: [PATCH 0221/1117] Fix osu!mania replays actuating incorrect keys when multiple stages are involved --- .../ManiaLegacyReplayTest.cs | 51 ++++++++++++ .../Replays/ManiaReplayFrame.cs | 83 ++++++++++++++++--- 2 files changed, 121 insertions(+), 13 deletions(-) create mode 100644 osu.Game.Rulesets.Mania.Tests/ManiaLegacyReplayTest.cs diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaLegacyReplayTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaLegacyReplayTest.cs new file mode 100644 index 0000000000..40bb83aece --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests/ManiaLegacyReplayTest.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 NUnit.Framework; +using osu.Game.Rulesets.Mania.Beatmaps; +using osu.Game.Rulesets.Mania.Replays; + +namespace osu.Game.Rulesets.Mania.Tests +{ + [TestFixture] + public class ManiaLegacyReplayTest + { + [TestCase(ManiaAction.Key1)] + [TestCase(ManiaAction.Key1, ManiaAction.Key2)] + [TestCase(ManiaAction.Special1)] + [TestCase(ManiaAction.Key8)] + public void TestEncodeDecodeSingleStage(params ManiaAction[] actions) + { + var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 9 }); + + var frame = new ManiaReplayFrame(0, actions); + var legacyFrame = frame.ToLegacy(beatmap); + + var decodedFrame = new ManiaReplayFrame(); + decodedFrame.FromLegacy(legacyFrame, beatmap); + + Assert.That(decodedFrame.Actions, Is.EquivalentTo(frame.Actions)); + } + + [TestCase(ManiaAction.Key1)] + [TestCase(ManiaAction.Key1, ManiaAction.Key2)] + [TestCase(ManiaAction.Special1)] + [TestCase(ManiaAction.Special2)] + [TestCase(ManiaAction.Special1, ManiaAction.Special2)] + [TestCase(ManiaAction.Special1, ManiaAction.Key5)] + [TestCase(ManiaAction.Key8)] + public void TestEncodeDecodeDualStage(params ManiaAction[] actions) + { + var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 5 }); + beatmap.Stages.Add(new StageDefinition { Columns = 5 }); + + var frame = new ManiaReplayFrame(0, actions); + var legacyFrame = frame.ToLegacy(beatmap); + + var decodedFrame = new ManiaReplayFrame(); + decodedFrame.FromLegacy(legacyFrame, beatmap); + + Assert.That(decodedFrame.Actions, Is.EquivalentTo(frame.Actions)); + } + } +} diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs index 8c73c36e99..0059a78a44 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.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; using System.Collections.Generic; -using System.Linq; using osu.Game.Beatmaps; using osu.Game.Replays.Legacy; using osu.Game.Rulesets.Mania.Beatmaps; @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Mania.Replays while (activeColumns > 0) { - var isSpecial = maniaBeatmap.Stages.First().IsSpecialColumn(counter); + bool isSpecial = isColumnAtIndexSpecial(maniaBeatmap, counter); if ((activeColumns & 1) > 0) Actions.Add(isSpecial ? specialAction : normalAction); @@ -58,33 +58,90 @@ namespace osu.Game.Rulesets.Mania.Replays int keys = 0; - var specialColumns = new List(); - - for (int i = 0; i < maniaBeatmap.TotalColumns; i++) - { - if (maniaBeatmap.Stages.First().IsSpecialColumn(i)) - specialColumns.Add(i); - } - foreach (var action in Actions) { switch (action) { case ManiaAction.Special1: - keys |= 1 << specialColumns[0]; + keys |= 1 << getSpecialColumnIndex(maniaBeatmap, 0); break; case ManiaAction.Special2: - keys |= 1 << specialColumns[1]; + keys |= 1 << getSpecialColumnIndex(maniaBeatmap, 1); break; default: - keys |= 1 << (action - ManiaAction.Key1); + // the index in lazer, which doesn't include special keys. + int nonSpecialKeyIndex = action - ManiaAction.Key1; + + int overallIndex = 0; + + // iterate to find the index including special keys. + while (true) + { + if (!isColumnAtIndexSpecial(maniaBeatmap, overallIndex)) + { + // found a non-special column we could use. + if (nonSpecialKeyIndex == 0) + break; + + // found a non-special column but not ours. + nonSpecialKeyIndex--; + } + + overallIndex++; + } + + keys |= 1 << overallIndex; break; } } return new LegacyReplayFrame(Time, keys, null, ReplayButtonState.None); } + + /// + /// Find the overall index (across all stages) for a specified special key. + /// + /// The beatmap. + /// The special key offset (0 is S1). + /// The overall index for the special column. + private int getSpecialColumnIndex(ManiaBeatmap maniaBeatmap, int specialOffset) + { + for (int i = 0; i < maniaBeatmap.TotalColumns; i++) + { + if (isColumnAtIndexSpecial(maniaBeatmap, i)) + { + if (specialOffset == 0) + return i; + + specialOffset--; + } + } + + throw new InvalidOperationException("Special key index too high"); + } + + /// + /// Check whether the column at an overall index (across all stages) is a special column. + /// + /// The beatmap. + /// The overall index to check. + /// + private bool isColumnAtIndexSpecial(ManiaBeatmap beatmap, int index) + { + foreach (var stage in beatmap.Stages) + { + for (int stageIndex = 0; stageIndex < stage.Columns; stageIndex++) + { + if (index == 0) + return stage.IsSpecialColumn(stageIndex); + + index--; + } + } + + return false; + } } } From 69352214637b9b5ef6f7e9d56da30da98455584a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 14 Apr 2020 21:05:07 +0900 Subject: [PATCH 0222/1117] Improve logic for CSB transfer --- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 8 ++++-- .../Objects/Legacy/ConvertHitObjectParser.cs | 28 ++++++++++++++++--- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index 90a5d0dcba..5b2b213322 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -8,6 +8,7 @@ using osu.Framework.Logging; using osu.Game.Audio; using osu.Game.Beatmaps.ControlPoints; using osu.Game.IO; +using osu.Game.Rulesets.Objects.Legacy; using osuTK.Graphics; namespace osu.Game.Beatmaps.Formats @@ -168,8 +169,11 @@ namespace osu.Game.Beatmaps.Formats { var baseInfo = base.ApplyTo(hitSampleInfo); - if (string.IsNullOrEmpty(baseInfo.Suffix) && CustomSampleBank > 0) - baseInfo.Suffix = CustomSampleBank.ToString(); + if (baseInfo is ConvertHitObjectParser.LegacyHitSampleInfo legacy + && legacy.CustomSampleBank == 0) + { + legacy.CustomSampleBank = CustomSampleBank; + } return baseInfo; } diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 1dca4a5c02..95cbf3ab40 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -409,26 +409,46 @@ namespace osu.Game.Rulesets.Objects.Legacy public SampleBankInfo Clone() => (SampleBankInfo)MemberwiseClone(); } - private class LegacyHitSampleInfo : HitSampleInfo + internal class LegacyHitSampleInfo : HitSampleInfo { + private int customSampleBank; + public int CustomSampleBank { + get => customSampleBank; set { + customSampleBank = value; + + // A 0 custom sample bank should cause LegacyBeatmapSkin to always fall back to the user skin. This is done by giving a null suffix. if (value > 0) Suffix = value.ToString(); } } + + public override IEnumerable LookupNames + { + get + { + // The lookup should only contain the suffix for custom sample bank 2 and beyond. + // For custom sample bank 1 and 0, the lookup should not contain the suffix as only the lookup source (beatmap or user skin) is changed. + if (CustomSampleBank >= 2) + yield return $"{Bank}-{Name}{Suffix}"; + + yield return $"{Bank}-{Name}"; + } + } } - private class FileHitSampleInfo : HitSampleInfo + private class FileHitSampleInfo : LegacyHitSampleInfo { public string Filename; public FileHitSampleInfo() { - // Has no effect since LookupNames is overridden, however prompts LegacyBeatmapSkin to not fallback. - Suffix = "0"; + // Make sure that the LegacyBeatmapSkin does not fall back to the user skin. + // Note that this does not change the lookup names, as they are overridden locally. + CustomSampleBank = 1; } public override IEnumerable LookupNames => new[] From b29957798f38c28f3940649ecc13be0ca1ee5b8e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 14 Apr 2020 21:05:42 +0900 Subject: [PATCH 0223/1117] Fix no audiomanager in test scene working beatmap --- osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs | 6 ++++-- osu.Game/Tests/Visual/OsuTestScene.cs | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs b/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs index 6db34af20c..8f8afb87d4 100644 --- a/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs +++ b/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.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.Audio; using osu.Framework.Audio.Track; using osu.Framework.Graphics.Textures; using osu.Game.Beatmaps; @@ -18,8 +19,9 @@ namespace osu.Game.Tests.Beatmaps /// /// The beatmap. /// An optional storyboard. - public TestWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null) - : base(beatmap.BeatmapInfo, null) + /// The . + public TestWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null, AudioManager audioManager = null) + : base(beatmap.BeatmapInfo, audioManager) { this.beatmap = beatmap; this.storyboard = storyboard; diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs index d1d8059cb1..5dc8714c07 100644 --- a/osu.Game/Tests/Visual/OsuTestScene.cs +++ b/osu.Game/Tests/Visual/OsuTestScene.cs @@ -179,7 +179,7 @@ namespace osu.Game.Tests.Visual /// Audio manager. Required if a reference clock isn't provided. /// The length of the returned virtual track. public ClockBackedTestWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard, IFrameBasedClock referenceClock, AudioManager audio, double length = 60000) - : base(beatmap, storyboard) + : base(beatmap, storyboard, audio) { if (referenceClock != null) { From 00d564d29cafc4b4319e7165de2d5a5210c9d083 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 14 Apr 2020 21:05:54 +0900 Subject: [PATCH 0224/1117] Add tests --- .../Gameplay/TestSceneHitObjectSamples.cs | 344 ++++++++++++++++++ .../controlpoint-beatmap-custom-sample.osu | 7 + .../controlpoint-beatmap-sample.osu | 7 + .../controlpoint-skin-sample.osu | 7 + .../SampleLookups/file-beatmap-sample.osu | 4 + ...tobject-beatmap-custom-sample-override.osu | 7 + .../hitobject-beatmap-custom-sample.osu | 4 + .../hitobject-beatmap-sample.osu | 4 + .../SampleLookups/hitobject-skin-sample.osu | 4 + 9 files changed, 388 insertions(+) create mode 100644 osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs create mode 100644 osu.Game.Tests/Resources/SampleLookups/controlpoint-beatmap-custom-sample.osu create mode 100644 osu.Game.Tests/Resources/SampleLookups/controlpoint-beatmap-sample.osu create mode 100644 osu.Game.Tests/Resources/SampleLookups/controlpoint-skin-sample.osu create mode 100644 osu.Game.Tests/Resources/SampleLookups/file-beatmap-sample.osu create mode 100644 osu.Game.Tests/Resources/SampleLookups/hitobject-beatmap-custom-sample-override.osu create mode 100644 osu.Game.Tests/Resources/SampleLookups/hitobject-beatmap-custom-sample.osu create mode 100644 osu.Game.Tests/Resources/SampleLookups/hitobject-beatmap-sample.osu create mode 100644 osu.Game.Tests/Resources/SampleLookups/hitobject-skin-sample.osu diff --git a/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs b/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs new file mode 100644 index 0000000000..f80ea3ae88 --- /dev/null +++ b/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs @@ -0,0 +1,344 @@ +// 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.Linq; +using System.Threading.Tasks; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.IO.Stores; +using osu.Framework.Timing; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Formats; +using osu.Game.IO; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Osu; +using osu.Game.Skinning; +using osu.Game.Storyboards; +using osu.Game.Tests.Resources; +using osu.Game.Tests.Visual; +using osu.Game.Users; + +namespace osu.Game.Tests.Gameplay +{ + public class TestSceneHitObjectSamples : PlayerTestScene + { + private readonly SkinInfo userSkinInfo = new SkinInfo(); + + private readonly BeatmapInfo beatmapInfo = new BeatmapInfo + { + BeatmapSet = new BeatmapSetInfo(), + Metadata = new BeatmapMetadata + { + Author = User.SYSTEM_USER + } + }; + + private readonly TestResourceStore userSkinResourceStore = new TestResourceStore(); + private readonly TestResourceStore beatmapSkinResourceStore = new TestResourceStore(); + + protected override bool HasCustomSteps => true; + + public TestSceneHitObjectSamples() + : base(new OsuRuleset()) + { + } + + private SkinSourceDependencyContainer dependencies; + + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) + => new DependencyContainer(dependencies = new SkinSourceDependencyContainer(base.CreateChildDependencies(parent))); + + /// + /// Tests that a hitobject which provides no custom sample set retrieves samples from the user skin. + /// + [Test] + public void TestDefaultSampleFromUserSkin() + { + const string expected_sample = "normal-hitnormal"; + + setupSkins(expected_sample, expected_sample); + + createTestWithBeatmap("hitobject-skin-sample.osu"); + + assertUserLookup(expected_sample); + } + + /// + /// Tests that a hitobject which provides a sample set of 1 retrieves samples from the beatmap skin. + /// + [Test] + public void TestDefaultSampleFromBeatmap() + { + const string expected_sample = "normal-hitnormal"; + + setupSkins(expected_sample, expected_sample); + + createTestWithBeatmap("hitobject-beatmap-sample.osu"); + + assertBeatmapLookup(expected_sample); + } + + /// + /// Tests that a hitobject which provides a sample set of 1 retrieves samples from the user skin when the beatmap does not contain the sample. + /// + [Test] + public void TestDefaultSampleFromUserSkinFallback() + { + const string expected_sample = "normal-hitnormal"; + + setupSkins(null, expected_sample); + + createTestWithBeatmap("hitobject-beatmap-sample.osu"); + + assertUserLookup(expected_sample); + } + + /// + /// Tests that a hitobject which provides a custom sample set of 2 retrieves the following samples from the beatmap skin: + /// normal-hitnormal2 + /// normal-hitnormal + /// + [TestCase("normal-hitnormal2")] + [TestCase("normal-hitnormal")] + public void TestDefaultCustomSampleFromBeatmap(string expectedSample) + { + setupSkins(expectedSample, expectedSample); + + createTestWithBeatmap("hitobject-beatmap-custom-sample.osu"); + + assertBeatmapLookup(expectedSample); + } + + /// + /// Tests that a hitobject which provides a custom sample set of 2 retrieves the following samples from the user skin when the beatmap does not contain the sample: + /// normal-hitnormal2 + /// normal-hitnormal + /// + [TestCase("normal-hitnormal2")] + [TestCase("normal-hitnormal")] + public void TestDefaultCustomSampleFromUserSkinFallback(string expectedSample) + { + setupSkins(string.Empty, expectedSample); + + createTestWithBeatmap("hitobject-beatmap-custom-sample.osu"); + + assertUserLookup(expectedSample); + } + + /// + /// Tests that a hitobject which provides a sample file retrieves the sample file from the beatmap skin. + /// + [Test] + public void TestFileSampleFromBeatmap() + { + const string expected_sample = "hit_1.wav"; + + setupSkins(expected_sample, expected_sample); + + createTestWithBeatmap("file-beatmap-sample.osu"); + + assertBeatmapLookup(expected_sample); + } + + /// + /// Tests that a default hitobject and control point causes . + /// + [Test] + public void TestControlPointSampleFromSkin() + { + const string expected_sample = "normal-hitnormal"; + + setupSkins(expected_sample, expected_sample); + + createTestWithBeatmap("controlpoint-skin-sample.osu"); + + assertUserLookup(expected_sample); + } + + /// + /// Tests that a control point that provides a custom sample set of 1 causes . + /// + [Test] + public void TestControlPointSampleFromBeatmap() + { + const string expected_sample = "normal-hitnormal"; + + setupSkins(expected_sample, expected_sample); + + createTestWithBeatmap("controlpoint-beatmap-sample.osu"); + + assertBeatmapLookup(expected_sample); + } + + /// + /// Tests that a control point that provides a custom sample of 2 causes . + /// + [TestCase("normal-hitnormal2")] + [TestCase("normal-hitnormal")] + public void TestControlPointCustomSampleFromBeatmap(string sampleName) + { + setupSkins(sampleName, sampleName); + + createTestWithBeatmap("controlpoint-beatmap-custom-sample.osu"); + + assertBeatmapLookup(sampleName); + } + + /// + /// Tests that a hitobject's custom sample overrides the control point's. + /// + [Test] + public void TestHitObjectCustomSampleOverride() + { + const string expected_sample = "normal-hitnormal3"; + + setupSkins(expected_sample, expected_sample); + + createTestWithBeatmap("hitobject-beatmap-custom-sample-override.osu"); + + assertBeatmapLookup(expected_sample); + } + + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => currentTestBeatmap; + + protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null) + => new TestWorkingBeatmap(beatmapInfo, beatmapSkinResourceStore, beatmap, storyboard, Clock, Audio); + + private IBeatmap currentTestBeatmap; + + private void createTestWithBeatmap(string filename) + { + CreateTest(() => + { + AddStep("clear performed lookups", () => + { + userSkinResourceStore.PerformedLookups.Clear(); + beatmapSkinResourceStore.PerformedLookups.Clear(); + }); + + AddStep($"load {filename}", () => + { + using (var reader = new LineBufferedReader(TestResources.OpenResource($"SampleLookups/{filename}"))) + currentTestBeatmap = Decoder.GetDecoder(reader).Decode(reader); + }); + }); + } + + private void setupSkins(string beatmapFile, string userFile) + { + AddStep("setup skins", () => + { + userSkinInfo.Files = new List + { + new SkinFileInfo + { + Filename = userFile, + FileInfo = new IO.FileInfo { Hash = userFile } + } + }; + + beatmapInfo.BeatmapSet.Files = new List + { + new BeatmapSetFileInfo + { + Filename = beatmapFile, + FileInfo = new IO.FileInfo { Hash = beatmapFile } + } + }; + + // Need to refresh the cached skin source to refresh the skin resource store. + dependencies.SkinSource = new SkinProvidingContainer(new LegacySkin(userSkinInfo, userSkinResourceStore, dependencies.Get())); + }); + } + + private void assertBeatmapLookup(string name) => AddAssert($"\"{name}\" looked up from beatmap skin", + () => !userSkinResourceStore.PerformedLookups.Contains(name) && beatmapSkinResourceStore.PerformedLookups.Contains(name)); + + private void assertUserLookup(string name) => AddAssert($"\"{name}\" looked up from user skin", + () => !beatmapSkinResourceStore.PerformedLookups.Contains(name) && userSkinResourceStore.PerformedLookups.Contains(name)); + + private class SkinSourceDependencyContainer : IReadOnlyDependencyContainer + { + public ISkinSource SkinSource; + + private readonly IReadOnlyDependencyContainer fallback; + + public SkinSourceDependencyContainer(IReadOnlyDependencyContainer fallback) + { + this.fallback = fallback; + } + + public object Get(Type type) + { + if (type == typeof(ISkinSource)) + return SkinSource; + + return fallback.Get(type); + } + + public object Get(Type type, CacheInfo info) + { + if (type == typeof(ISkinSource)) + return SkinSource; + + return fallback.Get(type); + } + + public void Inject(T instance) where T : class + { + // Never used directly + } + } + + private class TestResourceStore : IResourceStore + { + public readonly List PerformedLookups = new List(); + + public byte[] Get(string name) + { + markLookup(name); + return Array.Empty(); + } + + public Task GetAsync(string name) + { + markLookup(name); + return Task.FromResult(Array.Empty()); + } + + public Stream GetStream(string name) + { + markLookup(name); + return new MemoryStream(); + } + + private void markLookup(string name) => PerformedLookups.Add(name.Substring(name.LastIndexOf('/') + 1)); + + public IEnumerable GetAvailableResources() => Enumerable.Empty(); + + public void Dispose() + { + } + } + + private class TestWorkingBeatmap : ClockBackedTestWorkingBeatmap + { + private readonly BeatmapInfo skinBeatmapInfo; + private readonly IResourceStore resourceStore; + + public TestWorkingBeatmap(BeatmapInfo skinBeatmapInfo, IResourceStore resourceStore, IBeatmap beatmap, Storyboard storyboard, IFrameBasedClock referenceClock, AudioManager audio, + double length = 60000) + : base(beatmap, storyboard, referenceClock, audio, length) + { + this.skinBeatmapInfo = skinBeatmapInfo; + this.resourceStore = resourceStore; + } + + protected override ISkin GetSkin() => new LegacyBeatmapSkin(skinBeatmapInfo, resourceStore, AudioManager); + } + } +} diff --git a/osu.Game.Tests/Resources/SampleLookups/controlpoint-beatmap-custom-sample.osu b/osu.Game.Tests/Resources/SampleLookups/controlpoint-beatmap-custom-sample.osu new file mode 100644 index 0000000000..91dbc6a60e --- /dev/null +++ b/osu.Game.Tests/Resources/SampleLookups/controlpoint-beatmap-custom-sample.osu @@ -0,0 +1,7 @@ +osu file format v14 + +[TimingPoints] +0,300,4,0,2,100,1,0 + +[HitObjects] +444,320,1000,5,0,0:0:0:0: \ No newline at end of file diff --git a/osu.Game.Tests/Resources/SampleLookups/controlpoint-beatmap-sample.osu b/osu.Game.Tests/Resources/SampleLookups/controlpoint-beatmap-sample.osu new file mode 100644 index 0000000000..3274820100 --- /dev/null +++ b/osu.Game.Tests/Resources/SampleLookups/controlpoint-beatmap-sample.osu @@ -0,0 +1,7 @@ +osu file format v14 + +[TimingPoints] +0,300,4,0,1,100,1,0 + +[HitObjects] +444,320,1000,5,0,0:0:0:0: \ No newline at end of file diff --git a/osu.Game.Tests/Resources/SampleLookups/controlpoint-skin-sample.osu b/osu.Game.Tests/Resources/SampleLookups/controlpoint-skin-sample.osu new file mode 100644 index 0000000000..c53ec465fb --- /dev/null +++ b/osu.Game.Tests/Resources/SampleLookups/controlpoint-skin-sample.osu @@ -0,0 +1,7 @@ +osu file format v14 + +[TimingPoints] +0,300,4,0,0,100,1,0 + +[HitObjects] +444,320,1000,5,0,0:0:0:0: \ No newline at end of file diff --git a/osu.Game.Tests/Resources/SampleLookups/file-beatmap-sample.osu b/osu.Game.Tests/Resources/SampleLookups/file-beatmap-sample.osu new file mode 100644 index 0000000000..65b5ea8707 --- /dev/null +++ b/osu.Game.Tests/Resources/SampleLookups/file-beatmap-sample.osu @@ -0,0 +1,4 @@ +osu file format v14 + +[HitObjects] +255,193,2170,1,0,0:0:0:0:hit_1.wav diff --git a/osu.Game.Tests/Resources/SampleLookups/hitobject-beatmap-custom-sample-override.osu b/osu.Game.Tests/Resources/SampleLookups/hitobject-beatmap-custom-sample-override.osu new file mode 100644 index 0000000000..13dc2faab1 --- /dev/null +++ b/osu.Game.Tests/Resources/SampleLookups/hitobject-beatmap-custom-sample-override.osu @@ -0,0 +1,7 @@ +osu file format v14 + +[TimingPoints] +0,300,4,0,2,100,1,0 + +[HitObjects] +444,320,1000,5,0,0:0:3:0: \ No newline at end of file diff --git a/osu.Game.Tests/Resources/SampleLookups/hitobject-beatmap-custom-sample.osu b/osu.Game.Tests/Resources/SampleLookups/hitobject-beatmap-custom-sample.osu new file mode 100644 index 0000000000..4ab672dbb0 --- /dev/null +++ b/osu.Game.Tests/Resources/SampleLookups/hitobject-beatmap-custom-sample.osu @@ -0,0 +1,4 @@ +osu file format v14 + +[HitObjects] +444,320,1000,5,0,0:0:2:0: \ No newline at end of file diff --git a/osu.Game.Tests/Resources/SampleLookups/hitobject-beatmap-sample.osu b/osu.Game.Tests/Resources/SampleLookups/hitobject-beatmap-sample.osu new file mode 100644 index 0000000000..33bc34949a --- /dev/null +++ b/osu.Game.Tests/Resources/SampleLookups/hitobject-beatmap-sample.osu @@ -0,0 +1,4 @@ +osu file format v14 + +[HitObjects] +444,320,1000,5,0,0:0:1:0: \ No newline at end of file diff --git a/osu.Game.Tests/Resources/SampleLookups/hitobject-skin-sample.osu b/osu.Game.Tests/Resources/SampleLookups/hitobject-skin-sample.osu new file mode 100644 index 0000000000..47f5b44c90 --- /dev/null +++ b/osu.Game.Tests/Resources/SampleLookups/hitobject-skin-sample.osu @@ -0,0 +1,4 @@ +osu file format v14 + +[HitObjects] +444,320,1000,5,0,0:0:0:0: \ No newline at end of file From 44981431c5470c457d1708ad9227e4ec1dd60566 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 14 Apr 2020 21:33:32 +0900 Subject: [PATCH 0225/1117] Remove suffix hackery --- .../Objects/Legacy/ConvertHitObjectParser.cs | 16 +--------------- osu.Game/Skinning/LegacyBeatmapSkin.cs | 5 +++-- 2 files changed, 4 insertions(+), 17 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 95cbf3ab40..9a60a0a75c 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -420,24 +420,10 @@ namespace osu.Game.Rulesets.Objects.Legacy { customSampleBank = value; - // A 0 custom sample bank should cause LegacyBeatmapSkin to always fall back to the user skin. This is done by giving a null suffix. - if (value > 0) + if (value >= 2) Suffix = value.ToString(); } } - - public override IEnumerable LookupNames - { - get - { - // The lookup should only contain the suffix for custom sample bank 2 and beyond. - // For custom sample bank 1 and 0, the lookup should not contain the suffix as only the lookup source (beatmap or user skin) is changed. - if (CustomSampleBank >= 2) - yield return $"{Bank}-{Name}{Suffix}"; - - yield return $"{Bank}-{Name}"; - } - } } private class FileHitSampleInfo : LegacyHitSampleInfo diff --git a/osu.Game/Skinning/LegacyBeatmapSkin.cs b/osu.Game/Skinning/LegacyBeatmapSkin.cs index c4636f46f5..21533e58cd 100644 --- a/osu.Game/Skinning/LegacyBeatmapSkin.cs +++ b/osu.Game/Skinning/LegacyBeatmapSkin.cs @@ -7,6 +7,7 @@ using osu.Framework.Bindables; using osu.Framework.IO.Stores; using osu.Game.Audio; using osu.Game.Beatmaps; +using osu.Game.Rulesets.Objects.Legacy; namespace osu.Game.Skinning { @@ -37,9 +38,9 @@ namespace osu.Game.Skinning public override SampleChannel GetSample(ISampleInfo sampleInfo) { - if (sampleInfo is HitSampleInfo hsi && string.IsNullOrEmpty(hsi.Suffix)) + if (sampleInfo is ConvertHitObjectParser.LegacyHitSampleInfo legacy && legacy.CustomSampleBank == 0) { - // When no custom sample set is provided, always fall-back to the default samples. + // When no custom sample bank is provided, always fall-back to the default samples. return null; } From 64d44dedcd643cef2e971a968154e0fd17e2a6b2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 14 Apr 2020 22:39:51 +0900 Subject: [PATCH 0226/1117] Make testscene headless --- osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs b/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs index f80ea3ae88..a8bd902117 100644 --- a/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs +++ b/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs @@ -10,6 +10,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.IO.Stores; +using osu.Framework.Testing; using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Formats; @@ -24,6 +25,7 @@ using osu.Game.Users; namespace osu.Game.Tests.Gameplay { + [HeadlessTest] public class TestSceneHitObjectSamples : PlayerTestScene { private readonly SkinInfo userSkinInfo = new SkinInfo(); From 10486a0ad2b2aa1809d1d4f18d532cf545ebcb38 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 14 Apr 2020 23:10:14 +0900 Subject: [PATCH 0227/1117] Fix potential dependency-related issues --- osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs b/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs index a8bd902117..366437a771 100644 --- a/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs +++ b/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs @@ -253,7 +253,7 @@ namespace osu.Game.Tests.Gameplay }; // Need to refresh the cached skin source to refresh the skin resource store. - dependencies.SkinSource = new SkinProvidingContainer(new LegacySkin(userSkinInfo, userSkinResourceStore, dependencies.Get())); + dependencies.SkinSource = new SkinProvidingContainer(new LegacySkin(userSkinInfo, userSkinResourceStore, Audio)); }); } @@ -287,7 +287,7 @@ namespace osu.Game.Tests.Gameplay if (type == typeof(ISkinSource)) return SkinSource; - return fallback.Get(type); + return fallback.Get(type, info); } public void Inject(T instance) where T : class From d47e414fb142e7aa504814494d496a3d08528a46 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 15 Apr 2020 12:35:43 +0900 Subject: [PATCH 0228/1117] Apply review feedback (unroll inner loop / xml fixes) --- .../Replays/ManiaReplayFrame.cs | 35 +++++++++---------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs index 0059a78a44..da4b0c943c 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs @@ -74,22 +74,20 @@ namespace osu.Game.Rulesets.Mania.Replays // the index in lazer, which doesn't include special keys. int nonSpecialKeyIndex = action - ManiaAction.Key1; + // the index inclusive of special keys. int overallIndex = 0; // iterate to find the index including special keys. - while (true) + for (; overallIndex < maniaBeatmap.TotalColumns; overallIndex++) { - if (!isColumnAtIndexSpecial(maniaBeatmap, overallIndex)) - { - // found a non-special column we could use. - if (nonSpecialKeyIndex == 0) - break; - - // found a non-special column but not ours. - nonSpecialKeyIndex--; - } - - overallIndex++; + // skip over special columns. + if (isColumnAtIndexSpecial(maniaBeatmap, overallIndex)) + continue; + // found a non-special column to use. + if (nonSpecialKeyIndex == 0) + break; + // found a non-special column but not ours. + nonSpecialKeyIndex--; } keys |= 1 << overallIndex; @@ -127,21 +125,20 @@ namespace osu.Game.Rulesets.Mania.Replays /// /// The beatmap. /// The overall index to check. - /// private bool isColumnAtIndexSpecial(ManiaBeatmap beatmap, int index) { foreach (var stage in beatmap.Stages) { - for (int stageIndex = 0; stageIndex < stage.Columns; stageIndex++) + if (index >= stage.Columns) { - if (index == 0) - return stage.IsSpecialColumn(stageIndex); - - index--; + index -= stage.Columns; + continue; } + + return stage.IsSpecialColumn(index); } - return false; + throw new ArgumentException("Column index is too high.", nameof(index)); } } } From f4b5a17b650264a9b0fda00c1a59c94cfde58fec Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 15 Apr 2020 07:00:38 +0300 Subject: [PATCH 0229/1117] Fix typo in DrawableTaikoHitObject --- .../Objects/Drawables/DrawableTaikoHitObject.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs index 397888bb11..2f90f3b96c 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs @@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables /// /// Moves to a layer proxied above the playfield. - /// Does nothing is content is already proxied. + /// Does nothing if content is already proxied. /// protected void ProxyContent() { From e534d59c807ecd1350e2ced30c4595e49fc6af4a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 15 Apr 2020 13:08:15 +0900 Subject: [PATCH 0230/1117] Use another argument exception --- osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs index da4b0c943c..dbab54d1d0 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs @@ -117,7 +117,7 @@ namespace osu.Game.Rulesets.Mania.Replays } } - throw new InvalidOperationException("Special key index too high"); + throw new ArgumentException("Special key index is too high.", nameof(specialOffset)); } /// From 72707a9973f41f1961bd030c66d84a7627e4be81 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 15 Apr 2020 13:54:23 +0900 Subject: [PATCH 0231/1117] Fix OS-dependent substring --- osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs b/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs index 366437a771..f611f2717e 100644 --- a/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs +++ b/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs @@ -318,7 +318,7 @@ namespace osu.Game.Tests.Gameplay return new MemoryStream(); } - private void markLookup(string name) => PerformedLookups.Add(name.Substring(name.LastIndexOf('/') + 1)); + private void markLookup(string name) => PerformedLookups.Add(name.Substring(name.LastIndexOf(Path.DirectorySeparatorChar) + 1)); public IEnumerable GetAvailableResources() => Enumerable.Empty(); From 019e777d7da8022678efa7e4a60026d6d6440be7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 15 Apr 2020 16:01:49 +0900 Subject: [PATCH 0232/1117] Move taiko skinning tests to own namespace --- .../{ => Skinning}/TaikoSkinnableTestScene.cs | 2 +- .../{ => Skinning}/TestSceneDrawableHit.cs | 2 +- .../{ => Skinning}/TestSceneInputDrum.cs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) rename osu.Game.Rulesets.Taiko.Tests/{ => Skinning}/TaikoSkinnableTestScene.cs (92%) rename osu.Game.Rulesets.Taiko.Tests/{ => Skinning}/TestSceneDrawableHit.cs (97%) rename osu.Game.Rulesets.Taiko.Tests/{ => Skinning}/TestSceneInputDrum.cs (96%) diff --git a/osu.Game.Rulesets.Taiko.Tests/TaikoSkinnableTestScene.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TaikoSkinnableTestScene.cs similarity index 92% rename from osu.Game.Rulesets.Taiko.Tests/TaikoSkinnableTestScene.cs rename to osu.Game.Rulesets.Taiko.Tests/Skinning/TaikoSkinnableTestScene.cs index 6db2a6907f..161154b1a7 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TaikoSkinnableTestScene.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TaikoSkinnableTestScene.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; using osu.Game.Rulesets.Taiko.Skinning; using osu.Game.Tests.Visual; -namespace osu.Game.Rulesets.Taiko.Tests +namespace osu.Game.Rulesets.Taiko.Tests.Skinning { public abstract class TaikoSkinnableTestScene : SkinnableTestScene { diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneDrawableHit.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableHit.cs similarity index 97% rename from osu.Game.Rulesets.Taiko.Tests/TestSceneDrawableHit.cs rename to osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableHit.cs index 301295253d..a3832b010c 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneDrawableHit.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableHit.cs @@ -13,7 +13,7 @@ using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Objects.Drawables; using osu.Game.Rulesets.Taiko.Skinning; -namespace osu.Game.Rulesets.Taiko.Tests +namespace osu.Game.Rulesets.Taiko.Tests.Skinning { [TestFixture] public class TestSceneDrawableHit : TaikoSkinnableTestScene diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneInputDrum.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneInputDrum.cs similarity index 96% rename from osu.Game.Rulesets.Taiko.Tests/TestSceneInputDrum.cs rename to osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneInputDrum.cs index 1928e9f66f..412027ca61 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneInputDrum.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneInputDrum.cs @@ -6,14 +6,14 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; -using osuTK; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Taiko.Skinning; using osu.Game.Rulesets.Taiko.UI; +using osuTK; -namespace osu.Game.Rulesets.Taiko.Tests +namespace osu.Game.Rulesets.Taiko.Tests.Skinning { [TestFixture] public class TestSceneInputDrum : TaikoSkinnableTestScene From 102c1d9095d4189731f6d7ac547abd0c0e5b7527 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 15 Apr 2020 15:50:19 +0900 Subject: [PATCH 0233/1117] Add disabled state to menu items --- .../Visual/UserInterface/TestSceneOsuMenu.cs | 91 +++++++++++++++++++ .../UserInterface/DrawableOsuMenuItem.cs | 26 +++++- .../Graphics/UserInterface/OsuMenuItem.cs | 6 ++ 3 files changed, 118 insertions(+), 5 deletions(-) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneOsuMenu.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuMenu.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuMenu.cs new file mode 100644 index 0000000000..cdda1969ca --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuMenu.cs @@ -0,0 +1,91 @@ +// 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.Graphics; +using osu.Framework.Testing; +using osu.Game.Graphics.UserInterface; +using osuTK.Input; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public class TestSceneOsuMenu : OsuManualInputManagerTestScene + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(OsuMenu), + typeof(DrawableOsuMenuItem) + }; + + private OsuMenu menu; + private bool actionPeformed; + + [SetUp] + public void Setup() => Schedule(() => + { + actionPeformed = false; + + Child = menu = new OsuMenu(Direction.Vertical, true) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Items = new[] + { + new OsuMenuItem("standard", MenuItemType.Standard, performAction), + new OsuMenuItem("highlighted", MenuItemType.Highlighted, performAction), + new OsuMenuItem("destructive", MenuItemType.Destructive, performAction), + } + }; + }); + + [Test] + public void TestClickEnabledMenuItem() + { + AddStep("move to first menu item", () => InputManager.MoveMouseTo(menu.ChildrenOfType().First())); + AddStep("click", () => InputManager.Click(MouseButton.Left)); + + AddAssert("action performed", () => actionPeformed); + } + + [Test] + public void TestDisableMenuItemsAndClick() + { + AddStep("disable menu items", () => + { + foreach (var item in menu.Items) + ((OsuMenuItem)item).Enabled.Value = false; + }); + + AddStep("move to first menu item", () => InputManager.MoveMouseTo(menu.ChildrenOfType().First())); + AddStep("click", () => InputManager.Click(MouseButton.Left)); + + AddAssert("action not performed", () => !actionPeformed); + } + + [Test] + public void TestEnableMenuItemsAndClick() + { + AddStep("disable menu items", () => + { + foreach (var item in menu.Items) + ((OsuMenuItem)item).Enabled.Value = false; + }); + + AddStep("enable menu items", () => + { + foreach (var item in menu.Items) + ((OsuMenuItem)item).Enabled.Value = true; + }); + + AddStep("move to first menu item", () => InputManager.MoveMouseTo(menu.ChildrenOfType().First())); + AddStep("click", () => InputManager.Click(MouseButton.Left)); + + AddAssert("action performed", () => actionPeformed); + } + + private void performAction() => actionPeformed = true; + } +} diff --git a/osu.Game/Graphics/UserInterface/DrawableOsuMenuItem.cs b/osu.Game/Graphics/UserInterface/DrawableOsuMenuItem.cs index a3ca851341..abaae7b43c 100644 --- a/osu.Game/Graphics/UserInterface/DrawableOsuMenuItem.cs +++ b/osu.Game/Graphics/UserInterface/DrawableOsuMenuItem.cs @@ -42,6 +42,8 @@ namespace osu.Game.Graphics.UserInterface BackgroundColourHover = Color4Extensions.FromHex(@"172023"); updateTextColour(); + + Item.Action.BindDisabledChanged(_ => updateState(), true); } private void updateTextColour() @@ -65,19 +67,33 @@ namespace osu.Game.Graphics.UserInterface protected override bool OnHover(HoverEvent e) { - sampleHover.Play(); - text.BoldText.FadeIn(transition_length, Easing.OutQuint); - text.NormalText.FadeOut(transition_length, Easing.OutQuint); + updateState(); return base.OnHover(e); } protected override void OnHoverLost(HoverLostEvent e) { - text.BoldText.FadeOut(transition_length, Easing.OutQuint); - text.NormalText.FadeIn(transition_length, Easing.OutQuint); + updateState(); base.OnHoverLost(e); } + private void updateState() + { + Alpha = Item.Action.Disabled ? 0.2f : 1; + + if (IsHovered && !Item.Action.Disabled) + { + sampleHover.Play(); + text.BoldText.FadeIn(transition_length, Easing.OutQuint); + text.NormalText.FadeOut(transition_length, Easing.OutQuint); + } + else + { + text.BoldText.FadeOut(transition_length, Easing.OutQuint); + text.NormalText.FadeIn(transition_length, Easing.OutQuint); + } + } + protected override bool OnClick(ClickEvent e) { sampleClick.Play(); diff --git a/osu.Game/Graphics/UserInterface/OsuMenuItem.cs b/osu.Game/Graphics/UserInterface/OsuMenuItem.cs index 0fe41937ce..36122ca0b2 100644 --- a/osu.Game/Graphics/UserInterface/OsuMenuItem.cs +++ b/osu.Game/Graphics/UserInterface/OsuMenuItem.cs @@ -2,12 +2,15 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Bindables; using osu.Framework.Graphics.UserInterface; namespace osu.Game.Graphics.UserInterface { public class OsuMenuItem : MenuItem { + public readonly Bindable Enabled = new Bindable(true); + public readonly MenuItemType Type; public OsuMenuItem(string text, MenuItemType type = MenuItemType.Standard) @@ -19,6 +22,9 @@ namespace osu.Game.Graphics.UserInterface : base(text, action) { Type = type; + + Enabled.BindValueChanged(enabled => Action.Disabled = !enabled.NewValue); + Action.BindDisabledChanged(disabled => Enabled.Value = !disabled); } } } From e8c955ed9b0a253ba2bfa6bd1e1af737a4e34440 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 15 Apr 2020 15:50:43 +0900 Subject: [PATCH 0234/1117] Add CanUndo/CanRedo bindables --- .../Editor/EditorChangeHandlerTest.cs | 25 +++++++++++-------- osu.Game/Screens/Edit/EditorChangeHandler.cs | 17 ++++++++++--- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tests/Editor/EditorChangeHandlerTest.cs b/osu.Game.Tests/Editor/EditorChangeHandlerTest.cs index ef16976130..9613f250c4 100644 --- a/osu.Game.Tests/Editor/EditorChangeHandlerTest.cs +++ b/osu.Game.Tests/Editor/EditorChangeHandlerTest.cs @@ -15,15 +15,18 @@ namespace osu.Game.Tests.Editor { var handler = new EditorChangeHandler(new EditorBeatmap(new Beatmap())); - Assert.That(handler.HasUndoState, Is.False); + Assert.That(handler.CanUndo.Value, Is.False); + Assert.That(handler.CanRedo.Value, Is.False); handler.SaveState(); - Assert.That(handler.HasUndoState, Is.True); + Assert.That(handler.CanUndo.Value, Is.True); + Assert.That(handler.CanRedo.Value, Is.False); handler.RestoreState(-1); - Assert.That(handler.HasUndoState, Is.False); + Assert.That(handler.CanUndo.Value, Is.False); + Assert.That(handler.CanRedo.Value, Is.True); } [Test] @@ -31,20 +34,20 @@ namespace osu.Game.Tests.Editor { var handler = new EditorChangeHandler(new EditorBeatmap(new Beatmap())); - Assert.That(handler.HasUndoState, Is.False); + Assert.That(handler.CanUndo.Value, Is.False); for (int i = 0; i < EditorChangeHandler.MAX_SAVED_STATES; i++) handler.SaveState(); - Assert.That(handler.HasUndoState, Is.True); + Assert.That(handler.CanUndo.Value, Is.True); for (int i = 0; i < EditorChangeHandler.MAX_SAVED_STATES; i++) { - Assert.That(handler.HasUndoState, Is.True); + Assert.That(handler.CanUndo.Value, Is.True); handler.RestoreState(-1); } - Assert.That(handler.HasUndoState, Is.False); + Assert.That(handler.CanUndo.Value, Is.False); } [Test] @@ -52,20 +55,20 @@ namespace osu.Game.Tests.Editor { var handler = new EditorChangeHandler(new EditorBeatmap(new Beatmap())); - Assert.That(handler.HasUndoState, Is.False); + Assert.That(handler.CanUndo.Value, Is.False); for (int i = 0; i < EditorChangeHandler.MAX_SAVED_STATES * 2; i++) handler.SaveState(); - Assert.That(handler.HasUndoState, Is.True); + Assert.That(handler.CanUndo.Value, Is.True); for (int i = 0; i < EditorChangeHandler.MAX_SAVED_STATES; i++) { - Assert.That(handler.HasUndoState, Is.True); + Assert.That(handler.CanUndo.Value, Is.True); handler.RestoreState(-1); } - Assert.That(handler.HasUndoState, Is.False); + Assert.That(handler.CanUndo.Value, Is.False); } } } diff --git a/osu.Game/Screens/Edit/EditorChangeHandler.cs b/osu.Game/Screens/Edit/EditorChangeHandler.cs index a8204715cd..1553c2d2ef 100644 --- a/osu.Game/Screens/Edit/EditorChangeHandler.cs +++ b/osu.Game/Screens/Edit/EditorChangeHandler.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Text; +using osu.Framework.Bindables; using osu.Game.Beatmaps.Formats; using osu.Game.Rulesets.Objects; @@ -15,8 +16,10 @@ namespace osu.Game.Screens.Edit /// public class EditorChangeHandler : IEditorChangeHandler { - private readonly LegacyEditorBeatmapPatcher patcher; + public readonly Bindable CanUndo = new Bindable(); + public readonly Bindable CanRedo = new Bindable(); + private readonly LegacyEditorBeatmapPatcher patcher; private readonly List savedStates = new List(); private int currentState = -1; @@ -45,8 +48,6 @@ namespace osu.Game.Screens.Edit SaveState(); } - public bool HasUndoState => currentState > 0; - private void hitObjectAdded(HitObject obj) => SaveState(); private void hitObjectRemoved(HitObject obj) => SaveState(); @@ -90,6 +91,8 @@ namespace osu.Game.Screens.Edit } currentState = savedStates.Count - 1; + + updateBindables(); } /// @@ -114,6 +117,14 @@ namespace osu.Game.Screens.Edit currentState = newState; isRestoring = false; + + updateBindables(); + } + + private void updateBindables() + { + CanUndo.Value = savedStates.Count > 0 && currentState > 0; + CanRedo.Value = currentState < savedStates.Count - 1; } } } From ce21cfbb035b16bb42b473614edbe372b5ace04b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 15 Apr 2020 16:17:34 +0900 Subject: [PATCH 0235/1117] Use bindables in menu items --- osu.Game/Screens/Edit/Editor.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 14a227eb07..ad17498d93 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -155,8 +155,8 @@ namespace osu.Game.Screens.Edit { Items = new[] { - new EditorMenuItem("Undo", MenuItemType.Standard, undo), - new EditorMenuItem("Redo", MenuItemType.Standard, redo) + new EditorMenuItem("Undo", MenuItemType.Standard, undo) { Enabled = { BindTarget = changeHandler.CanUndo } }, + new EditorMenuItem("Redo", MenuItemType.Standard, redo) { Enabled = { BindTarget = changeHandler.CanRedo } } } } } From 18c28390ef6f441acd11acd318cffa057331fa4e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 15 Apr 2020 16:29:39 +0900 Subject: [PATCH 0236/1117] Setup drumroll testing --- .../Skinning/TestSceneDrawableDrumRoll.cs | 84 +++++++++++++++++++ .../Tests/Visual/ScrollingTestContainer.cs | 4 +- 2 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableDrumRoll.cs diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableDrumRoll.cs new file mode 100644 index 0000000000..388be5bbc4 --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableDrumRoll.cs @@ -0,0 +1,84 @@ +// 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.Allocation; +using osu.Framework.Graphics; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Taiko.Objects.Drawables; +using osu.Game.Rulesets.UI.Scrolling; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Taiko.Tests.Skinning +{ + [TestFixture] + public class TestSceneDrawableDrumRoll : TaikoSkinnableTestScene + { + public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] + { + typeof(DrawableDrumRoll), + typeof(DrawableDrumRollTick), + }).ToList(); + + [Cached(typeof(IScrollingInfo))] + private ScrollingTestContainer.TestScrollingInfo info = new ScrollingTestContainer.TestScrollingInfo + { + Direction = { Value = ScrollingDirection.Left }, + TimeRange = { Value = 5000 }, + }; + + [BackgroundDependencyLoader] + private void load() + { + AddStep("Drum roll", () => SetContents(() => + { + var hoc = new ScrollingHitObjectContainer(); + + hoc.Add(new DrawableDrumRoll(createDrumRollAtCurrentTime()) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Width = 500, + }); + + return hoc; + })); + + AddStep("Drum roll (strong)", () => SetContents(() => + { + var hoc = new ScrollingHitObjectContainer(); + + hoc.Add(new DrawableDrumRoll(createDrumRollAtCurrentTime(true)) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Width = 500, + }); + + return hoc; + })); + } + + private DrumRoll createDrumRollAtCurrentTime(bool strong = false) + { + var drumroll = new DrumRoll + { + IsStrong = strong, + StartTime = Time.Current + 1000, + Duration = 4000, + }; + + var cpi = new ControlPointInfo(); + cpi.Add(0, new TimingControlPoint { BeatLength = 500 }); + + drumroll.ApplyDefaults(cpi, new BeatmapDifficulty()); + + return drumroll; + } + } +} diff --git a/osu.Game/Tests/Visual/ScrollingTestContainer.cs b/osu.Game/Tests/Visual/ScrollingTestContainer.cs index 18326a78ad..3b741fcf1d 100644 --- a/osu.Game/Tests/Visual/ScrollingTestContainer.cs +++ b/osu.Game/Tests/Visual/ScrollingTestContainer.cs @@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual public void Flip() => scrollingInfo.Direction.Value = scrollingInfo.Direction.Value == ScrollingDirection.Up ? ScrollingDirection.Down : ScrollingDirection.Up; - private class TestScrollingInfo : IScrollingInfo + public class TestScrollingInfo : IScrollingInfo { public readonly Bindable Direction = new Bindable(); IBindable IScrollingInfo.Direction => Direction; @@ -54,7 +54,7 @@ namespace osu.Game.Tests.Visual IScrollAlgorithm IScrollingInfo.Algorithm => Algorithm; } - private class TestScrollAlgorithm : IScrollAlgorithm + public class TestScrollAlgorithm : IScrollAlgorithm { public readonly SortedList ControlPoints = new SortedList(); From eb165840cb4e202846dfbc11b2da997af6a814fd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 15 Apr 2020 16:54:50 +0900 Subject: [PATCH 0237/1117] Add remaining taiko hitobject skinnables and expose as SkinnableDrawable for safety --- .../Objects/Drawables/DrawableCentreHit.cs | 3 +-- .../Objects/Drawables/DrawableDrumRoll.cs | 27 +++++++++++++------ .../Objects/Drawables/DrawableDrumRollTick.cs | 8 +++--- .../Objects/Drawables/DrawableHit.cs | 2 +- .../Objects/Drawables/DrawableRimHit.cs | 3 +-- .../Objects/Drawables/DrawableSwell.cs | 16 ++++++----- .../Objects/Drawables/DrawableSwellTick.cs | 5 ++-- .../Drawables/DrawableTaikoHitObject.cs | 5 ++-- .../Objects/Drawables/Pieces/CirclePiece.cs | 3 ++- .../TaikoSkinComponents.cs | 5 +++- 10 files changed, 46 insertions(+), 31 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs index f3f4c59a62..a87da44415 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.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 osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; using osu.Game.Skinning; @@ -16,7 +15,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { } - protected override CompositeDrawable CreateMainPiece() => new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.CentreHit), + protected override SkinnableDrawable CreateMainPiece() => new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.CentreHit), _ => new CentreHitCirclePiece(), confineMode: ConfineMode.ScaleToFit); } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs index 0627eb95fd..5c3433cbf4 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs @@ -14,6 +14,7 @@ using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; +using osu.Game.Skinning; namespace osu.Game.Rulesets.Taiko.Objects.Drawables { @@ -29,25 +30,29 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables /// private int rollingHits; - private readonly Container tickContainer; + private Container tickContainer; private Color4 colourIdle; private Color4 colourEngaged; - private ElongatedCirclePiece elongatedPiece; - public DrawableDrumRoll(DrumRoll drumRoll) : base(drumRoll) { RelativeSizeAxes = Axes.Y; - elongatedPiece.Add(tickContainer = new Container { RelativeSizeAxes = Axes.Both }); } [BackgroundDependencyLoader] private void load(OsuColour colours) { - elongatedPiece.AccentColour = colourIdle = colours.YellowDark; + colourIdle = colours.YellowDark; colourEngaged = colours.YellowDarker; + + updateColour(); + + ((Container)MainPiece.Drawable).Add(tickContainer = new Container { RelativeSizeAxes = Axes.Both }); + + if (MainPiece.Drawable is IHasAccentColour accentMain) + accentMain.AccentColour = colourIdle; } protected override void LoadComplete() @@ -86,7 +91,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables return base.CreateNestedHitObject(hitObject); } - protected override CompositeDrawable CreateMainPiece() => elongatedPiece = new ElongatedCirclePiece(); + protected override SkinnableDrawable CreateMainPiece() => new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.DrumRollBody), + _ => new ElongatedCirclePiece()); public override bool OnPressed(TaikoAction action) => false; @@ -102,8 +108,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables rollingHits = Math.Clamp(rollingHits, 0, rolling_hits_for_engaged_colour); - Color4 newColour = Interpolation.ValueAt((float)rollingHits / rolling_hits_for_engaged_colour, colourIdle, colourEngaged, 0, 1); - (MainPiece as IHasAccentColour)?.FadeAccent(newColour, 100); + updateColour(); } protected override void CheckForResult(bool userTriggered, double timeOffset) @@ -151,5 +156,11 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables public override bool OnPressed(TaikoAction action) => false; } + + private void updateColour() + { + Color4 newColour = Interpolation.ValueAt((float)rollingHits / rolling_hits_for_engaged_colour, colourIdle, colourEngaged, 0, 1); + (MainPiece.Drawable as IHasAccentColour)?.FadeAccent(newColour, 100); + } } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs index fea3eea6a9..e11e019826 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs @@ -3,10 +3,10 @@ using System; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; +using osu.Game.Skinning; namespace osu.Game.Rulesets.Taiko.Objects.Drawables { @@ -20,10 +20,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables public override bool DisplayResult => false; - protected override CompositeDrawable CreateMainPiece() => new TickPiece - { - Filled = HitObject.FirstTick - }; + protected override SkinnableDrawable CreateMainPiece() => new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.DrumRollTick), + _ => new TickPiece()); protected override void CheckForResult(bool userTriggered, double timeOffset) { diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index 85dfc8d5e0..9333e5f144 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -116,7 +116,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables // If we're far enough away from the left stage, we should bring outselves in front of it ProxyContent(); - var flash = (MainPiece as CirclePiece)?.FlashBox; + var flash = (MainPiece.Drawable as CirclePiece)?.FlashBox; flash?.FadeTo(0.9f).FadeOut(300); const float gravity_time = 300; diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs index 463a8b746c..f767403c65 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.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 osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; using osu.Game.Skinning; @@ -16,7 +15,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { } - protected override CompositeDrawable CreateMainPiece() => new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.RimHit), + protected override SkinnableDrawable CreateMainPiece() => new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.RimHit), _ => new RimHitCirclePiece(), confineMode: ConfineMode.ScaleToFit); } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs index 3a2e44038f..32f7acadc8 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs @@ -14,6 +14,7 @@ using osu.Framework.Graphics.Shapes; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; +using osu.Game.Skinning; namespace osu.Game.Rulesets.Taiko.Objects.Drawables { @@ -114,12 +115,13 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables targetRing.BorderColour = colours.YellowDark.Opacity(0.25f); } - protected override CompositeDrawable CreateMainPiece() => new SwellCirclePiece - { - // to allow for rotation transform - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }; + protected override SkinnableDrawable CreateMainPiece() => new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.Swell), + _ => new SwellCirclePiece + { + // to allow for rotation transform + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }); protected override void LoadComplete() { @@ -184,7 +186,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables .Then() .FadeTo(completion / 8, 2000, Easing.OutQuint); - MainPiece.RotateTo((float)(completion * HitObject.Duration / 8), 4000, Easing.OutQuint); + MainPiece.Drawable.RotateTo((float)(completion * HitObject.Duration / 8), 4000, Easing.OutQuint); expandingRing.ScaleTo(1f + Math.Min(target_ring_scale - 1f, (target_ring_scale - 1f) * completion * 1.3f), 260, Easing.OutQuint); diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs index 5a954addfb..1685576f0d 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs @@ -2,9 +2,9 @@ // 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.Scoring; using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; +using osu.Game.Skinning; namespace osu.Game.Rulesets.Taiko.Objects.Drawables { @@ -31,6 +31,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables public override bool OnPressed(TaikoAction action) => false; - protected override CompositeDrawable CreateMainPiece() => new TickPiece(); + protected override SkinnableDrawable CreateMainPiece() => new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.DrumRollTick), + _ => new TickPiece()); } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs index 2f90f3b96c..1be04f1760 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs @@ -11,6 +11,7 @@ using System.Collections.Generic; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; using osu.Game.Rulesets.Objects; +using osu.Game.Skinning; namespace osu.Game.Rulesets.Taiko.Objects.Drawables { @@ -115,7 +116,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables public new TObject HitObject; protected readonly Vector2 BaseSize; - protected readonly CompositeDrawable MainPiece; + protected readonly SkinnableDrawable MainPiece; private readonly Container strongHitContainer; @@ -167,7 +168,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables // Normal and clap samples are handled by the drum protected override IEnumerable GetSamples() => HitObject.Samples.Where(s => s.Name != HitSampleInfo.HIT_NORMAL && s.Name != HitSampleInfo.HIT_CLAP); - protected abstract CompositeDrawable CreateMainPiece(); + protected abstract SkinnableDrawable CreateMainPiece(); /// /// Creates the handler for this 's . diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs index 6ca77e666d..b5471e6976 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs @@ -10,6 +10,7 @@ using osuTK.Graphics; using osu.Game.Beatmaps.ControlPoints; using osu.Framework.Audio.Track; using osu.Framework.Graphics.Effects; +using osu.Game.Graphics; using osu.Game.Graphics.Containers; namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces @@ -21,7 +22,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces /// for a usage example. /// /// - public abstract class CirclePiece : BeatSyncedContainer + public abstract class CirclePiece : BeatSyncedContainer, IHasAccentColour { public const float SYMBOL_SIZE = 0.45f; public const float SYMBOL_BORDER = 8; diff --git a/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs b/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs index babf21b6a9..156ea71c16 100644 --- a/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs +++ b/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs @@ -7,6 +7,9 @@ namespace osu.Game.Rulesets.Taiko { InputDrum, CentreHit, - RimHit + RimHit, + DrumRollBody, + DrumRollTick, + Swell } } From 45d88b70f8de3cf146a1e28e99d07dabf6d511ce Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 15 Apr 2020 17:50:37 +0900 Subject: [PATCH 0238/1117] Split out base logic from LegacyHit into LegacyCirclePiece --- .../Skinning/TestSceneDrawableHit.cs | 1 + .../Skinning/LegacyCirclePiece.cs | 96 +++++++++++++++++++ osu.Game.Rulesets.Taiko/Skinning/LegacyHit.cs | 69 +------------ 3 files changed, 99 insertions(+), 67 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko/Skinning/LegacyCirclePiece.cs diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableHit.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableHit.cs index a3832b010c..6d6da1fb5b 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableHit.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableHit.cs @@ -24,6 +24,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning typeof(DrawableCentreHit), typeof(DrawableRimHit), typeof(LegacyHit), + typeof(LegacyCirclePiece), }).ToList(); [BackgroundDependencyLoader] diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyCirclePiece.cs b/osu.Game.Rulesets.Taiko/Skinning/LegacyCirclePiece.cs new file mode 100644 index 0000000000..bfcf268c3d --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Skinning/LegacyCirclePiece.cs @@ -0,0 +1,96 @@ +// 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.Animations; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Taiko.Objects.Drawables; +using osu.Game.Skinning; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Taiko.Skinning +{ + public class LegacyCirclePiece : CompositeDrawable, IHasAccentColour + { + private Drawable backgroundLayer; + + public LegacyCirclePiece() + { + RelativeSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader] + private void load(ISkinSource skin, DrawableHitObject drawableHitObject) + { + Drawable getDrawableFor(string lookup) + { + const string normal_hit = "taikohit"; + const string big_hit = "taikobig"; + + string prefix = ((drawableHitObject as DrawableTaikoHitObject)?.HitObject.IsStrong ?? false) ? big_hit : normal_hit; + + return skin.GetAnimation($"{prefix}{lookup}", true, false) ?? + // fallback to regular size if "big" version doesn't exist. + skin.GetAnimation($"{normal_hit}{lookup}", true, false); + } + + // backgroundLayer is guaranteed to exist due to the pre-check in TaikoLegacySkinTransformer. + AddInternal(backgroundLayer = getDrawableFor("circle")); + + var foregroundLayer = getDrawableFor("circleoverlay"); + if (foregroundLayer != null) + AddInternal(foregroundLayer); + + // Animations in taiko skins are used in a custom way (>150 combo and animating in time with beat). + // For now just stop at first frame for sanity. + foreach (var c in InternalChildren) + { + (c as IFramedAnimation)?.Stop(); + + c.Anchor = Anchor.Centre; + c.Origin = Anchor.Centre; + } + } + + protected override void LoadComplete() + { + base.LoadComplete(); + updateAccentColour(); + } + + protected override void Update() + { + base.Update(); + + // Not all skins (including the default osu-stable) have similar sizes for "hitcircle" and "hitcircleoverlay". + // This ensures they are scaled relative to each other but also match the expected DrawableHit size. + foreach (var c in InternalChildren) + c.Scale = new Vector2(DrawHeight / 128); + } + + private Color4 accentColour; + + public Color4 AccentColour + { + get => accentColour; + set + { + if (value == accentColour) + return; + + accentColour = value; + if (IsLoaded) + updateAccentColour(); + } + } + + private void updateAccentColour() + { + backgroundLayer.Colour = accentColour; + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyHit.cs b/osu.Game.Rulesets.Taiko/Skinning/LegacyHit.cs index 80bf97936d..656728f6e4 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/LegacyHit.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/LegacyHit.cs @@ -2,90 +2,25 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Animations; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Taiko.Objects.Drawables; -using osu.Game.Skinning; -using osuTK; using osuTK.Graphics; namespace osu.Game.Rulesets.Taiko.Skinning { - public class LegacyHit : CompositeDrawable, IHasAccentColour + public class LegacyHit : LegacyCirclePiece { private readonly TaikoSkinComponents component; - private Drawable backgroundLayer; - public LegacyHit(TaikoSkinComponents component) { this.component = component; - - RelativeSizeAxes = Axes.Both; } [BackgroundDependencyLoader] - private void load(ISkinSource skin, DrawableHitObject drawableHitObject) + private void load() { - Drawable getDrawableFor(string lookup) - { - const string normal_hit = "taikohit"; - const string big_hit = "taikobig"; - - string prefix = ((drawableHitObject as DrawableTaikoHitObject)?.HitObject.IsStrong ?? false) ? big_hit : normal_hit; - - return skin.GetAnimation($"{prefix}{lookup}", true, false) ?? - // fallback to regular size if "big" version doesn't exist. - skin.GetAnimation($"{normal_hit}{lookup}", true, false); - } - - // backgroundLayer is guaranteed to exist due to the pre-check in TaikoLegacySkinTransformer. - AddInternal(backgroundLayer = getDrawableFor("circle")); - - var foregroundLayer = getDrawableFor("circleoverlay"); - if (foregroundLayer != null) - AddInternal(foregroundLayer); - - // Animations in taiko skins are used in a custom way (>150 combo and animating in time with beat). - // For now just stop at first frame for sanity. - foreach (var c in InternalChildren) - { - (c as IFramedAnimation)?.Stop(); - - c.Anchor = Anchor.Centre; - c.Origin = Anchor.Centre; - } - AccentColour = component == TaikoSkinComponents.CentreHit ? new Color4(235, 69, 44, 255) : new Color4(67, 142, 172, 255); } - - protected override void Update() - { - base.Update(); - - // Not all skins (including the default osu-stable) have similar sizes for "hitcircle" and "hitcircleoverlay". - // This ensures they are scaled relative to each other but also match the expected DrawableHit size. - foreach (var c in InternalChildren) - c.Scale = new Vector2(DrawWidth / 128); - } - - private Color4 accentColour; - - public Color4 AccentColour - { - get => accentColour; - set - { - if (value == accentColour) - return; - - backgroundLayer.Colour = accentColour = value; - } - } } } From 313741799468b34fb5abb903b9513b9bbafbe4a0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 15 Apr 2020 17:50:57 +0900 Subject: [PATCH 0239/1117] Add drumroll skinning --- .../Skinning/TestSceneDrawableDrumRoll.cs | 2 + .../Skinning/LegacyDrumRoll.cs | 110 ++++++++++++++++++ .../Skinning/TaikoLegacySkinTransformer.cs | 6 + 3 files changed, 118 insertions(+) create mode 100644 osu.Game.Rulesets.Taiko/Skinning/LegacyDrumRoll.cs diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableDrumRoll.cs index 388be5bbc4..554894bf68 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableDrumRoll.cs @@ -11,6 +11,7 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Objects.Drawables; +using osu.Game.Rulesets.Taiko.Skinning; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Tests.Visual; @@ -23,6 +24,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning { typeof(DrawableDrumRoll), typeof(DrawableDrumRollTick), + typeof(LegacyDrumRoll), }).ToList(); [Cached(typeof(IScrollingInfo))] diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyDrumRoll.cs b/osu.Game.Rulesets.Taiko/Skinning/LegacyDrumRoll.cs new file mode 100644 index 0000000000..d3579fbbbd --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Skinning/LegacyDrumRoll.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 osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; +using osu.Game.Skinning; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Taiko.Skinning +{ + public class LegacyDrumRoll : Container, IHasAccentColour + { + protected override Container Content => content; + + private Container content; + + private LegacyCirclePiece headCircle; + + private Sprite body; + + private Sprite end; + + public LegacyDrumRoll() + { + RelativeSizeAxes = Axes.Y; + } + + [BackgroundDependencyLoader] + private void load(ISkinSource skin) + { + InternalChildren = new Drawable[] + { + content = new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + headCircle = new LegacyCirclePiece + { + Depth = float.MinValue, + RelativeSizeAxes = Axes.Y, + Anchor = Anchor.TopLeft, + Origin = Anchor.TopLeft, + }, + body = new Sprite + { + RelativeSizeAxes = Axes.Both, + Texture = skin.GetTexture("taiko-roll-middle"), + }, + end = new Sprite + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreLeft, + RelativeSizeAxes = Axes.Both, + Texture = skin.GetTexture("taiko-roll-end"), + FillMode = FillMode.Fit, + }, + }, + }, + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + updateAccentColour(); + } + + protected override void Update() + { + base.Update(); + + var padding = Content.DrawHeight * Content.Width / 2; + + Content.Padding = new MarginPadding + { + Left = padding, + Right = padding, + }; + + Width = Parent.DrawSize.X + DrawHeight; + } + + private Color4 accentColour; + + public Color4 AccentColour + { + get => accentColour; + set + { + if (value == accentColour) + return; + + accentColour = value; + if (IsLoaded) + updateAccentColour(); + } + } + + private void updateAccentColour() + { + headCircle.AccentColour = accentColour; + body.Colour = accentColour; + end.Colour = accentColour; + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs index 9cd625c35f..86e3945021 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs @@ -27,6 +27,12 @@ namespace osu.Game.Rulesets.Taiko.Skinning switch (taikoComponent.Component) { + case TaikoSkinComponents.DrumRollBody: + if (GetTexture("taiko-roll-middle") != null) + return new LegacyDrumRoll(); + + return null; + case TaikoSkinComponents.InputDrum: if (GetTexture("taiko-bar-left") != null) return new LegacyInputDrum(); From 07632cd1e53a9e297861b5f152f5e74e7d2552bb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 15 Apr 2020 18:44:12 +0900 Subject: [PATCH 0240/1117] Remove unnecessary container logic --- .../Objects/Drawables/DrawableDrumRoll.cs | 16 ++--- .../Drawables/Pieces/ElongatedCirclePiece.cs | 17 +++-- .../Skinning/LegacyDrumRoll.cs | 65 ++++++------------- 3 files changed, 35 insertions(+), 63 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs index 5c3433cbf4..0a6f462607 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables /// private int rollingHits; - private Container tickContainer; + private Container tickContainer; private Color4 colourIdle; private Color4 colourEngaged; @@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables updateColour(); - ((Container)MainPiece.Drawable).Add(tickContainer = new Container { RelativeSizeAxes = Axes.Both }); + Content.Add(tickContainer = new Container { RelativeSizeAxes = Axes.Both }); if (MainPiece.Drawable is IHasAccentColour accentMain) accentMain.AccentColour = colourIdle; @@ -139,6 +139,12 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables protected override DrawableStrongNestedHit CreateStrongHit(StrongHitObject hitObject) => new StrongNestedHit(hitObject, this); + private void updateColour() + { + Color4 newColour = Interpolation.ValueAt((float)rollingHits / rolling_hits_for_engaged_colour, colourIdle, colourEngaged, 0, 1); + (MainPiece.Drawable as IHasAccentColour)?.FadeAccent(newColour, 100); + } + private class StrongNestedHit : DrawableStrongNestedHit { public StrongNestedHit(StrongHitObject strong, DrawableDrumRoll drumRoll) @@ -156,11 +162,5 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables public override bool OnPressed(TaikoAction action) => false; } - - private void updateColour() - { - Color4 newColour = Interpolation.ValueAt((float)rollingHits / rolling_hits_for_engaged_colour, colourIdle, colourEngaged, 0, 1); - (MainPiece.Drawable as IHasAccentColour)?.FadeAccent(newColour, 100); - } } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/ElongatedCirclePiece.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/ElongatedCirclePiece.cs index 7e3272e42b..034ab6dd21 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/ElongatedCirclePiece.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/ElongatedCirclePiece.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 osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Game.Graphics; namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces { @@ -12,18 +14,15 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces RelativeSizeAxes = Axes.Y; } + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + AccentColour = colours.YellowDark; + } + protected override void Update() { base.Update(); - - var padding = Content.DrawHeight * Content.Width / 2; - - Content.Padding = new MarginPadding - { - Left = padding, - Right = padding, - }; - Width = Parent.DrawSize.X + DrawHeight; } } diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyDrumRoll.cs b/osu.Game.Rulesets.Taiko/Skinning/LegacyDrumRoll.cs index d3579fbbbd..8531f3cefd 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/LegacyDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/LegacyDrumRoll.cs @@ -11,12 +11,8 @@ using osuTK.Graphics; namespace osu.Game.Rulesets.Taiko.Skinning { - public class LegacyDrumRoll : Container, IHasAccentColour + public class LegacyDrumRoll : CompositeDrawable, IHasAccentColour { - protected override Container Content => content; - - private Container content; - private LegacyCirclePiece headCircle; private Sprite body; @@ -25,42 +21,34 @@ namespace osu.Game.Rulesets.Taiko.Skinning public LegacyDrumRoll() { - RelativeSizeAxes = Axes.Y; + RelativeSizeAxes = Axes.Both; } [BackgroundDependencyLoader] - private void load(ISkinSource skin) + private void load(ISkinSource skin, OsuColour colours) { InternalChildren = new Drawable[] { - content = new Container + end = new Sprite + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreLeft, + RelativeSizeAxes = Axes.Both, + Texture = skin.GetTexture("taiko-roll-end"), + FillMode = FillMode.Fit, + }, + body = new Sprite { RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - headCircle = new LegacyCirclePiece - { - Depth = float.MinValue, - RelativeSizeAxes = Axes.Y, - Anchor = Anchor.TopLeft, - Origin = Anchor.TopLeft, - }, - body = new Sprite - { - RelativeSizeAxes = Axes.Both, - Texture = skin.GetTexture("taiko-roll-middle"), - }, - end = new Sprite - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreLeft, - RelativeSizeAxes = Axes.Both, - Texture = skin.GetTexture("taiko-roll-end"), - FillMode = FillMode.Fit, - }, - }, + Texture = skin.GetTexture("taiko-roll-middle"), + }, + headCircle = new LegacyCirclePiece + { + RelativeSizeAxes = Axes.Y, }, }; + + AccentColour = colours.YellowDark; } protected override void LoadComplete() @@ -69,21 +57,6 @@ namespace osu.Game.Rulesets.Taiko.Skinning updateAccentColour(); } - protected override void Update() - { - base.Update(); - - var padding = Content.DrawHeight * Content.Width / 2; - - Content.Padding = new MarginPadding - { - Left = padding, - Right = padding, - }; - - Width = Parent.DrawSize.X + DrawHeight; - } - private Color4 accentColour; public Color4 AccentColour From bfc0d41c0ca81f5a0cc4c3fb00e7d320fac55330 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 15 Apr 2020 19:24:50 +0900 Subject: [PATCH 0241/1117] Add tick skinning support --- osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs index 86e3945021..3af7df07c4 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs @@ -46,6 +46,9 @@ namespace osu.Game.Rulesets.Taiko.Skinning return new LegacyHit(taikoComponent.Component); return null; + + case TaikoSkinComponents.DrumRollTick: + return this.GetAnimation("sliderscorepoint", false, false); } return source.GetDrawableComponent(component); From f36477e39dd1bbd055d345997f91c99701ffa208 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Apr 2020 10:04:09 +0900 Subject: [PATCH 0242/1117] Add back "filled" property setting --- .../Objects/Drawables/DrawableDrumRollTick.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs index e11e019826..689a7bfa64 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs @@ -21,7 +21,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables public override bool DisplayResult => false; protected override SkinnableDrawable CreateMainPiece() => new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.DrumRollTick), - _ => new TickPiece()); + _ => new TickPiece + { + Filled = HitObject.FirstTick + }); protected override void CheckForResult(bool userTriggered, double timeOffset) { From e2b28bfe88cc94c685a8ddbe3f496190c39103c1 Mon Sep 17 00:00:00 2001 From: Joehu Date: Wed, 15 Apr 2020 18:17:12 -0700 Subject: [PATCH 0243/1117] Hide edit context menu item in multiplayer song select --- .../Select/Carousel/DrawableCarouselBeatmap.cs | 12 +++++++++--- osu.Game/Screens/Select/SongSelect.cs | 10 ---------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs index 2520c70989..a371c56101 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs @@ -41,6 +41,9 @@ namespace osu.Game.Screens.Select.Carousel [Resolved(CanBeNull = true)] private BeatmapSetOverlay beatmapOverlay { get; set; } + [Resolved(CanBeNull = true)] + private SongSelect songSelect { get; set; } + public DrawableCarouselBeatmap(CarouselBeatmap panel) : base(panel) { @@ -49,7 +52,7 @@ namespace osu.Game.Screens.Select.Carousel } [BackgroundDependencyLoader(true)] - private void load(SongSelect songSelect, BeatmapManager manager) + private void load(BeatmapManager manager) { if (songSelect != null) { @@ -190,10 +193,13 @@ namespace osu.Game.Screens.Select.Carousel List items = new List { new OsuMenuItem("Play", MenuItemType.Highlighted, () => startRequested?.Invoke(beatmap)), - new OsuMenuItem("Edit", MenuItemType.Standard, () => editRequested?.Invoke(beatmap)), - new OsuMenuItem("Hide", MenuItemType.Destructive, () => hideRequested?.Invoke(beatmap)), }; + if (songSelect.AllowEditing) + items.Add(new OsuMenuItem("Edit", MenuItemType.Standard, () => editRequested?.Invoke(beatmap))); + + items.Add(new OsuMenuItem("Hide", MenuItemType.Destructive, () => hideRequested?.Invoke(beatmap))); + if (beatmap.OnlineBeatmapID.HasValue) items.Add(new OsuMenuItem("Details", MenuItemType.Standard, () => beatmapOverlay?.FetchAndShowBeatmap(beatmap.OnlineBeatmapID.Value))); diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index f164056ede..8967628954 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -34,7 +34,6 @@ using System.Linq; using System.Threading.Tasks; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; -using osu.Game.Overlays.Notifications; using osu.Game.Scoring; namespace osu.Game.Screens.Select @@ -71,9 +70,6 @@ namespace osu.Game.Screens.Select /// public virtual bool AllowEditing => true; - [Resolved(canBeNull: true)] - private NotificationOverlay notificationOverlay { get; set; } - [Resolved] private Bindable> selectedMods { get; set; } @@ -328,12 +324,6 @@ namespace osu.Game.Screens.Select public void Edit(BeatmapInfo beatmap = null) { - if (!AllowEditing) - { - notificationOverlay?.Post(new SimpleNotification { Text = "Editing is not available from the current mode." }); - return; - } - Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap ?? beatmapNoDebounce); this.Push(new Editor()); } From 06e25091f666c8b8f2ac4e4e42f1d24d83026c37 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Apr 2020 10:44:08 +0900 Subject: [PATCH 0244/1117] Fix typo --- .../Visual/UserInterface/TestSceneOsuMenu.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuMenu.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuMenu.cs index cdda1969ca..c171e567ad 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuMenu.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuMenu.cs @@ -21,12 +21,12 @@ namespace osu.Game.Tests.Visual.UserInterface }; private OsuMenu menu; - private bool actionPeformed; + private bool actionPerformed; [SetUp] public void Setup() => Schedule(() => { - actionPeformed = false; + actionPerformed = false; Child = menu = new OsuMenu(Direction.Vertical, true) { @@ -47,7 +47,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("move to first menu item", () => InputManager.MoveMouseTo(menu.ChildrenOfType().First())); AddStep("click", () => InputManager.Click(MouseButton.Left)); - AddAssert("action performed", () => actionPeformed); + AddAssert("action performed", () => actionPerformed); } [Test] @@ -62,7 +62,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("move to first menu item", () => InputManager.MoveMouseTo(menu.ChildrenOfType().First())); AddStep("click", () => InputManager.Click(MouseButton.Left)); - AddAssert("action not performed", () => !actionPeformed); + AddAssert("action not performed", () => !actionPerformed); } [Test] @@ -83,9 +83,9 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("move to first menu item", () => InputManager.MoveMouseTo(menu.ChildrenOfType().First())); AddStep("click", () => InputManager.Click(MouseButton.Left)); - AddAssert("action performed", () => actionPeformed); + AddAssert("action performed", () => actionPerformed); } - private void performAction() => actionPeformed = true; + private void performAction() => actionPerformed = true; } } From c4caf38febbe7862589c2f33d761c005a7e6f0fa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Apr 2020 12:10:20 +0900 Subject: [PATCH 0245/1117] Simplify menu item checks (and add for other items) --- .../Carousel/DrawableCarouselBeatmap.cs | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs index a371c56101..3e4798a812 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs @@ -41,9 +41,6 @@ namespace osu.Game.Screens.Select.Carousel [Resolved(CanBeNull = true)] private BeatmapSetOverlay beatmapOverlay { get; set; } - [Resolved(CanBeNull = true)] - private SongSelect songSelect { get; set; } - public DrawableCarouselBeatmap(CarouselBeatmap panel) : base(panel) { @@ -52,12 +49,13 @@ namespace osu.Game.Screens.Select.Carousel } [BackgroundDependencyLoader(true)] - private void load(BeatmapManager manager) + private void load(BeatmapManager manager, SongSelect songSelect) { if (songSelect != null) { startRequested = b => songSelect.FinaliseSelection(b); - editRequested = songSelect.Edit; + if (songSelect.AllowEditing) + editRequested = songSelect.Edit; } if (manager != null) @@ -190,18 +188,19 @@ namespace osu.Game.Screens.Select.Carousel { get { - List items = new List - { - new OsuMenuItem("Play", MenuItemType.Highlighted, () => startRequested?.Invoke(beatmap)), - }; + List items = new List(); - if (songSelect.AllowEditing) - items.Add(new OsuMenuItem("Edit", MenuItemType.Standard, () => editRequested?.Invoke(beatmap))); + if (startRequested != null) + items.Add(new OsuMenuItem("Play", MenuItemType.Highlighted, () => startRequested(beatmap))); - items.Add(new OsuMenuItem("Hide", MenuItemType.Destructive, () => hideRequested?.Invoke(beatmap))); + if (editRequested != null) + items.Add(new OsuMenuItem("Edit", MenuItemType.Standard, () => editRequested(beatmap))); - if (beatmap.OnlineBeatmapID.HasValue) - items.Add(new OsuMenuItem("Details", MenuItemType.Standard, () => beatmapOverlay?.FetchAndShowBeatmap(beatmap.OnlineBeatmapID.Value))); + if (hideRequested != null) + items.Add(new OsuMenuItem("Hide", MenuItemType.Destructive, () => hideRequested(beatmap))); + + if (beatmap.OnlineBeatmapID.HasValue && beatmapOverlay != null) + items.Add(new OsuMenuItem("Details", MenuItemType.Standard, () => beatmapOverlay.FetchAndShowBeatmap(beatmap.OnlineBeatmapID.Value))); return items.ToArray(); } From 91b13f91eaaaad87dd221bdb8daf3ed34d7166b2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Apr 2020 12:11:12 +0900 Subject: [PATCH 0246/1117] Add exception disallowing potential edit when disabled at a property level --- osu.Game/Screens/Select/SongSelect.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 8967628954..5bc2e1aa56 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -324,6 +324,9 @@ namespace osu.Game.Screens.Select public void Edit(BeatmapInfo beatmap = null) { + if (!AllowEditing) + throw new InvalidOperationException($"Attempted to edit when {nameof(AllowEditing)} is disabled"); + Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap ?? beatmapNoDebounce); this.Push(new Editor()); } From 03a74a4320db317e063130161933694d4563ca85 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Apr 2020 12:13:26 +0900 Subject: [PATCH 0247/1117] Apply same conditional check changes to DrawableCarouselBeatmapSet --- .../Select/Carousel/DrawableCarouselBeatmapSet.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index a53b74c1b8..5acb6d1946 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -46,6 +46,7 @@ namespace osu.Game.Screens.Select.Carousel private void load(BeatmapManager manager, BeatmapSetOverlay beatmapOverlay) { restoreHiddenRequested = s => s.Beatmaps.ForEach(manager.Restore); + if (beatmapOverlay != null) viewDetails = beatmapOverlay.FetchAndShowBeatmapSet; @@ -131,13 +132,14 @@ 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 (beatmapSet.OnlineBeatmapSetID != null) - items.Add(new OsuMenuItem("Details...", MenuItemType.Standard, () => viewDetails?.Invoke(beatmapSet.OnlineBeatmapSetID.Value))); + if (beatmapSet.OnlineBeatmapSetID != null && viewDetails != null) + items.Add(new OsuMenuItem("Details...", MenuItemType.Standard, () => viewDetails(beatmapSet.OnlineBeatmapSetID.Value))); if (beatmapSet.Beatmaps.Any(b => b.Hidden)) - items.Add(new OsuMenuItem("Restore all hidden", MenuItemType.Standard, () => restoreHiddenRequested?.Invoke(beatmapSet))); + items.Add(new OsuMenuItem("Restore all hidden", MenuItemType.Standard, () => restoreHiddenRequested(beatmapSet))); - items.Add(new OsuMenuItem("Delete", MenuItemType.Destructive, () => dialogOverlay?.Push(new BeatmapDeleteDialog(beatmapSet)))); + if (dialogOverlay != null) + items.Add(new OsuMenuItem("Delete", MenuItemType.Destructive, () => dialogOverlay.Push(new BeatmapDeleteDialog(beatmapSet)))); return items.ToArray(); } From 9e2be6f2f438dcc288bbe711c486a8cc112e310d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 16 Apr 2020 13:25:08 +0900 Subject: [PATCH 0248/1117] Remove bindable to promote one-way access --- osu.Game.Tests/Visual/UserInterface/TestSceneOsuMenu.cs | 6 +++--- osu.Game/Graphics/UserInterface/OsuMenuItem.cs | 6 ------ osu.Game/Screens/Edit/Editor.cs | 9 +++++++-- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuMenu.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuMenu.cs index c171e567ad..9ea76c2c7b 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuMenu.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuMenu.cs @@ -56,7 +56,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("disable menu items", () => { foreach (var item in menu.Items) - ((OsuMenuItem)item).Enabled.Value = false; + ((OsuMenuItem)item).Action.Disabled = true; }); AddStep("move to first menu item", () => InputManager.MoveMouseTo(menu.ChildrenOfType().First())); @@ -71,13 +71,13 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("disable menu items", () => { foreach (var item in menu.Items) - ((OsuMenuItem)item).Enabled.Value = false; + ((OsuMenuItem)item).Action.Disabled = true; }); AddStep("enable menu items", () => { foreach (var item in menu.Items) - ((OsuMenuItem)item).Enabled.Value = true; + ((OsuMenuItem)item).Action.Disabled = false; }); AddStep("move to first menu item", () => InputManager.MoveMouseTo(menu.ChildrenOfType().First())); diff --git a/osu.Game/Graphics/UserInterface/OsuMenuItem.cs b/osu.Game/Graphics/UserInterface/OsuMenuItem.cs index 36122ca0b2..0fe41937ce 100644 --- a/osu.Game/Graphics/UserInterface/OsuMenuItem.cs +++ b/osu.Game/Graphics/UserInterface/OsuMenuItem.cs @@ -2,15 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using System; -using osu.Framework.Bindables; using osu.Framework.Graphics.UserInterface; namespace osu.Game.Graphics.UserInterface { public class OsuMenuItem : MenuItem { - public readonly Bindable Enabled = new Bindable(true); - public readonly MenuItemType Type; public OsuMenuItem(string text, MenuItemType type = MenuItemType.Standard) @@ -22,9 +19,6 @@ namespace osu.Game.Graphics.UserInterface : base(text, action) { Type = type; - - Enabled.BindValueChanged(enabled => Action.Disabled = !enabled.NewValue); - Action.BindDisabledChanged(disabled => Enabled.Value = !disabled); } } } diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index ad17498d93..9a1f450dc6 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -107,6 +107,8 @@ namespace osu.Game.Screens.Edit dependencies.CacheAs(changeHandler); EditorMenuBar menuBar; + OsuMenuItem undoMenuItem; + OsuMenuItem redoMenuItem; var fileMenuItems = new List { @@ -155,8 +157,8 @@ namespace osu.Game.Screens.Edit { Items = new[] { - new EditorMenuItem("Undo", MenuItemType.Standard, undo) { Enabled = { BindTarget = changeHandler.CanUndo } }, - new EditorMenuItem("Redo", MenuItemType.Standard, redo) { Enabled = { BindTarget = changeHandler.CanRedo } } + undoMenuItem = new EditorMenuItem("Undo", MenuItemType.Standard, undo), + redoMenuItem = new EditorMenuItem("Redo", MenuItemType.Standard, redo) } } } @@ -214,6 +216,9 @@ namespace osu.Game.Screens.Edit } }); + changeHandler.CanUndo.BindValueChanged(v => undoMenuItem.Action.Disabled = !v.NewValue, true); + changeHandler.CanRedo.BindValueChanged(v => redoMenuItem.Action.Disabled = !v.NewValue, true); + menuBar.Mode.ValueChanged += onModeChanged; bottomBackground.Colour = colours.Gray2; From 9dda7da489918120d251c6c266272f41a2fa8671 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Apr 2020 14:11:38 +0900 Subject: [PATCH 0249/1117] Fix spinners being considered the "first object" for increased visibility in hidden --- .../Mods/TestSceneOsuModHidden.cs | 106 ++++++++++++++++++ osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs | 2 + osu.Game/Rulesets/Mods/ModHidden.cs | 14 ++- 3 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs new file mode 100644 index 0000000000..8bd3d3c7cc --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs @@ -0,0 +1,106 @@ +// 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.Tests.Visual; +using osuTK; + +namespace osu.Game.Rulesets.Osu.Tests.Mods +{ + public class TestSceneOsuModHidden : ModTestScene + { + public TestSceneOsuModHidden() + : base(new OsuRuleset()) + { + } + + [Test] + public void TestDefaultBeatmapTest() => CreateModTest(new ModTestData + { + Mod = new OsuModHidden(), + Autoplay = true, + PassCondition = checkSomeHit + }); + + [Test] + public void FirstCircleAfterTwoSpinners() => CreateModTest(new ModTestData + { + Mod = new OsuModHidden(), + Autoplay = true, + Beatmap = new Beatmap + { + HitObjects = new List + { + new Spinner + { + Position = new Vector2(256, 192), + EndTime = 1000, + }, + new Spinner + { + Position = new Vector2(256, 192), + StartTime = 1200, + EndTime = 2200, + }, + new HitCircle + { + Position = new Vector2(300, 192), + StartTime = 3200, + }, + new HitCircle + { + Position = new Vector2(384, 192), + StartTime = 4200, + } + } + }, + PassCondition = checkSomeHit + }); + + [Test] + public void FirstSliderAfterTwoSpinners() => CreateModTest(new ModTestData + { + Mod = new OsuModHidden(), + Autoplay = true, + Beatmap = new Beatmap + { + HitObjects = new List + { + new Spinner + { + Position = new Vector2(256, 192), + EndTime = 1000, + }, + new Spinner + { + Position = new Vector2(256, 192), + StartTime = 1200, + EndTime = 2200, + }, + new Slider + { + StartTime = 3200, + 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), }) + } + } + }, + PassCondition = checkSomeHit + }); + + private bool checkSomeHit() + { + return Player.ScoreProcessor.JudgedHits >= 4; + } + } +} diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs index 91a4e049e3..fdba03f260 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs @@ -23,6 +23,8 @@ namespace osu.Game.Rulesets.Osu.Mods private const double fade_in_duration_multiplier = 0.4; private const double fade_out_duration_multiplier = 0.3; + protected override bool IsFirstHideableObject(DrawableHitObject hitObject) => !(hitObject is DrawableSpinner); + public override void ApplyToDrawableHitObjects(IEnumerable drawables) { static void adjustFadeIn(OsuHitObject h) => h.TimeFadeIn = h.TimePreempt * fade_in_duration_multiplier; diff --git a/osu.Game/Rulesets/Mods/ModHidden.cs b/osu.Game/Rulesets/Mods/ModHidden.cs index 4e4a75db82..a1915b974c 100644 --- a/osu.Game/Rulesets/Mods/ModHidden.cs +++ b/osu.Game/Rulesets/Mods/ModHidden.cs @@ -23,6 +23,13 @@ namespace osu.Game.Rulesets.Mods protected Bindable IncreaseFirstObjectVisibility = new Bindable(); + /// + /// Check whether the provided hitobject should be considered the "first" hideable object. + /// Can be used to skip spinners, for instance. + /// + /// The hitobject to check. + protected virtual bool IsFirstHideableObject(DrawableHitObject hitObject) => true; + public void ReadFromConfig(OsuConfigManager config) { IncreaseFirstObjectVisibility = config.GetBindable(OsuSetting.IncreaseFirstObjectVisibility); @@ -30,8 +37,11 @@ namespace osu.Game.Rulesets.Mods public virtual void ApplyToDrawableHitObjects(IEnumerable drawables) { - foreach (var d in drawables.Skip(IncreaseFirstObjectVisibility.Value ? 1 : 0)) - d.ApplyCustomUpdateState += ApplyHiddenState; + if (IncreaseFirstObjectVisibility.Value) + drawables = drawables.SkipWhile(h => !IsFirstHideableObject(h)).Skip(1); + + foreach (var dho in drawables) + dho.ApplyCustomUpdateState += ApplyHiddenState; } public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) From ef0da9e3e831096674d37ba799246de1d569a786 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 16 Apr 2020 11:01:36 +0300 Subject: [PATCH 0250/1117] Basic overlay layout implementation --- .../Online/TestSceneDashboardOverlay.cs | 43 +++++++++++++ .../Dashboard/DashboardOverlayHeader.cs | 24 +++++++ osu.Game/Overlays/DashboardOverlay.cs | 62 +++++++++++++++++++ 3 files changed, 129 insertions(+) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneDashboardOverlay.cs create mode 100644 osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs create mode 100644 osu.Game/Overlays/DashboardOverlay.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneDashboardOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneDashboardOverlay.cs new file mode 100644 index 0000000000..df95f24686 --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneDashboardOverlay.cs @@ -0,0 +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 NUnit.Framework; +using osu.Game.Overlays; +using osu.Game.Overlays.Dashboard; +using osu.Game.Overlays.Dashboard.Friends; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneDashboardOverlay : OsuTestScene + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(DashboardOverlay), + typeof(DashboardOverlayHeader), + typeof(FriendDisplay) + }; + + protected override bool UseOnlineAPI => true; + + private readonly DashboardOverlay overlay; + + public TestSceneDashboardOverlay() + { + Add(overlay = new DashboardOverlay()); + } + + [Test] + public void TestShow() + { + AddStep("Show", overlay.Show); + } + + [Test] + public void TestHide() + { + AddStep("Hide", overlay.Hide); + } + } +} diff --git a/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs b/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs new file mode 100644 index 0000000000..1c52b033a5 --- /dev/null +++ b/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.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. + +namespace osu.Game.Overlays.Dashboard +{ + public class DashboardOverlayHeader : TabControlOverlayHeader + { + protected override OverlayTitle CreateTitle() => new DashboardTitle(); + + private class DashboardTitle : OverlayTitle + { + public DashboardTitle() + { + Title = "dashboard"; + IconTexture = "Icons/changelog"; + } + } + } + + public enum HomeOverlayTabs + { + Friends + } +} diff --git a/osu.Game/Overlays/DashboardOverlay.cs b/osu.Game/Overlays/DashboardOverlay.cs new file mode 100644 index 0000000000..a1a7c9889a --- /dev/null +++ b/osu.Game/Overlays/DashboardOverlay.cs @@ -0,0 +1,62 @@ +// 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.Game.Graphics.UserInterface; +using osu.Game.Overlays.Dashboard; + +namespace osu.Game.Overlays +{ + public class DashboardOverlay : FullscreenOverlay + { + private readonly Box background; + private readonly Container content; + + public DashboardOverlay() + : base(OverlayColourScheme.Purple) + { + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both + }, + new OverlayScrollContainer + { + RelativeSizeAxes = Axes.Both, + ScrollbarVisible = false, + Child = new FillFlowContainer + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new DashboardOverlayHeader + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Depth = -float.MaxValue + }, + content = new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y + } + } + } + }, + new LoadingLayer(content), + }; + } + + [BackgroundDependencyLoader] + private void load() + { + background.Colour = ColourProvider.Background5; + } + } +} From 2ab4a7293ec507b691fbf5fcd1208634fbe74aa2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Apr 2020 17:26:09 +0900 Subject: [PATCH 0251/1117] Clean up enum sorting attribute code --- .../API/Requests/SearchBeatmapSetsRequest.cs | 18 +------ .../BeatmapListing/BeatmapSearchFilterRow.cs | 28 ++-------- osu.Game/Utils/OrderAttribute.cs | 52 +++++++++++++++++++ 3 files changed, 56 insertions(+), 42 deletions(-) create mode 100644 osu.Game/Utils/OrderAttribute.cs diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs index aef0788b49..1206563b18 100644 --- a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs +++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs @@ -1,12 +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 System.ComponentModel; using osu.Framework.IO.Network; using osu.Game.Overlays; using osu.Game.Overlays.Direct; using osu.Game.Rulesets; +using osu.Game.Utils; namespace osu.Game.Online.API.Requests { @@ -139,20 +139,4 @@ namespace osu.Game.Online.API.Requests [Order(5)] Italian } - - [AttributeUsage(AttributeTargets.Field)] - public class OrderAttribute : Attribute - { - public readonly int Order; - - public OrderAttribute(int order) - { - Order = order; - } - } - - [AttributeUsage(AttributeTargets.Enum)] - public class HasOrderedElementsAttribute : Attribute - { - } } diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapSearchFilterRow.cs b/osu.Game/Overlays/BeatmapListing/BeatmapSearchFilterRow.cs index bc0a011e31..64b3afcae1 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapSearchFilterRow.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapSearchFilterRow.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -14,10 +13,10 @@ using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; -using osu.Game.Online.API.Requests; using osuTK; using osuTK.Graphics; using Humanizer; +using osu.Game.Utils; namespace osu.Game.Overlays.BeatmapListing { @@ -82,30 +81,9 @@ namespace osu.Game.Overlays.BeatmapListing TabContainer.Spacing = new Vector2(10, 0); - var type = typeof(T); - - if (type.IsEnum) + if (typeof(T).IsEnum) { - if (Attribute.GetCustomAttribute(type, typeof(HasOrderedElementsAttribute)) != null) - { - var enumValues = Enum.GetValues(type).Cast().ToArray(); - var enumNames = Enum.GetNames(type); - - int[] enumPositions = Array.ConvertAll(enumNames, n => - { - var orderAttr = (OrderAttribute)type.GetField(n).GetCustomAttributes(typeof(OrderAttribute), false)[0]; - return orderAttr.Order; - }); - - Array.Sort(enumPositions, enumValues); - - foreach (var val in enumValues) - AddItem(val); - - return; - } - - foreach (var val in (T[])Enum.GetValues(type)) + foreach (var val in OrderAttributeUtils.GetValuesInOrder()) AddItem(val); } } diff --git a/osu.Game/Utils/OrderAttribute.cs b/osu.Game/Utils/OrderAttribute.cs new file mode 100644 index 0000000000..4959caa726 --- /dev/null +++ b/osu.Game/Utils/OrderAttribute.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; +using System.Collections.Generic; +using System.Linq; + +namespace osu.Game.Utils +{ + public static class OrderAttributeUtils + { + /// + /// Get values of an enum in order. Supports custom ordering via . + /// + public static IEnumerable GetValuesInOrder() + { + var type = typeof(T); + + if (!type.IsEnum) + throw new InvalidOperationException("T must be an enum"); + + IEnumerable items = (T[])Enum.GetValues(type); + + if (Attribute.GetCustomAttribute(type, typeof(HasOrderedElementsAttribute)) == null) + return items; + + return items.OrderBy(i => + { + if (type.GetField(i.ToString()).GetCustomAttributes(typeof(OrderAttribute), false).FirstOrDefault() is OrderAttribute attr) + return attr.Order; + + return 0; + }); + } + } + + [AttributeUsage(AttributeTargets.Field)] + public class OrderAttribute : Attribute + { + public readonly int Order; + + public OrderAttribute(int order) + { + Order = order; + } + } + + [AttributeUsage(AttributeTargets.Enum)] + public class HasOrderedElementsAttribute : Attribute + { + } +} From c6aa6acc1b2f46c6c39a0d241325b1d2d8f154a5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 16 Apr 2020 17:28:06 +0900 Subject: [PATCH 0252/1117] Apply performance calculator changes --- .../Difficulty/CatchPerformanceCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs index 21d4642c22..bc52f0b812 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs @@ -70,7 +70,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty if (approachRate > 9.0f) approachRateFactor += 0.1f * (approachRate - 9.0f); // 10% for each AR above 9 if (approachRate > 10.0f) - approachRateFactor += 0.2f * (float)Math.Pow(approachRate - 10.0f, 1.5f); // Additional 20% at AR 11, 40% total + approachRateFactor += 0.1f * (approachRate - 10.0f); // Additional 10% at AR 11, 30% total else if (approachRate < 8.0f) approachRateFactor += 0.025f * (8.0f - approachRate); // 2.5% for each AR below 8 From 29bea4e11c03292545a9937a149f28c3686c14c4 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 16 Apr 2020 11:42:21 +0300 Subject: [PATCH 0253/1117] Implement OverlayView component --- .../Visual/Online/TestSceneFriendDisplay.cs | 17 ++- .../Dashboard/Friends/FriendDisplay.cs | 143 ++++++++---------- osu.Game/Overlays/OverlayView.cs | 71 +++++++++ 3 files changed, 149 insertions(+), 82 deletions(-) create mode 100644 osu.Game/Overlays/OverlayView.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneFriendDisplay.cs b/osu.Game.Tests/Visual/Online/TestSceneFriendDisplay.cs index cf365a7614..0b5ff1c960 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneFriendDisplay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneFriendDisplay.cs @@ -10,6 +10,7 @@ using osu.Game.Users; using osu.Game.Overlays; using osu.Framework.Allocation; using NUnit.Framework; +using osu.Game.Online.API; namespace osu.Game.Tests.Visual.Online { @@ -27,7 +28,7 @@ namespace osu.Game.Tests.Visual.Online [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); - private FriendDisplay display; + private TestFriendDisplay display; [SetUp] public void Setup() => Schedule(() => @@ -35,7 +36,7 @@ namespace osu.Game.Tests.Visual.Online Child = new BasicScrollContainer { RelativeSizeAxes = Axes.Both, - Child = display = new FriendDisplay() + Child = display = new TestFriendDisplay() }; }); @@ -83,5 +84,17 @@ namespace osu.Game.Tests.Visual.Online LastVisit = DateTimeOffset.Now } }; + + private class TestFriendDisplay : FriendDisplay + { + public void Fetch() + { + base.APIStateChanged(API, APIState.Online); + } + + public override void APIStateChanged(IAPIProvider api, APIState state) + { + } + } } } diff --git a/osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs b/osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs index 3c9b31daae..9764f82199 100644 --- a/osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs +++ b/osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs @@ -16,7 +16,7 @@ using osuTK; namespace osu.Game.Overlays.Dashboard.Friends { - public class FriendDisplay : CompositeDrawable + public class FriendDisplay : OverlayView> { private List users = new List(); @@ -26,15 +26,10 @@ namespace osu.Game.Overlays.Dashboard.Friends set { users = value; - onlineStreamControl.Populate(value); } } - [Resolved] - private IAPIProvider api { get; set; } - - private GetFriendsRequest request; private CancellationTokenSource cancellationToken; private Drawable currentContent; @@ -48,92 +43,85 @@ namespace osu.Game.Overlays.Dashboard.Friends public FriendDisplay() { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - InternalChild = new FillFlowContainer + AddRange(new Drawable[] { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new Drawable[] + new Container { - new Container + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new Drawable[] + controlBackground = new Box { - controlBackground = new Box + RelativeSizeAxes = Axes.Both + }, + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { - RelativeSizeAxes = Axes.Both + Top = 20, + Horizontal = 45 }, - new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding - { - Top = 20, - Horizontal = 45 - }, - Child = onlineStreamControl = new FriendOnlineStreamControl(), - } + Child = onlineStreamControl = new FriendOnlineStreamControl(), } - }, - new Container + } + }, + new Container + { + Name = "User List", + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] { - Name = "User List", - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new Drawable[] + background = new Box { - background = new Box + RelativeSizeAxes = Axes.Both + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Margin = new MarginPadding { Bottom = 20 }, + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both - }, - new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Margin = new MarginPadding { Bottom = 20 }, - Children = new Drawable[] + new Container { - new Container + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding - { - Horizontal = 40, - Vertical = 20 - }, - Child = userListToolbar = new UserListToolbar - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - } + Horizontal = 40, + Vertical = 20 }, - new Container + Child = userListToolbar = new UserListToolbar { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new Drawable[] + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + } + }, + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + itemsPlaceholder = new Container { - itemsPlaceholder = new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding { Horizontal = 50 } - }, - loading = new LoadingLayer(itemsPlaceholder) - } + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Horizontal = 50 } + }, + loading = new LoadingLayer(itemsPlaceholder) } } } } } } - }; + }); } [BackgroundDependencyLoader] @@ -152,14 +140,11 @@ namespace osu.Game.Overlays.Dashboard.Friends userListToolbar.SortCriteria.BindValueChanged(_ => recreatePanels()); } - public void Fetch() - { - if (!api.IsLoggedIn) - return; + protected override APIRequest> CreateRequest() => new GetFriendsRequest(); - request = new GetFriendsRequest(); - request.Success += response => Schedule(() => Users = response); - api.Queue(request); + protected override void OnSuccess(List response) + { + Users = response; } private void recreatePanels() @@ -258,9 +243,7 @@ namespace osu.Game.Overlays.Dashboard.Friends protected override void Dispose(bool isDisposing) { - request?.Cancel(); cancellationToken?.Cancel(); - base.Dispose(isDisposing); } } diff --git a/osu.Game/Overlays/OverlayView.cs b/osu.Game/Overlays/OverlayView.cs new file mode 100644 index 0000000000..f39c6bd1b9 --- /dev/null +++ b/osu.Game/Overlays/OverlayView.cs @@ -0,0 +1,71 @@ +// 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.Game.Online.API; + +namespace osu.Game.Overlays +{ + /// + /// Drawable which used to represent online content in . + /// + /// Response type + public abstract class OverlayView : Container, IOnlineComponent + where T : class + { + [Resolved] + protected IAPIProvider API { get; private set; } + + protected override Container Content => content; + + private readonly FillFlowContainer content; + + protected OverlayView() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + AddInternal(content = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + }); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + API.Register(this); + } + + private APIRequest request; + + protected abstract APIRequest CreateRequest(); + + protected abstract void OnSuccess(T response); + + public virtual void APIStateChanged(IAPIProvider api, APIState state) + { + switch (state) + { + case APIState.Online: + request = CreateRequest(); + request.Success += response => Schedule(() => OnSuccess(response)); + api.Queue(request); + break; + + default: + break; + } + } + + protected override void Dispose(bool isDisposing) + { + request?.Cancel(); + API?.Unregister(this); + base.Dispose(isDisposing); + } + } +} From 894598eb220e7cc05f3fab5df81a786973f804d5 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 16 Apr 2020 12:05:51 +0300 Subject: [PATCH 0254/1117] Replace SocialOverlay with DashboardOverlay --- osu.Game.Tests/Visual/TestSceneOsuGame.cs | 2 +- osu.Game/OsuGame.cs | 8 +- .../Dashboard/DashboardOverlayHeader.cs | 4 +- osu.Game/Overlays/DashboardOverlay.cs | 94 ++++++++++++++++++- .../Overlays/Toolbar/ToolbarSocialButton.cs | 4 +- 5 files changed, 100 insertions(+), 12 deletions(-) diff --git a/osu.Game.Tests/Visual/TestSceneOsuGame.cs b/osu.Game.Tests/Visual/TestSceneOsuGame.cs index 492494ada3..8793d880e3 100644 --- a/osu.Game.Tests/Visual/TestSceneOsuGame.cs +++ b/osu.Game.Tests/Visual/TestSceneOsuGame.cs @@ -48,7 +48,7 @@ namespace osu.Game.Tests.Visual typeof(OnScreenDisplay), typeof(NotificationOverlay), typeof(DirectOverlay), - typeof(SocialOverlay), + typeof(DashboardOverlay), typeof(ChannelManager), typeof(ChatOverlay), typeof(SettingsOverlay), diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 5e93d760e3..c861b84835 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -67,7 +67,7 @@ namespace osu.Game private DirectOverlay direct; - private SocialOverlay social; + private DashboardOverlay dashboard; private UserProfileOverlay userProfile; @@ -611,7 +611,7 @@ namespace osu.Game //overlay elements loadComponentSingleFile(direct = new DirectOverlay(), overlayContent.Add, true); - loadComponentSingleFile(social = new SocialOverlay(), overlayContent.Add, true); + loadComponentSingleFile(dashboard = new DashboardOverlay(), overlayContent.Add, true); var rankingsOverlay = loadComponentSingleFile(new RankingsOverlay(), overlayContent.Add, true); loadComponentSingleFile(channelManager = new ChannelManager(), AddInternal, true); loadComponentSingleFile(chatOverlay = new ChatOverlay(), overlayContent.Add, true); @@ -670,7 +670,7 @@ namespace osu.Game } // ensure only one of these overlays are open at once. - var singleDisplayOverlays = new OverlayContainer[] { chatOverlay, social, direct, changelogOverlay, rankingsOverlay }; + var singleDisplayOverlays = new OverlayContainer[] { chatOverlay, dashboard, direct, changelogOverlay, rankingsOverlay }; foreach (var overlay in singleDisplayOverlays) { @@ -842,7 +842,7 @@ namespace osu.Game return true; case GlobalAction.ToggleSocial: - social.ToggleVisibility(); + dashboard.ToggleVisibility(); return true; case GlobalAction.ResetInputSettings: diff --git a/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs b/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs index 1c52b033a5..9ee679a866 100644 --- a/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs +++ b/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs @@ -3,7 +3,7 @@ namespace osu.Game.Overlays.Dashboard { - public class DashboardOverlayHeader : TabControlOverlayHeader + public class DashboardOverlayHeader : TabControlOverlayHeader { protected override OverlayTitle CreateTitle() => new DashboardTitle(); @@ -17,7 +17,7 @@ namespace osu.Game.Overlays.Dashboard } } - public enum HomeOverlayTabs + public enum DashboardOverlayTabs { Friends } diff --git a/osu.Game/Overlays/DashboardOverlay.cs b/osu.Game/Overlays/DashboardOverlay.cs index a1a7c9889a..1e0fbc90b4 100644 --- a/osu.Game/Overlays/DashboardOverlay.cs +++ b/osu.Game/Overlays/DashboardOverlay.cs @@ -1,19 +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 System.Threading; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API; using osu.Game.Overlays.Dashboard; +using osu.Game.Overlays.Dashboard.Friends; namespace osu.Game.Overlays { public class DashboardOverlay : FullscreenOverlay { + private CancellationTokenSource cancellationToken; + private readonly Box background; private readonly Container content; + private readonly DashboardOverlayHeader header; + private readonly LoadingLayer loading; + private readonly OverlayScrollContainer scrollFlow; public DashboardOverlay() : base(OverlayColourScheme.Purple) @@ -24,7 +34,7 @@ namespace osu.Game.Overlays { RelativeSizeAxes = Axes.Both }, - new OverlayScrollContainer + scrollFlow = new OverlayScrollContainer { RelativeSizeAxes = Axes.Both, ScrollbarVisible = false, @@ -35,7 +45,7 @@ namespace osu.Game.Overlays Direction = FillDirection.Vertical, Children = new Drawable[] { - new DashboardOverlayHeader + header = new DashboardOverlayHeader { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, @@ -49,7 +59,7 @@ namespace osu.Game.Overlays } } }, - new LoadingLayer(content), + loading = new LoadingLayer(content), }; } @@ -58,5 +68,83 @@ namespace osu.Game.Overlays { background.Colour = ColourProvider.Background5; } + + protected override void LoadComplete() + { + base.LoadComplete(); + header.Current.BindValueChanged(onTabChanged); + } + + private bool displayUpdateRequired = true; + + protected override void PopIn() + { + base.PopIn(); + + // We don't want to create new display on every call, only when exiting from fully closed state. + if (displayUpdateRequired) + { + header.Current.TriggerChange(); + displayUpdateRequired = false; + } + } + + protected override void PopOutComplete() + { + base.PopOutComplete(); + loadDisplay(Empty()); + displayUpdateRequired = true; + } + + private void loadDisplay(Drawable display) + { + scrollFlow.ScrollToStart(); + + LoadComponentAsync(display, loaded => + { + loading.Hide(); + content.Child = loaded; + }, (cancellationToken = new CancellationTokenSource()).Token); + } + + private void onTabChanged(ValueChangedEvent tab) + { + cancellationToken?.Cancel(); + + loading.Show(); + + switch (tab.NewValue) + { + case DashboardOverlayTabs.Friends: + loadDisplay(new FriendDisplay()); + break; + + default: + throw new NotImplementedException($"Display for {tab.NewValue} tab is not implemented"); + } + } + + public override void APIStateChanged(IAPIProvider api, APIState state) + { + switch (state) + { + case APIState.Online: + // Will force to create a display based on visibility state + displayUpdateRequired = true; + State.TriggerChange(); + return; + + default: + content.Clear(); + loading.Show(); + return; + } + } + + protected override void Dispose(bool isDisposing) + { + cancellationToken?.Cancel(); + base.Dispose(isDisposing); + } } } diff --git a/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs b/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs index 5e353d3319..f6646eb81d 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs @@ -14,9 +14,9 @@ namespace osu.Game.Overlays.Toolbar } [BackgroundDependencyLoader(true)] - private void load(SocialOverlay chat) + private void load(DashboardOverlay dashboard) { - StateContainer = chat; + StateContainer = dashboard; } } } From eb86be0a6da6d76dfef8526eff26ddb584d8bd7b Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 16 Apr 2020 12:07:38 +0300 Subject: [PATCH 0255/1117] Adjust header content margin --- osu.Game/Overlays/OverlayHeader.cs | 4 +++- osu.Game/Overlays/TabControlOverlayHeader.cs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/OverlayHeader.cs b/osu.Game/Overlays/OverlayHeader.cs index 4ac0f697c3..dbc934bde9 100644 --- a/osu.Game/Overlays/OverlayHeader.cs +++ b/osu.Game/Overlays/OverlayHeader.cs @@ -12,6 +12,8 @@ namespace osu.Game.Overlays { public abstract class OverlayHeader : Container { + public const int CONTENT_X_MARGIN = 50; + private readonly Box titleBackground; protected readonly FillFlowContainer HeaderInfo; @@ -54,7 +56,7 @@ namespace osu.Game.Overlays AutoSizeAxes = Axes.Y, Padding = new MarginPadding { - Horizontal = UserProfileOverlay.CONTENT_X_MARGIN, + Horizontal = CONTENT_X_MARGIN, }, Children = new[] { diff --git a/osu.Game/Overlays/TabControlOverlayHeader.cs b/osu.Game/Overlays/TabControlOverlayHeader.cs index ab1a6aff78..e8e000f441 100644 --- a/osu.Game/Overlays/TabControlOverlayHeader.cs +++ b/osu.Game/Overlays/TabControlOverlayHeader.cs @@ -44,7 +44,7 @@ namespace osu.Game.Overlays }, TabControl = CreateTabControl().With(control => { - control.Margin = new MarginPadding { Left = UserProfileOverlay.CONTENT_X_MARGIN }; + control.Margin = new MarginPadding { Left = CONTENT_X_MARGIN }; control.Current = Current; }) } From 87f52b82331dc1f6ba4b198d96cb8b768a152c19 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 16 Apr 2020 12:09:44 +0300 Subject: [PATCH 0256/1117] Remove redundant switch section --- osu.Game/Overlays/OverlayView.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Overlays/OverlayView.cs b/osu.Game/Overlays/OverlayView.cs index f39c6bd1b9..e3a07fc2de 100644 --- a/osu.Game/Overlays/OverlayView.cs +++ b/osu.Game/Overlays/OverlayView.cs @@ -55,9 +55,6 @@ namespace osu.Game.Overlays request.Success += response => Schedule(() => OnSuccess(response)); api.Queue(request); break; - - default: - break; } } From d62094cd4ba1e9d20d60edc8e326198c615f8732 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Apr 2020 18:10:35 +0900 Subject: [PATCH 0257/1117] Fix carousel not correctly updating when selection changes to a new beatmap from a child screen --- osu.Game/Screens/Select/BeatmapCarousel.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index a8225ba1ec..d8178bbbbb 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -217,6 +217,9 @@ namespace osu.Game.Screens.Select /// True if a selection was made, False if it wasn't. public bool SelectBeatmap(BeatmapInfo beatmap, bool bypassFilters = true) { + // ensure that any pending events from BeatmapManager have been run before attempting a selection. + Scheduler.Update(); + if (beatmap?.Hidden != false) return false; From d7ea5432a8b1eda9d85e29de2261b0d4cfa3ccbc Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 16 Apr 2020 18:15:52 +0900 Subject: [PATCH 0258/1117] Fix incorrect combo calculation --- .../Difficulty/CatchPerformanceCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs index bc52f0b812..e7ce680365 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs @@ -63,7 +63,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty // Combo scaling if (Attributes.MaxCombo > 0) - value *= Math.Min(Math.Pow(Attributes.MaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0); + value *= Math.Min(Math.Pow(Score.MaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0); float approachRate = (float)Attributes.ApproachRate; float approachRateFactor = 1.0f; From ae210d567d794f5b16587b8684bb9bdb9edfeeb2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 16 Apr 2020 18:16:08 +0900 Subject: [PATCH 0259/1117] Add temporary solution for tick hit/miss count --- osu.Game/Rulesets/Scoring/HitResult.cs | 5 +++++ .../Scoring/Legacy/ScoreInfoExtensions.cs | 21 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/osu.Game/Rulesets/Scoring/HitResult.cs b/osu.Game/Rulesets/Scoring/HitResult.cs index 7ba88d3df8..0c895bd086 100644 --- a/osu.Game/Rulesets/Scoring/HitResult.cs +++ b/osu.Game/Rulesets/Scoring/HitResult.cs @@ -43,5 +43,10 @@ namespace osu.Game.Rulesets.Scoring /// [Description(@"Perfect")] Perfect, + + SmallTickHit, + SmallTickMiss, + LargeTickHit, + LargeTickMiss, } } diff --git a/osu.Game/Scoring/Legacy/ScoreInfoExtensions.cs b/osu.Game/Scoring/Legacy/ScoreInfoExtensions.cs index 66b1acf591..9745d1abef 100644 --- a/osu.Game/Scoring/Legacy/ScoreInfoExtensions.cs +++ b/osu.Game/Scoring/Legacy/ScoreInfoExtensions.cs @@ -66,6 +66,9 @@ namespace osu.Game.Scoring.Legacy { case 3: return scoreInfo.Statistics[HitResult.Good]; + + case 2: + return scoreInfo.Statistics[HitResult.SmallTickMiss]; } return null; @@ -78,6 +81,10 @@ namespace osu.Game.Scoring.Legacy case 3: scoreInfo.Statistics[HitResult.Good] = value; break; + + case 2: + scoreInfo.Statistics[HitResult.SmallTickMiss] = value; + break; } } @@ -91,6 +98,9 @@ namespace osu.Game.Scoring.Legacy case 3: return scoreInfo.Statistics[HitResult.Ok]; + + case 2: + return scoreInfo.Statistics[HitResult.LargeTickHit]; } return null; @@ -108,6 +118,10 @@ namespace osu.Game.Scoring.Legacy case 3: scoreInfo.Statistics[HitResult.Ok] = value; break; + + case 2: + scoreInfo.Statistics[HitResult.LargeTickHit] = value; + break; } } @@ -118,6 +132,9 @@ namespace osu.Game.Scoring.Legacy case 0: case 3: return scoreInfo.Statistics[HitResult.Meh]; + + case 2: + return scoreInfo.Statistics[HitResult.SmallTickHit]; } return null; @@ -131,6 +148,10 @@ namespace osu.Game.Scoring.Legacy case 3: scoreInfo.Statistics[HitResult.Meh] = value; break; + + case 2: + scoreInfo.Statistics[HitResult.SmallTickHit] = value; + break; } } From c5a343d3a07daf31ad95a036850a05e7007f2a41 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 16 Apr 2020 14:10:39 +0300 Subject: [PATCH 0260/1117] Fix overlay accepting state changes while hidden --- osu.Game/Overlays/DashboardOverlay.cs | 28 +++++++++++++-------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/osu.Game/Overlays/DashboardOverlay.cs b/osu.Game/Overlays/DashboardOverlay.cs index 1e0fbc90b4..86c0f3bd83 100644 --- a/osu.Game/Overlays/DashboardOverlay.cs +++ b/osu.Game/Overlays/DashboardOverlay.cs @@ -81,7 +81,7 @@ namespace osu.Game.Overlays { base.PopIn(); - // We don't want to create new display on every call, only when exiting from fully closed state. + // We don't want to create a new display on every call, only when exiting from fully closed state. if (displayUpdateRequired) { header.Current.TriggerChange(); @@ -102,7 +102,9 @@ namespace osu.Game.Overlays LoadComponentAsync(display, loaded => { - loading.Hide(); + if (API.IsLoggedIn) + loading.Hide(); + content.Child = loaded; }, (cancellationToken = new CancellationTokenSource()).Token); } @@ -110,9 +112,14 @@ namespace osu.Game.Overlays private void onTabChanged(ValueChangedEvent tab) { cancellationToken?.Cancel(); - loading.Show(); + if (!API.IsLoggedIn) + { + loadDisplay(Empty()); + return; + } + switch (tab.NewValue) { case DashboardOverlayTabs.Friends: @@ -126,19 +133,10 @@ namespace osu.Game.Overlays public override void APIStateChanged(IAPIProvider api, APIState state) { - switch (state) - { - case APIState.Online: - // Will force to create a display based on visibility state - displayUpdateRequired = true; - State.TriggerChange(); - return; + if (State.Value == Visibility.Hidden) + return; - default: - content.Clear(); - loading.Show(); - return; - } + header.Current.TriggerChange(); } protected override void Dispose(bool isDisposing) From 3daacbc2d202b3d42ac84e7242e571a045d8fa09 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 17 Apr 2020 13:34:20 +0900 Subject: [PATCH 0261/1117] Initial inefficient refactor of hitobject enumeration --- osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.cs | 76 ++++++++------------ osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs | 2 +- 2 files changed, 29 insertions(+), 49 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.cs b/osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.cs index dfca2aff7b..171ce6fe61 100644 --- a/osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.cs +++ b/osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.cs @@ -1,9 +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.Collections.Generic; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.UI; @@ -37,12 +37,9 @@ namespace osu.Game.Rulesets.Osu.UI DrawableHitObject blockingObject = null; // Find the last hitobject which blocks future hits. - foreach (var obj in hitObjectContainer.AliveObjects) + foreach (var obj in enumerateHitObjectsUpTo(hitObject)) { - if (obj == hitObject) - break; - - if (drawableCanBlockFutureHits(obj)) + if (hitObjectCanBlockFutureHits(obj)) blockingObject = obj; } @@ -64,64 +61,47 @@ namespace osu.Game.Rulesets.Osu.UI /// Handles a being hit to potentially miss all earlier s. /// /// The that was hit. - public void HandleHit(HitObject hitObject) + public void HandleHit(DrawableHitObject hitObject) { // Hitobjects which themselves don't block future hitobjects don't cause misses (e.g. slider ticks, spinners). if (!hitObjectCanBlockFutureHits(hitObject)) return; - double maximumTime = hitObject.StartTime; - - // Iterate through and apply miss results to all top-level and nested hitobjects which block future hits. - foreach (var obj in hitObjectContainer.AliveObjects) + foreach (var obj in enumerateHitObjectsUpTo(hitObject)) { - if (obj.Judged || obj.HitObject.StartTime >= maximumTime) + if (obj.Judged) continue; - if (hitObjectCanBlockFutureHits(obj.HitObject)) - applyMiss(obj); - - foreach (var nested in obj.NestedHitObjects) - { - if (nested.Judged || nested.HitObject.StartTime >= maximumTime) - continue; - - if (hitObjectCanBlockFutureHits(nested.HitObject)) - applyMiss(nested); - } + if (hitObjectCanBlockFutureHits(obj)) + ((DrawableOsuHitObject)obj).MissForcefully(); } - - static void applyMiss(DrawableHitObject obj) => ((DrawableOsuHitObject)obj).MissForcefully(); - } - - /// - /// Whether a blocks hits on future s until its start time is reached. - /// - /// - /// This will ONLY match on top-most s. - /// - /// The to test. - private static bool drawableCanBlockFutureHits(DrawableHitObject hitObject) - { - // Special considerations for slider tails aren't required since only top-most drawable hitobjects are being iterated over. - return hitObject is DrawableHitCircle || hitObject is DrawableSlider; } /// /// Whether a blocks hits on future s until its start time is reached. /// - /// - /// This is more rigorous and may not match on top-most s as does. - /// /// The to test. - private static bool hitObjectCanBlockFutureHits(HitObject hitObject) - { - // Unlike the above we will receive slider tails, but they do not block future hits. - if (hitObject is SliderTailCircle) - return false; + private static bool hitObjectCanBlockFutureHits(DrawableHitObject hitObject) + => hitObject is DrawableHitCircle; - // All other hitcircles continue to block future hits. - return hitObject is HitCircle; + // Todo: Inefficient + private IEnumerable enumerateHitObjectsUpTo(DrawableHitObject hitObject) + { + return enumerate(hitObjectContainer.AliveObjects); + + IEnumerable enumerate(IEnumerable list) + { + foreach (var obj in list) + { + if (obj.HitObject.StartTime >= hitObject.HitObject.StartTime) + yield break; + + yield return obj; + + foreach (var nested in enumerate(obj.NestedHitObjects)) + yield return nested; + } + } } } } diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index 2f222f59b4..4b1a2ce43c 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -86,7 +86,7 @@ namespace osu.Game.Rulesets.Osu.UI private void onNewResult(DrawableHitObject judgedObject, JudgementResult result) { // Hitobjects that block future hits should miss previous hitobjects if they're hit out-of-order. - hitPolicy.HandleHit(result.HitObject); + hitPolicy.HandleHit(judgedObject); if (!judgedObject.DisplayResult || !DisplayJudgements.Value) return; From 62f77a05befb156ac6cda6411f2dda85ffcc8b44 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 17 Apr 2020 14:00:00 +0900 Subject: [PATCH 0262/1117] Optimise by removing state machine --- osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.cs | 115 +++++++++++++++---- 1 file changed, 95 insertions(+), 20 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.cs b/osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.cs index 171ce6fe61..b55e04ec4c 100644 --- a/osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.cs +++ b/osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.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.Collections; using System.Collections.Generic; +using System.Diagnostics; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables; @@ -36,11 +38,14 @@ namespace osu.Game.Rulesets.Osu.UI { DrawableHitObject blockingObject = null; - // Find the last hitobject which blocks future hits. - foreach (var obj in enumerateHitObjectsUpTo(hitObject)) + var enumerator = new HitObjectEnumerator(hitObjectContainer, hitObject.HitObject.StartTime); + + while (enumerator.MoveNext()) { - if (hitObjectCanBlockFutureHits(obj)) - blockingObject = obj; + Debug.Assert(enumerator.Current != null); + + if (hitObjectCanBlockFutureHits(enumerator.Current)) + blockingObject = enumerator.Current; } // If there is no previous hitobject, allow the hit. @@ -67,13 +72,17 @@ namespace osu.Game.Rulesets.Osu.UI if (!hitObjectCanBlockFutureHits(hitObject)) return; - foreach (var obj in enumerateHitObjectsUpTo(hitObject)) + var enumerator = new HitObjectEnumerator(hitObjectContainer, hitObject.HitObject.StartTime); + + while (enumerator.MoveNext()) { - if (obj.Judged) + Debug.Assert(enumerator.Current != null); + + if (enumerator.Current.Judged) continue; - if (hitObjectCanBlockFutureHits(obj)) - ((DrawableOsuHitObject)obj).MissForcefully(); + if (hitObjectCanBlockFutureHits(enumerator.Current)) + ((DrawableOsuHitObject)enumerator.Current).MissForcefully(); } } @@ -84,23 +93,89 @@ namespace osu.Game.Rulesets.Osu.UI private static bool hitObjectCanBlockFutureHits(DrawableHitObject hitObject) => hitObject is DrawableHitCircle; - // Todo: Inefficient - private IEnumerable enumerateHitObjectsUpTo(DrawableHitObject hitObject) + private struct HitObjectEnumerator : IEnumerator { - return enumerate(hitObjectContainer.AliveObjects); + private readonly IEnumerator hitObjectEnumerator; + private readonly double targetTime; - IEnumerable enumerate(IEnumerable list) + private DrawableHitObject currentTopLevel; + private int currentNestedIndex; + + public HitObjectEnumerator(HitObjectContainer hitObjectContainer, double targetTime) { - foreach (var obj in list) - { - if (obj.HitObject.StartTime >= hitObject.HitObject.StartTime) - yield break; + hitObjectEnumerator = hitObjectContainer.AliveObjects.GetEnumerator(); + this.targetTime = targetTime; - yield return obj; + currentTopLevel = null; + currentNestedIndex = -1; + Current = null; + } - foreach (var nested in enumerate(obj.NestedHitObjects)) - yield return nested; - } + /// + /// Attempts to move to the next top-level or nested hitobject. + /// Stops when no such hitobject is found or until the hitobject start time reaches . + /// + /// Whether a new hitobject was moved to. + public bool MoveNext() + { + // If we don't already have a top-level hitobject, try to get one. + if (currentTopLevel == null) + return moveNextTopLevel(); + + // If we have a top-level hitobject, try to move to the next nested hitobject or otherwise move to the next top-level hitobject. + if (!moveNextNested()) + return moveNextTopLevel(); + + // Guaranteed by moveNextNested() to have a hitobject. + return true; + } + + /// + /// Attempts to move to the next top-level hitobject. + /// + /// Whether a new top-level hitobject was found. + private bool moveNextTopLevel() + { + currentNestedIndex = -1; + + hitObjectEnumerator.MoveNext(); + currentTopLevel = hitObjectEnumerator.Current; + + Current = currentTopLevel; + + return Current?.HitObject.StartTime < targetTime; + } + + /// + /// Attempts to move to the next nested hitobject in the current top-level hitobject. + /// + /// Whether a new nested hitobject was moved to. + private bool moveNextNested() + { + currentNestedIndex++; + if (currentNestedIndex >= currentTopLevel.NestedHitObjects.Count) + return false; + + Current = currentTopLevel.NestedHitObjects[currentNestedIndex]; + Debug.Assert(Current != null); + + return Current?.HitObject.StartTime < targetTime; + } + + public void Reset() + { + hitObjectEnumerator.Reset(); + currentTopLevel = null; + currentNestedIndex = -1; + Current = null; + } + + public DrawableHitObject Current { get; set; } + + object IEnumerator.Current => Current; + + public void Dispose() + { } } } From ee5301b887a78a3bd0cabab17c306857133da794 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 17 Apr 2020 14:12:38 +0900 Subject: [PATCH 0263/1117] Fix head/tail circles not getting correct hit windows --- osu.Game.Rulesets.Osu.Tests/TestSceneOutOfOrderHits.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOutOfOrderHits.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOutOfOrderHits.cs index d6858f831e..3df51be600 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneOutOfOrderHits.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOutOfOrderHits.cs @@ -371,6 +371,9 @@ namespace osu.Game.Rulesets.Osu.Tests { HeadCircle.HitWindows = new TestHitWindows(); TailCircle.HitWindows = new TestHitWindows(); + + HeadCircle.HitWindows.SetDifficulty(0); + TailCircle.HitWindows.SetDifficulty(0); }; } } From 08df9d49e52a968691b77a76fe360c3111eb3436 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 17 Apr 2020 14:12:43 +0900 Subject: [PATCH 0264/1117] Add failing test --- .../TestSceneOutOfOrderHits.cs | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOutOfOrderHits.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOutOfOrderHits.cs index 3df51be600..40ee53e8f2 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneOutOfOrderHits.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOutOfOrderHits.cs @@ -296,6 +296,44 @@ namespace osu.Game.Rulesets.Osu.Tests addJudgementAssert(hitObjects[1], HitResult.Great); } + [Test] + public void TestHitSliderHeadBeforeHitCircle() + { + const double time_circle = 1000; + const double time_slider = 1200; + Vector2 positionCircle = Vector2.Zero; + Vector2 positionSlider = new Vector2(80); + + var hitObjects = new List + { + new TestHitCircle + { + StartTime = time_circle, + Position = positionCircle + }, + new TestSlider + { + StartTime = time_slider, + Position = positionSlider, + Path = new SliderPath(PathType.Linear, new[] + { + Vector2.Zero, + new Vector2(25, 0), + }) + } + }; + + performTest(hitObjects, new List + { + new OsuReplayFrame { Time = time_circle - 100, Position = positionSlider, Actions = { OsuAction.LeftButton } }, + new OsuReplayFrame { Time = time_circle, Position = positionCircle, Actions = { OsuAction.RightButton } }, + new OsuReplayFrame { Time = time_slider, Position = positionSlider, Actions = { OsuAction.LeftButton } }, + }); + + addJudgementAssert(hitObjects[0], HitResult.Great); + addJudgementAssert(hitObjects[1], HitResult.Great); + } + private void addJudgementAssert(OsuHitObject hitObject, HitResult result) { AddAssert($"({hitObject.GetType().ReadableName()} @ {hitObject.StartTime}) judgement is {result}", From a4a782381797f927bc80f108cbbf94f410faef99 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 17 Apr 2020 14:22:03 +0900 Subject: [PATCH 0265/1117] Add fail-safe to ensure hittability after a hit --- osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.cs b/osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.cs index b55e04ec4c..31edefea83 100644 --- a/osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.cs +++ b/osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.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; using System.Collections.Generic; using System.Diagnostics; @@ -72,6 +73,9 @@ namespace osu.Game.Rulesets.Osu.UI if (!hitObjectCanBlockFutureHits(hitObject)) return; + if (!IsHittable(hitObject, hitObject.HitObject.StartTime + hitObject.Result.TimeOffset)) + throw new InvalidOperationException($"A {hitObject} was hit before it become hittable!"); + var enumerator = new HitObjectEnumerator(hitObjectContainer, hitObject.HitObject.StartTime); while (enumerator.MoveNext()) From 4e4fe5cc904107ac647ffaa57d5ac17361ee073e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 17 Apr 2020 14:33:29 +0900 Subject: [PATCH 0266/1117] Fix slider heads not being blocked when hit out of order --- 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 522217a916..72502c02cd 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -125,7 +125,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables return new DrawableSliderTail(slider, tail); case SliderHeadCircle head: - return new DrawableSliderHead(slider, head) { OnShake = Shake }; + return new DrawableSliderHead(slider, head) + { + OnShake = Shake, + CheckHittable = (d, t) => CheckHittable?.Invoke(d, t) ?? true + }; case SliderTick tick: return new DrawableSliderTick(tick) { Position = tick.Position - slider.Position }; From 2dee5e03e30f158880a09aaa04ded47879d6f74d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 17 Apr 2020 14:40:29 +0900 Subject: [PATCH 0267/1117] Dispose enumerators for safety --- osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.cs | 31 +++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.cs b/osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.cs index 31edefea83..4bc7da4794 100644 --- a/osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.cs +++ b/osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.cs @@ -39,14 +39,15 @@ namespace osu.Game.Rulesets.Osu.UI { DrawableHitObject blockingObject = null; - var enumerator = new HitObjectEnumerator(hitObjectContainer, hitObject.HitObject.StartTime); - - while (enumerator.MoveNext()) + using (var enumerator = new HitObjectEnumerator(hitObjectContainer, hitObject.HitObject.StartTime)) { - Debug.Assert(enumerator.Current != null); + while (enumerator.MoveNext()) + { + Debug.Assert(enumerator.Current != null); - if (hitObjectCanBlockFutureHits(enumerator.Current)) - blockingObject = enumerator.Current; + if (hitObjectCanBlockFutureHits(enumerator.Current)) + blockingObject = enumerator.Current; + } } // If there is no previous hitobject, allow the hit. @@ -76,17 +77,18 @@ namespace osu.Game.Rulesets.Osu.UI if (!IsHittable(hitObject, hitObject.HitObject.StartTime + hitObject.Result.TimeOffset)) throw new InvalidOperationException($"A {hitObject} was hit before it become hittable!"); - var enumerator = new HitObjectEnumerator(hitObjectContainer, hitObject.HitObject.StartTime); - - while (enumerator.MoveNext()) + using (var enumerator = new HitObjectEnumerator(hitObjectContainer, hitObject.HitObject.StartTime)) { - Debug.Assert(enumerator.Current != null); + while (enumerator.MoveNext()) + { + Debug.Assert(enumerator.Current != null); - if (enumerator.Current.Judged) - continue; + if (enumerator.Current.Judged) + continue; - if (hitObjectCanBlockFutureHits(enumerator.Current)) - ((DrawableOsuHitObject)enumerator.Current).MissForcefully(); + if (hitObjectCanBlockFutureHits(enumerator.Current)) + ((DrawableOsuHitObject)enumerator.Current).MissForcefully(); + } } } @@ -180,6 +182,7 @@ namespace osu.Game.Rulesets.Osu.UI public void Dispose() { + hitObjectEnumerator?.Dispose(); } } } From bbcbd7e3fbc790e91415bc96ffcfb93c63bcffc6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 17 Apr 2020 14:48:12 +0900 Subject: [PATCH 0268/1117] Simplify by removing custom enumerator --- osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.cs | 118 +++---------------- 1 file changed, 19 insertions(+), 99 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.cs b/osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.cs index 4bc7da4794..cd9838e7bf 100644 --- a/osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.cs +++ b/osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.cs @@ -2,9 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections; using System.Collections.Generic; -using System.Diagnostics; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables; @@ -39,15 +37,10 @@ namespace osu.Game.Rulesets.Osu.UI { DrawableHitObject blockingObject = null; - using (var enumerator = new HitObjectEnumerator(hitObjectContainer, hitObject.HitObject.StartTime)) + foreach (var obj in enumerateHitObjectsUpTo(hitObject.HitObject.StartTime)) { - while (enumerator.MoveNext()) - { - Debug.Assert(enumerator.Current != null); - - if (hitObjectCanBlockFutureHits(enumerator.Current)) - blockingObject = enumerator.Current; - } + if (hitObjectCanBlockFutureHits(obj)) + blockingObject = obj; } // If there is no previous hitobject, allow the hit. @@ -77,18 +70,13 @@ namespace osu.Game.Rulesets.Osu.UI if (!IsHittable(hitObject, hitObject.HitObject.StartTime + hitObject.Result.TimeOffset)) throw new InvalidOperationException($"A {hitObject} was hit before it become hittable!"); - using (var enumerator = new HitObjectEnumerator(hitObjectContainer, hitObject.HitObject.StartTime)) + foreach (var obj in enumerateHitObjectsUpTo(hitObject.HitObject.StartTime)) { - while (enumerator.MoveNext()) - { - Debug.Assert(enumerator.Current != null); + if (obj.Judged) + continue; - if (enumerator.Current.Judged) - continue; - - if (hitObjectCanBlockFutureHits(enumerator.Current)) - ((DrawableOsuHitObject)enumerator.Current).MissForcefully(); - } + if (hitObjectCanBlockFutureHits(obj)) + ((DrawableOsuHitObject)obj).MissForcefully(); } } @@ -99,90 +87,22 @@ namespace osu.Game.Rulesets.Osu.UI private static bool hitObjectCanBlockFutureHits(DrawableHitObject hitObject) => hitObject is DrawableHitCircle; - private struct HitObjectEnumerator : IEnumerator + private IEnumerable enumerateHitObjectsUpTo(double targetTime) { - private readonly IEnumerator hitObjectEnumerator; - private readonly double targetTime; - - private DrawableHitObject currentTopLevel; - private int currentNestedIndex; - - public HitObjectEnumerator(HitObjectContainer hitObjectContainer, double targetTime) + foreach (var obj in hitObjectContainer.AliveObjects) { - hitObjectEnumerator = hitObjectContainer.AliveObjects.GetEnumerator(); - this.targetTime = targetTime; + if (obj.HitObject.StartTime >= targetTime) + yield break; - currentTopLevel = null; - currentNestedIndex = -1; - Current = null; - } + yield return obj; - /// - /// Attempts to move to the next top-level or nested hitobject. - /// Stops when no such hitobject is found or until the hitobject start time reaches . - /// - /// Whether a new hitobject was moved to. - public bool MoveNext() - { - // If we don't already have a top-level hitobject, try to get one. - if (currentTopLevel == null) - return moveNextTopLevel(); + for (int i = 0; i < obj.NestedHitObjects.Count; i++) + { + if (obj.NestedHitObjects[i].HitObject.StartTime >= targetTime) + break; - // If we have a top-level hitobject, try to move to the next nested hitobject or otherwise move to the next top-level hitobject. - if (!moveNextNested()) - return moveNextTopLevel(); - - // Guaranteed by moveNextNested() to have a hitobject. - return true; - } - - /// - /// Attempts to move to the next top-level hitobject. - /// - /// Whether a new top-level hitobject was found. - private bool moveNextTopLevel() - { - currentNestedIndex = -1; - - hitObjectEnumerator.MoveNext(); - currentTopLevel = hitObjectEnumerator.Current; - - Current = currentTopLevel; - - return Current?.HitObject.StartTime < targetTime; - } - - /// - /// Attempts to move to the next nested hitobject in the current top-level hitobject. - /// - /// Whether a new nested hitobject was moved to. - private bool moveNextNested() - { - currentNestedIndex++; - if (currentNestedIndex >= currentTopLevel.NestedHitObjects.Count) - return false; - - Current = currentTopLevel.NestedHitObjects[currentNestedIndex]; - Debug.Assert(Current != null); - - return Current?.HitObject.StartTime < targetTime; - } - - public void Reset() - { - hitObjectEnumerator.Reset(); - currentTopLevel = null; - currentNestedIndex = -1; - Current = null; - } - - public DrawableHitObject Current { get; set; } - - object IEnumerator.Current => Current; - - public void Dispose() - { - hitObjectEnumerator?.Dispose(); + yield return obj.NestedHitObjects[i]; + } } } } From 69fb984e71fae2c371b19de763cbf8a80ff861d2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 17 Apr 2020 17:04:09 +0900 Subject: [PATCH 0269/1117] Remove EquivalentTo() and Equals() --- osu.Game/Beatmaps/ControlPoints/ControlPoint.cs | 11 +---------- .../Beatmaps/ControlPoints/DifficultyControlPoint.cs | 7 +++---- osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs | 10 +++++----- osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs | 9 ++++----- osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs | 9 ++------- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 7 ++++--- osu.Game/Graphics/Containers/BeatSyncedContainer.cs | 2 +- 7 files changed, 20 insertions(+), 35 deletions(-) diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs index 9599ad184b..f9bb3877d3 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs @@ -5,7 +5,7 @@ using System; namespace osu.Game.Beatmaps.ControlPoints { - public abstract class ControlPoint : IComparable, IEquatable + public abstract class ControlPoint : IComparable { /// /// The time at which the control point takes effect. @@ -18,13 +18,6 @@ namespace osu.Game.Beatmaps.ControlPoints public int CompareTo(ControlPoint other) => Time.CompareTo(other.Time); - /// - /// Whether this control point is equivalent to another, ignoring time. - /// - /// Another control point to compare with. - /// Whether equivalent. - public abstract bool EquivalentTo(ControlPoint other); - /// /// Whether this control point results in a meaningful change when placed after another. /// @@ -32,7 +25,5 @@ namespace osu.Game.Beatmaps.ControlPoints /// The time this control point will be placed at if it is added. /// Whether redundant. public abstract bool IsRedundant(ControlPoint existing, double time); - - public bool Equals(ControlPoint other) => Time == other?.Time && EquivalentTo(other); } } diff --git a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs index dc856b0a0a..42140462cb 100644 --- a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs @@ -27,9 +27,8 @@ namespace osu.Game.Beatmaps.ControlPoints set => SpeedMultiplierBindable.Value = value; } - public override bool EquivalentTo(ControlPoint other) => - other is DifficultyControlPoint otherTyped && otherTyped.SpeedMultiplier.Equals(SpeedMultiplier); - - public override bool IsRedundant(ControlPoint existing, double time) => EquivalentTo(existing); + public override bool IsRedundant(ControlPoint existing, double time) + => existing is DifficultyControlPoint existingDifficulty + && SpeedMultiplier == existingDifficulty.SpeedMultiplier; } } diff --git a/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs index d050f44ba4..f7a232c394 100644 --- a/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs @@ -35,10 +35,10 @@ namespace osu.Game.Beatmaps.ControlPoints set => KiaiModeBindable.Value = value; } - public override bool EquivalentTo(ControlPoint other) => - other is EffectControlPoint otherTyped && - KiaiMode == otherTyped.KiaiMode && OmitFirstBarLine == otherTyped.OmitFirstBarLine; - - public override bool IsRedundant(ControlPoint existing, double time) => !OmitFirstBarLine && EquivalentTo(existing); + public override bool IsRedundant(ControlPoint existing, double time) + => !OmitFirstBarLine + && existing is EffectControlPoint existingEffect + && KiaiMode == existingEffect.KiaiMode + && OmitFirstBarLine == existingEffect.OmitFirstBarLine; } } diff --git a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs index 38edbe70da..0fced16b4d 100644 --- a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs @@ -68,10 +68,9 @@ namespace osu.Game.Beatmaps.ControlPoints return newSampleInfo; } - public override bool EquivalentTo(ControlPoint other) => - other is SampleControlPoint otherTyped && - SampleBank == otherTyped.SampleBank && SampleVolume == otherTyped.SampleVolume; - - public override bool IsRedundant(ControlPoint existing, double time) => EquivalentTo(existing); + public override bool IsRedundant(ControlPoint existing, double time) + => existing is SampleControlPoint existingSample + && SampleBank == existingSample.SampleBank + && SampleVolume == existingSample.SampleVolume; } } diff --git a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs index 316c603ece..27f4662d49 100644 --- a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs @@ -48,12 +48,7 @@ namespace osu.Game.Beatmaps.ControlPoints /// public double BPM => 60000 / BeatLength; - public override bool EquivalentTo(ControlPoint other) => - other is TimingControlPoint otherTyped - && TimeSignature == otherTyped.TimeSignature && BeatLength.Equals(otherTyped.BeatLength); - - public override bool IsRedundant(ControlPoint existing, double time) => - EquivalentTo(existing) - && existing.Time == time; + // Timing points are never redundant as they can change the time signature. + public override bool IsRedundant(ControlPoint existing, double time) => false; } } diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index 561707f9ef..5fa1da111d 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -174,9 +174,10 @@ namespace osu.Game.Beatmaps.Formats return baseInfo; } - public override bool EquivalentTo(ControlPoint other) => - base.EquivalentTo(other) && other is LegacySampleControlPoint otherTyped && - CustomSampleBank == otherTyped.CustomSampleBank; + public override bool IsRedundant(ControlPoint existing, double time) + => base.IsRedundant(existing, time) + && existing is LegacySampleControlPoint existingSample + && CustomSampleBank == existingSample.CustomSampleBank; } } } diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index f36079682e..5a613d1a54 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -103,7 +103,7 @@ namespace osu.Game.Graphics.Containers TimeSinceLastBeat = beatLength - TimeUntilNextBeat; - if (timingPoint.Equals(lastTimingPoint) && beatIndex == lastBeat) + if (timingPoint == lastTimingPoint && beatIndex == lastBeat) return; using (BeginDelayedSequence(-TimeSinceLastBeat, true)) From 9aac98664ce9379938cea94e62dc3bb31df13a26 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 17 Apr 2020 17:06:12 +0900 Subject: [PATCH 0270/1117] Remove unnecessary time property --- osu.Game/Beatmaps/ControlPoints/ControlPoint.cs | 7 +++---- osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs | 2 +- osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs | 2 +- osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs | 2 +- osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs | 2 +- osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs | 2 +- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 4 ++-- 7 files changed, 10 insertions(+), 11 deletions(-) diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs index f9bb3877d3..a1822a1163 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs @@ -19,11 +19,10 @@ namespace osu.Game.Beatmaps.ControlPoints public int CompareTo(ControlPoint other) => Time.CompareTo(other.Time); /// - /// Whether this control point results in a meaningful change when placed after another. + /// Determines whether this results in a meaningful change when placed alongside another. /// /// An existing control point to compare with. - /// The time this control point will be placed at if it is added. - /// Whether redundant. - public abstract bool IsRedundant(ControlPoint existing, double time); + /// Whether this is redundant when placed alongside . + public abstract bool IsRedundant(ControlPoint existing); } } diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs index 37a3dbf592..8e4079f776 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs @@ -247,7 +247,7 @@ namespace osu.Game.Beatmaps.ControlPoints break; } - return newPoint.IsRedundant(existing, time); + return newPoint.IsRedundant(existing); } private void groupItemAdded(ControlPoint controlPoint) diff --git a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs index 42140462cb..2448b2b25c 100644 --- a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs @@ -27,7 +27,7 @@ namespace osu.Game.Beatmaps.ControlPoints set => SpeedMultiplierBindable.Value = value; } - public override bool IsRedundant(ControlPoint existing, double time) + public override bool IsRedundant(ControlPoint existing) => existing is DifficultyControlPoint existingDifficulty && SpeedMultiplier == existingDifficulty.SpeedMultiplier; } diff --git a/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs index f7a232c394..9b69147468 100644 --- a/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs @@ -35,7 +35,7 @@ namespace osu.Game.Beatmaps.ControlPoints set => KiaiModeBindable.Value = value; } - public override bool IsRedundant(ControlPoint existing, double time) + public override bool IsRedundant(ControlPoint existing) => !OmitFirstBarLine && existing is EffectControlPoint existingEffect && KiaiMode == existingEffect.KiaiMode diff --git a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs index 0fced16b4d..61851a00d7 100644 --- a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs @@ -68,7 +68,7 @@ namespace osu.Game.Beatmaps.ControlPoints return newSampleInfo; } - public override bool IsRedundant(ControlPoint existing, double time) + public override bool IsRedundant(ControlPoint existing) => existing is SampleControlPoint existingSample && SampleBank == existingSample.SampleBank && SampleVolume == existingSample.SampleVolume; diff --git a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs index 27f4662d49..1927dd6575 100644 --- a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs @@ -49,6 +49,6 @@ namespace osu.Game.Beatmaps.ControlPoints public double BPM => 60000 / BeatLength; // Timing points are never redundant as they can change the time signature. - public override bool IsRedundant(ControlPoint existing, double time) => false; + public override bool IsRedundant(ControlPoint existing) => false; } } diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index 5fa1da111d..556527bfd5 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -174,8 +174,8 @@ namespace osu.Game.Beatmaps.Formats return baseInfo; } - public override bool IsRedundant(ControlPoint existing, double time) - => base.IsRedundant(existing, time) + public override bool IsRedundant(ControlPoint existing) + => base.IsRedundant(existing) && existing is LegacySampleControlPoint existingSample && CustomSampleBank == existingSample.CustomSampleBank; } From 0fba93bf658d917286defb5b029fcf5bc0f1b566 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 17 Apr 2020 17:10:13 +0900 Subject: [PATCH 0271/1117] Add back null check --- osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs index 8e4079f776..d33a922a32 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs @@ -247,7 +247,7 @@ namespace osu.Game.Beatmaps.ControlPoints break; } - return newPoint.IsRedundant(existing); + return newPoint?.IsRedundant(existing) == true; } private void groupItemAdded(ControlPoint controlPoint) From 67bd7bfa3905aa96f21f2225a517e2e369e80540 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 17 Apr 2020 06:17:15 +0300 Subject: [PATCH 0272/1117] Add `CreateRuleset` in OsuTestScene for scenes that depend on it --- osu.Game/Tests/Visual/OsuTestScene.cs | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs index 5dc8714c07..8058a074ef 100644 --- a/osu.Game/Tests/Visual/OsuTestScene.cs +++ b/osu.Game/Tests/Visual/OsuTestScene.cs @@ -70,7 +70,18 @@ namespace osu.Game.Tests.Visual Beatmap.SetDefault(); Ruleset = Dependencies.Ruleset; - Ruleset.SetDefault(); + + var definedRuleset = CreateRuleset()?.RulesetInfo; + + if (definedRuleset != null) + { + // Set global ruleset bindable to the ruleset defined + // for this test scene and disallow changing it. + Ruleset.Value = definedRuleset; + Ruleset.Disabled = true; + } + else + Ruleset.SetDefault(); SelectedMods = Dependencies.Mods; SelectedMods.SetDefault(); @@ -124,6 +135,14 @@ namespace osu.Game.Tests.Visual [Resolved] protected AudioManager Audio { get; private set; } + /// + /// Creates the ruleset to be used for this test scene. + /// + /// + /// When testing against ruleset-specific components, this method must be overriden to their ruleset. + /// + protected virtual Ruleset CreateRuleset() => null; + protected virtual IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset); protected WorkingBeatmap CreateWorkingBeatmap(RulesetInfo ruleset) => @@ -135,7 +154,8 @@ namespace osu.Game.Tests.Visual [BackgroundDependencyLoader] private void load(RulesetStore rulesets) { - Ruleset.Value = rulesets.AvailableRulesets.First(); + if (!Ruleset.Disabled) + Ruleset.Value = rulesets.AvailableRulesets.First(); } protected override void Dispose(bool isDisposing) From 5fa6bcb5a3f73b080873f48ee34a7dcec0a9da58 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 17 Apr 2020 11:17:14 +0300 Subject: [PATCH 0273/1117] Move `SkinnableTestScene` into using the global `CreateRuleset` method --- .../CatchSkinnableTestScene.cs | 2 +- .../Skinning/ManiaSkinnableTestScene.cs | 4 ++-- .../OsuSkinnableTestScene.cs | 2 +- .../TaikoSkinnableTestScene.cs | 2 +- osu.Game/Tests/Visual/SkinnableTestScene.cs | 13 ++++++++----- 5 files changed, 13 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/CatchSkinnableTestScene.cs b/osu.Game.Rulesets.Catch.Tests/CatchSkinnableTestScene.cs index 0c46b078b5..c0060af74a 100644 --- a/osu.Game.Rulesets.Catch.Tests/CatchSkinnableTestScene.cs +++ b/osu.Game.Rulesets.Catch.Tests/CatchSkinnableTestScene.cs @@ -16,6 +16,6 @@ namespace osu.Game.Rulesets.Catch.Tests typeof(CatchLegacySkinTransformer), }; - protected override Ruleset CreateRulesetForSkinProvider() => new CatchRuleset(); + protected override Ruleset CreateRuleset() => new CatchRuleset(); } } diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/ManiaSkinnableTestScene.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/ManiaSkinnableTestScene.cs index a3c1d518c5..f41ba4db42 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/ManiaSkinnableTestScene.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/ManiaSkinnableTestScene.cs @@ -34,8 +34,6 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning typeof(ManiaSettingsSubsection) }; - protected override Ruleset CreateRulesetForSkinProvider() => new ManiaRuleset(); - protected ManiaSkinnableTestScene() { scrollingInfo.Direction.Value = ScrollingDirection.Down; @@ -60,6 +58,8 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning AddStep("change direction to up", () => scrollingInfo.Direction.Value = ScrollingDirection.Up); } + protected override Ruleset CreateRuleset() => new ManiaRuleset(); + private class TestScrollingInfo : IScrollingInfo { public readonly Bindable Direction = new Bindable(); diff --git a/osu.Game.Rulesets.Osu.Tests/OsuSkinnableTestScene.cs b/osu.Game.Rulesets.Osu.Tests/OsuSkinnableTestScene.cs index 90ebbd9f04..1458270193 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuSkinnableTestScene.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuSkinnableTestScene.cs @@ -16,6 +16,6 @@ namespace osu.Game.Rulesets.Osu.Tests typeof(OsuLegacySkinTransformer), }; - protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); + protected override Ruleset CreateRuleset() => new OsuRuleset(); } } diff --git a/osu.Game.Rulesets.Taiko.Tests/TaikoSkinnableTestScene.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoSkinnableTestScene.cs index 6db2a6907f..98e6c2ec52 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TaikoSkinnableTestScene.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TaikoSkinnableTestScene.cs @@ -16,6 +16,6 @@ namespace osu.Game.Rulesets.Taiko.Tests typeof(TaikoLegacySkinTransformer), }; - protected override Ruleset CreateRulesetForSkinProvider() => new TaikoRuleset(); + protected override Ruleset CreateRuleset() => new TaikoRuleset(); } } diff --git a/osu.Game/Tests/Visual/SkinnableTestScene.cs b/osu.Game/Tests/Visual/SkinnableTestScene.cs index ace24c0d7e..d648afd504 100644 --- a/osu.Game/Tests/Visual/SkinnableTestScene.cs +++ b/osu.Game/Tests/Visual/SkinnableTestScene.cs @@ -13,7 +13,6 @@ using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; using osu.Game.Beatmaps; using osu.Game.Graphics.Sprites; -using osu.Game.Rulesets; using osu.Game.Skinning; using osuTK; using osuTK.Graphics; @@ -30,11 +29,15 @@ namespace osu.Game.Tests.Visual protected SkinnableTestScene() : base(2, 3) { + // avoid running silently incorrectly. + if (CreateRuleset() == null) + { + throw new InvalidOperationException( + $"No ruleset provided, override {nameof(CreateRuleset)} to the ruleset belonging to the skinnable content." + + "This is required to add the legacy skin transformer for the content to behave as expected."); + } } - // Required to be part of the per-ruleset implementation to construct the newer version of the Ruleset. - protected abstract Ruleset CreateRulesetForSkinProvider(); - [BackgroundDependencyLoader] private void load(AudioManager audio, SkinManager skinManager) { @@ -107,7 +110,7 @@ namespace osu.Game.Tests.Visual { new OutlineBox { Alpha = autoSize ? 1 : 0 }, mainProvider.WithChild( - new SkinProvidingContainer(CreateRulesetForSkinProvider().CreateLegacySkinProvider(mainProvider, beatmap)) + new SkinProvidingContainer(Ruleset.Value.CreateInstance().CreateLegacySkinProvider(mainProvider, beatmap)) { Child = created, RelativeSizeAxes = !autoSize ? Axes.Both : Axes.None, From 92df4e3a9eb8ad56c6da99b088d0159a419c8110 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 17 Apr 2020 10:32:12 +0300 Subject: [PATCH 0274/1117] Remove `PlayerTestScene` constructor and use `CreateRuleset` method instead --- .../TestSceneAutoJuiceStream.cs | 7 +------ .../TestSceneBananaShower.cs | 8 +------ .../TestSceneCatchPlayer.cs | 10 ++++++--- .../TestSceneCatchStacker.cs | 8 +------ .../TestSceneHyperDash.cs | 8 +------ .../TestSceneJuiceStream.cs | 8 +------ .../TestSceneManiaPlayer.cs | 19 +++++++++++++++++ .../TestScenePlayer.cs | 15 ------------- .../TestSceneHitCircleLongCombo.cs | 8 +------ .../TestSceneOsuPlayer.cs | 10 ++++++--- .../TestSceneSkinFallbacks.cs | 3 +-- .../TestSceneSwellJudgements.cs | 8 +------ .../TestSceneTaikoPlayer.cs | 19 +++++++++++++++++ .../TestSceneTaikoSuddenDeath.cs | 7 +------ .../Gameplay/TestSceneHitObjectSamples.cs | 10 ++------- .../Visual/Gameplay/TestPlayerTestScene.cs | 16 ++++++++++++++ .../Gameplay/TestSceneGameplayRewinding.cs | 8 +------ .../Visual/Gameplay/TestScenePause.cs | 4 +--- .../Gameplay/TestScenePauseWhenInactive.cs | 8 +------ osu.Game/Tests/Visual/PlayerTestScene.cs | 21 +++++++------------ 20 files changed, 89 insertions(+), 116 deletions(-) create mode 100644 osu.Game.Rulesets.Mania.Tests/TestSceneManiaPlayer.cs delete mode 100644 osu.Game.Rulesets.Mania.Tests/TestScenePlayer.cs create mode 100644 osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayer.cs create mode 100644 osu.Game.Tests/Visual/Gameplay/TestPlayerTestScene.cs diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs index ed7bfb9a44..7c2304694f 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs @@ -12,13 +12,8 @@ using osuTK; namespace osu.Game.Rulesets.Catch.Tests { - public class TestSceneAutoJuiceStream : PlayerTestScene + public class TestSceneAutoJuiceStream : TestSceneCatchPlayer { - public TestSceneAutoJuiceStream() - : base(new CatchRuleset()) - { - } - protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) { var beatmap = new Beatmap diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneBananaShower.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneBananaShower.cs index 024c4cefb0..56f94e609f 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneBananaShower.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneBananaShower.cs @@ -8,12 +8,11 @@ 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.Tests.Visual; namespace osu.Game.Rulesets.Catch.Tests { [TestFixture] - public class TestSceneBananaShower : PlayerTestScene + public class TestSceneBananaShower : TestSceneCatchPlayer { public override IReadOnlyList RequiredTypes => new[] { @@ -26,11 +25,6 @@ namespace osu.Game.Rulesets.Catch.Tests typeof(DrawableCatchRuleset), }; - public TestSceneBananaShower() - : base(new CatchRuleset()) - { - } - [Test] public void TestBananaShower() { diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayer.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayer.cs index 9836a7811a..722f3b5a3b 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayer.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayer.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.Collections.Generic; using NUnit.Framework; using osu.Game.Tests.Visual; @@ -9,9 +11,11 @@ namespace osu.Game.Rulesets.Catch.Tests [TestFixture] public class TestSceneCatchPlayer : PlayerTestScene { - public TestSceneCatchPlayer() - : base(new CatchRuleset()) + public override IReadOnlyList RequiredTypes => new[] { - } + typeof(CatchRuleset), + }; + + protected override Ruleset CreateRuleset() => new CatchRuleset(); } } diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchStacker.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchStacker.cs index 9ce46ad6ba..44672b6526 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchStacker.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchStacker.cs @@ -4,18 +4,12 @@ using NUnit.Framework; using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.Objects; -using osu.Game.Tests.Visual; namespace osu.Game.Rulesets.Catch.Tests { [TestFixture] - public class TestSceneCatchStacker : PlayerTestScene + public class TestSceneCatchStacker : TestSceneCatchPlayer { - public TestSceneCatchStacker() - : base(new CatchRuleset()) - { - } - protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) { var beatmap = new Beatmap diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs index 49ff9df4d7..75b8b68c14 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs @@ -10,24 +10,18 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Objects; -using osu.Game.Tests.Visual; using osuTK; namespace osu.Game.Rulesets.Catch.Tests { [TestFixture] - public class TestSceneHyperDash : PlayerTestScene + public class TestSceneHyperDash : TestSceneCatchPlayer { public override IReadOnlyList RequiredTypes => new[] { typeof(CatcherArea), }; - public TestSceneHyperDash() - : base(new CatchRuleset()) - { - } - protected override bool Autoplay => true; [Test] diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneJuiceStream.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneJuiceStream.cs index cbc87459e1..ffcf61a4bf 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneJuiceStream.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneJuiceStream.cs @@ -7,18 +7,12 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; -using osu.Game.Tests.Visual; using osuTK; namespace osu.Game.Rulesets.Catch.Tests { - public class TestSceneJuiceStream : PlayerTestScene + public class TestSceneJuiceStream : TestSceneCatchPlayer { - public TestSceneJuiceStream() - : base(new CatchRuleset()) - { - } - [Test] public void TestJuiceStreamEndingCombo() { diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaPlayer.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaPlayer.cs new file mode 100644 index 0000000000..11663605e2 --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaPlayer.cs @@ -0,0 +1,19 @@ +// 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.Tests.Visual; + +namespace osu.Game.Rulesets.Mania.Tests +{ + public class TestSceneManiaPlayer : PlayerTestScene + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(ManiaRuleset), + }; + + protected override Ruleset CreateRuleset() => new ManiaRuleset(); + } +} diff --git a/osu.Game.Rulesets.Mania.Tests/TestScenePlayer.cs b/osu.Game.Rulesets.Mania.Tests/TestScenePlayer.cs deleted file mode 100644 index cd25d162d0..0000000000 --- a/osu.Game.Rulesets.Mania.Tests/TestScenePlayer.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.Tests.Visual; - -namespace osu.Game.Rulesets.Mania.Tests -{ - public class TestScenePlayer : PlayerTestScene - { - public TestScenePlayer() - : base(new ManiaRuleset()) - { - } - } -} diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLongCombo.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLongCombo.cs index b99cd523ff..8cf29ddfbf 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLongCombo.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLongCombo.cs @@ -4,19 +4,13 @@ using NUnit.Framework; using osu.Game.Beatmaps; using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Tests.Visual; using osuTK; namespace osu.Game.Rulesets.Osu.Tests { [TestFixture] - public class TestSceneHitCircleLongCombo : PlayerTestScene + public class TestSceneHitCircleLongCombo : TestSceneOsuPlayer { - public TestSceneHitCircleLongCombo() - : base(new OsuRuleset()) - { - } - protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) { var beatmap = new Beatmap diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuPlayer.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuPlayer.cs index 0a33b09ba8..102f8bf841 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuPlayer.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuPlayer.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.Collections.Generic; using NUnit.Framework; using osu.Game.Tests.Visual; @@ -9,9 +11,11 @@ namespace osu.Game.Rulesets.Osu.Tests [TestFixture] public class TestSceneOsuPlayer : PlayerTestScene { - public TestSceneOsuPlayer() - : base(new OsuRuleset()) + public override IReadOnlyList RequiredTypes => new[] { - } + typeof(OsuRuleset), + }; + + protected override Ruleset CreateRuleset() => new OsuRuleset(); } } diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs index d39e24fc1f..b357e20ee8 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs @@ -25,13 +25,12 @@ using osu.Game.Tests.Visual; namespace osu.Game.Rulesets.Osu.Tests { [TestFixture] - public class TestSceneSkinFallbacks : PlayerTestScene + public class TestSceneSkinFallbacks : TestSceneOsuPlayer { private readonly TestSource testUserSkin; private readonly TestSource testBeatmapSkin; public TestSceneSkinFallbacks() - : base(new OsuRuleset()) { testUserSkin = new TestSource("user"); testBeatmapSkin = new TestSource("beatmap"); diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneSwellJudgements.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneSwellJudgements.cs index 303f0163b1..965cde0f3f 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneSwellJudgements.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneSwellJudgements.cs @@ -5,17 +5,11 @@ using System.Linq; using NUnit.Framework; using osu.Game.Beatmaps; using osu.Game.Rulesets.Taiko.Objects; -using osu.Game.Tests.Visual; namespace osu.Game.Rulesets.Taiko.Tests { - public class TestSceneSwellJudgements : PlayerTestScene + public class TestSceneSwellJudgements : TestSceneTaikoPlayer { - public TestSceneSwellJudgements() - : base(new TaikoRuleset()) - { - } - [Test] public void TestZeroTickTimeOffsets() { diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayer.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayer.cs new file mode 100644 index 0000000000..4c5ab7eabf --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayer.cs @@ -0,0 +1,19 @@ +// 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.Tests.Visual; + +namespace osu.Game.Rulesets.Taiko.Tests +{ + public class TestSceneTaikoPlayer : PlayerTestScene + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(TaikoRuleset) + }; + + protected override Ruleset CreateRuleset() => new TaikoRuleset(); + } +} diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoSuddenDeath.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoSuddenDeath.cs index 2ab041e191..aaa634648a 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoSuddenDeath.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoSuddenDeath.cs @@ -11,13 +11,8 @@ using osu.Game.Tests.Visual; namespace osu.Game.Rulesets.Taiko.Tests { - public class TestSceneTaikoSuddenDeath : PlayerTestScene + public class TestSceneTaikoSuddenDeath : TestSceneTaikoPlayer { - public TestSceneTaikoSuddenDeath() - : base(new TaikoRuleset()) - { - } - protected override bool AllowFail => true; protected override TestPlayer CreatePlayer(Ruleset ruleset) diff --git a/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs b/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs index f611f2717e..7d3d8b7f16 100644 --- a/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs +++ b/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs @@ -16,17 +16,16 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.Formats; using osu.Game.IO; using osu.Game.Rulesets; -using osu.Game.Rulesets.Osu; using osu.Game.Skinning; using osu.Game.Storyboards; using osu.Game.Tests.Resources; -using osu.Game.Tests.Visual; +using osu.Game.Tests.Visual.Gameplay; using osu.Game.Users; namespace osu.Game.Tests.Gameplay { [HeadlessTest] - public class TestSceneHitObjectSamples : PlayerTestScene + public class TestSceneHitObjectSamples : TestPlayerTestScene { private readonly SkinInfo userSkinInfo = new SkinInfo(); @@ -44,11 +43,6 @@ namespace osu.Game.Tests.Gameplay protected override bool HasCustomSteps => true; - public TestSceneHitObjectSamples() - : base(new OsuRuleset()) - { - } - private SkinSourceDependencyContainer dependencies; protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) diff --git a/osu.Game.Tests/Visual/Gameplay/TestPlayerTestScene.cs b/osu.Game.Tests/Visual/Gameplay/TestPlayerTestScene.cs new file mode 100644 index 0000000000..2130171449 --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestPlayerTestScene.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 osu.Game.Rulesets; +using osu.Game.Rulesets.Osu; + +namespace osu.Game.Tests.Visual.Gameplay +{ + /// + /// A with an arbitrary ruleset value to test with. + /// + public abstract class TestPlayerTestScene : PlayerTestScene + { + protected override Ruleset CreateRuleset() => new OsuRuleset(); + } +} diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs index 310746d179..744eeed022 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs @@ -10,23 +10,17 @@ using osu.Framework.Utils; using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Rulesets; -using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Storyboards; using osuTK; namespace osu.Game.Tests.Visual.Gameplay { - public class TestSceneGameplayRewinding : PlayerTestScene + public class TestSceneGameplayRewinding : TestPlayerTestScene { [Resolved] private AudioManager audioManager { get; set; } - public TestSceneGameplayRewinding() - : base(new OsuRuleset()) - { - } - private Track track; protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs index 944e6ca6be..411265d600 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs @@ -10,14 +10,13 @@ using osu.Framework.Testing; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Cursor; using osu.Game.Rulesets; -using osu.Game.Rulesets.Osu; using osu.Game.Screens.Play; using osuTK; using osuTK.Input; namespace osu.Game.Tests.Visual.Gameplay { - public class TestScenePause : PlayerTestScene + public class TestScenePause : TestPlayerTestScene { protected new PausePlayer Player => (PausePlayer)base.Player; @@ -26,7 +25,6 @@ namespace osu.Game.Tests.Visual.Gameplay protected override Container Content => content; public TestScenePause() - : base(new OsuRuleset()) { base.Content.Add(content = new MenuCursorContainer { RelativeSizeAxes = Axes.Both }); } diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePauseWhenInactive.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePauseWhenInactive.cs index a83320048b..20911bfa4d 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePauseWhenInactive.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePauseWhenInactive.cs @@ -8,12 +8,11 @@ using osu.Framework.Platform; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Rulesets; -using osu.Game.Rulesets.Osu; namespace osu.Game.Tests.Visual.Gameplay { [HeadlessTest] // we alter unsafe properties on the game host to test inactive window state. - public class TestScenePauseWhenInactive : PlayerTestScene + public class TestScenePauseWhenInactive : TestPlayerTestScene { protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) { @@ -27,11 +26,6 @@ namespace osu.Game.Tests.Visual.Gameplay [Resolved] private GameHost host { get; set; } - public TestScenePauseWhenInactive() - : base(new OsuRuleset()) - { - } - [Test] public void TestDoesntPauseDuringIntro() { diff --git a/osu.Game/Tests/Visual/PlayerTestScene.cs b/osu.Game/Tests/Visual/PlayerTestScene.cs index 9e852719e0..f5e78fbbd1 100644 --- a/osu.Game/Tests/Visual/PlayerTestScene.cs +++ b/osu.Game/Tests/Visual/PlayerTestScene.cs @@ -19,15 +19,8 @@ namespace osu.Game.Tests.Visual /// protected virtual bool HasCustomSteps { get; } = false; - private readonly Ruleset ruleset; - protected TestPlayer Player; - protected PlayerTestScene(Ruleset ruleset) - { - this.ruleset = ruleset; - } - protected OsuConfigManager LocalConfig; [BackgroundDependencyLoader] @@ -53,7 +46,7 @@ namespace osu.Game.Tests.Visual action?.Invoke(); - AddStep(ruleset.RulesetInfo.Name, LoadPlayer); + AddStep(CreateRuleset().RulesetInfo.Name, LoadPlayer); AddUntilStep("player loaded", () => Player.IsLoaded && Player.Alpha == 1); } @@ -63,28 +56,28 @@ namespace osu.Game.Tests.Visual protected void LoadPlayer() { - var beatmap = CreateBeatmap(ruleset.RulesetInfo); + var beatmap = CreateBeatmap(Ruleset.Value); Beatmap.Value = CreateWorkingBeatmap(beatmap); - Ruleset.Value = ruleset.RulesetInfo; - SelectedMods.Value = Array.Empty(); + var rulesetInstance = Ruleset.Value.CreateInstance(); + if (!AllowFail) { - var noFailMod = ruleset.GetAllMods().FirstOrDefault(m => m is ModNoFail); + var noFailMod = rulesetInstance.GetAllMods().FirstOrDefault(m => m is ModNoFail); if (noFailMod != null) SelectedMods.Value = new[] { noFailMod }; } if (Autoplay) { - var mod = ruleset.GetAutoplayMod(); + var mod = rulesetInstance.GetAutoplayMod(); if (mod != null) SelectedMods.Value = SelectedMods.Value.Concat(mod.Yield()).ToArray(); } - Player = CreatePlayer(ruleset); + Player = CreatePlayer(rulesetInstance); LoadScreen(Player); } From 155bc8b49a08842297cf1a4eb1b4d9e36d799b55 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 17 Apr 2020 10:56:01 +0300 Subject: [PATCH 0275/1117] Remove `ModTestScene` ruleset parameter on constructor and use `CreateRuleset` instead --- .../Mods/TestSceneCatchModPerfect.cs | 4 +++- .../Mods/TestSceneManiaModPerfect.cs | 4 +++- .../Mods/TestSceneOsuModDifficultyAdjust.cs | 7 ++----- .../Mods/TestSceneOsuModDoubleTime.cs | 7 ++----- osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModPerfect.cs | 4 +++- .../TestSceneMissHitWindowJudgements.cs | 7 ++----- .../Mods/TestSceneTaikoModPerfect.cs | 4 +++- osu.Game/Tests/Visual/ModPerfectTestScene.cs | 7 ++----- osu.Game/Tests/Visual/ModTestScene.cs | 5 ----- 9 files changed, 20 insertions(+), 29 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModPerfect.cs b/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModPerfect.cs index 47e91e50d4..1e69a3f1b6 100644 --- a/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModPerfect.cs +++ b/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModPerfect.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Mods public class TestSceneCatchModPerfect : ModPerfectTestScene { public TestSceneCatchModPerfect() - : base(new CatchRuleset(), new CatchModPerfect()) + : base(new CatchModPerfect()) { } @@ -50,5 +50,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Mods // We only care about testing misses, hits are tested via JuiceStream [TestCase(true)] public void TestTinyDroplet(bool shouldMiss) => CreateHitObjectTest(new HitObjectTestData(new TinyDroplet { StartTime = 1000 }), shouldMiss); + + protected override Ruleset CreateRuleset() => new CatchRuleset(); } } diff --git a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModPerfect.cs b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModPerfect.cs index 607d42a1bb..72ef58ec73 100644 --- a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModPerfect.cs +++ b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModPerfect.cs @@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods public class TestSceneManiaModPerfect : ModPerfectTestScene { public TestSceneManiaModPerfect() - : base(new ManiaRuleset(), new ManiaModPerfect()) + : base(new ManiaModPerfect()) { } @@ -22,5 +22,7 @@ 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); + + protected override Ruleset CreateRuleset() => new ManiaRuleset(); } } diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModDifficultyAdjust.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModDifficultyAdjust.cs index 69415b70e3..6c5949ca85 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModDifficultyAdjust.cs @@ -15,11 +15,6 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods { public class TestSceneOsuModDifficultyAdjust : ModTestScene { - public TestSceneOsuModDifficultyAdjust() - : base(new OsuRuleset()) - { - } - [Test] public void TestNoAdjustment() => CreateModTest(new ModTestData { @@ -82,5 +77,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods { return Player.ScoreProcessor.JudgedHits >= 2; } + + protected override Ruleset CreateRuleset() => new OsuRuleset(); } } diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModDoubleTime.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModDoubleTime.cs index dcf19ad993..c61ef2724b 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModDoubleTime.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModDoubleTime.cs @@ -10,11 +10,6 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods { public class TestSceneOsuModDoubleTime : ModTestScene { - public TestSceneOsuModDoubleTime() - : base(new OsuRuleset()) - { - } - [TestCase(0.5)] [TestCase(1.01)] [TestCase(1.5)] @@ -31,5 +26,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods Precision.AlmostEquals(Player.GameplayClockContainer.GameplayClock.Rate, mod.SpeedChange.Value) }); } + + protected override Ruleset CreateRuleset() => new OsuRuleset(); } } diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModPerfect.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModPerfect.cs index b03a894085..ddbbf9554c 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModPerfect.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModPerfect.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods public class TestSceneOsuModPerfect : ModPerfectTestScene { public TestSceneOsuModPerfect() - : base(new OsuRuleset(), new OsuModPerfect()) + : base(new OsuModPerfect()) { } @@ -48,5 +48,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods CreateHitObjectTest(new HitObjectTestData(spinner), shouldMiss); } + + protected override Ruleset CreateRuleset() => new OsuRuleset(); } } diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneMissHitWindowJudgements.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneMissHitWindowJudgements.cs index 5f3596976d..13457ccaf9 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneMissHitWindowJudgements.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneMissHitWindowJudgements.cs @@ -19,11 +19,6 @@ namespace osu.Game.Rulesets.Osu.Tests { public class TestSceneMissHitWindowJudgements : ModTestScene { - public TestSceneMissHitWindowJudgements() - : base(new OsuRuleset()) - { - } - [Test] public void TestMissViaEarlyHit() { @@ -66,6 +61,8 @@ namespace osu.Game.Rulesets.Osu.Tests }); } + protected override Ruleset CreateRuleset() => new OsuRuleset(); + private class TestAutoMod : OsuModAutoplay { public override Score CreateReplayScore(IBeatmap beatmap) => new Score diff --git a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModPerfect.cs b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModPerfect.cs index 26c90ad295..a9c962bfa0 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModPerfect.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModPerfect.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Mods public class TestSceneTaikoModPerfect : ModPerfectTestScene { public TestSceneTaikoModPerfect() - : base(new TestTaikoRuleset(), new TaikoModPerfect()) + : base(new TaikoModPerfect()) { } @@ -29,6 +29,8 @@ namespace osu.Game.Rulesets.Taiko.Tests.Mods [TestCase(true)] public void TestSwell(bool shouldMiss) => CreateHitObjectTest(new HitObjectTestData(new Swell { StartTime = 1000, EndTime = 3000 }), shouldMiss); + protected override Ruleset CreateRuleset() => new TestTaikoRuleset(); + private class TestTaikoRuleset : TaikoRuleset { public override HealthProcessor CreateHealthProcessor(double drainStartTime) => new TestTaikoHealthProcessor(); diff --git a/osu.Game/Tests/Visual/ModPerfectTestScene.cs b/osu.Game/Tests/Visual/ModPerfectTestScene.cs index 798947eb40..3565fe751b 100644 --- a/osu.Game/Tests/Visual/ModPerfectTestScene.cs +++ b/osu.Game/Tests/Visual/ModPerfectTestScene.cs @@ -10,13 +10,10 @@ namespace osu.Game.Tests.Visual { public abstract class ModPerfectTestScene : ModTestScene { - private readonly Ruleset ruleset; private readonly ModPerfect mod; - protected ModPerfectTestScene(Ruleset ruleset, ModPerfect mod) - : base(ruleset) + protected ModPerfectTestScene(ModPerfect mod) { - this.ruleset = ruleset; this.mod = mod; } @@ -25,7 +22,7 @@ namespace osu.Game.Tests.Visual Mod = mod, Beatmap = new Beatmap { - BeatmapInfo = { Ruleset = ruleset.RulesetInfo }, + BeatmapInfo = { Ruleset = Ruleset.Value }, HitObjects = { testData.HitObject } }, Autoplay = !shouldMiss, diff --git a/osu.Game/Tests/Visual/ModTestScene.cs b/osu.Game/Tests/Visual/ModTestScene.cs index 8b41fb5075..c198d6b52c 100644 --- a/osu.Game/Tests/Visual/ModTestScene.cs +++ b/osu.Game/Tests/Visual/ModTestScene.cs @@ -19,11 +19,6 @@ namespace osu.Game.Tests.Visual typeof(ModTestScene) }; - protected ModTestScene(Ruleset ruleset) - : base(ruleset) - { - } - private ModTestData currentTestData; protected void CreateModTest(ModTestData testData) => CreateTest(() => From 7f791dcdf04d3434ea7275de20fd8d369060b062 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 17 Apr 2020 10:57:58 +0300 Subject: [PATCH 0276/1117] Re-enable ruleset bindable before setting defined ruleset in case it's disabled Happens on cases like restarting the test scene by clicking directly on it on the browser (*where it for some reason reloads the entire test scene*) --- osu.Game/Tests/Visual/OsuTestScene.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs index 8058a074ef..25ac768272 100644 --- a/osu.Game/Tests/Visual/OsuTestScene.cs +++ b/osu.Game/Tests/Visual/OsuTestScene.cs @@ -75,6 +75,10 @@ namespace osu.Game.Tests.Visual if (definedRuleset != null) { + // re-enable the bindable in case it was disabled. + // happens when restarting current test scene. + Ruleset.Disabled = false; + // Set global ruleset bindable to the ruleset defined // for this test scene and disallow changing it. Ruleset.Value = definedRuleset; From 5833a7ac913af77a8c8d801ffd5d117b9861930c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Apr 2020 18:50:58 +0900 Subject: [PATCH 0277/1117] Fix presenting new ruleset and beatmap at once causing wedge display desync --- .../SongSelect/TestScenePlaySongSelect.cs | 62 +++++++++++++++++++ osu.Game/Screens/Select/SongSelect.cs | 6 +- 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 4405c75744..39e04ed39a 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -359,6 +359,68 @@ namespace osu.Game.Tests.Visual.SongSelect AddUntilStep("no selection", () => songSelect.Carousel.SelectedBeatmap == null); } + [Test] + public void TestPresentNewRulesetNewBeatmap() + { + createSongSelect(); + changeRuleset(2); + + addRulesetImportStep(2); + AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap.RulesetID == 2); + + addRulesetImportStep(0); + addRulesetImportStep(0); + addRulesetImportStep(0); + + BeatmapInfo target = null; + + AddStep("select beatmap/ruleset externally", () => + { + target = manager.GetAllUsableBeatmapSets() + .Last(b => b.Beatmaps.Any(bi => bi.RulesetID == 0)).Beatmaps.Last(); + + Ruleset.Value = rulesets.AvailableRulesets.First(r => r.ID == 0); + Beatmap.Value = manager.GetWorkingBeatmap(target); + }); + + AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap.Equals(target)); + + // this is an important check, to make sure updateComponentFromBeatmap() was actually run + AddUntilStep("selection shown on wedge", () => songSelect.CurrentBeatmapDetailsBeatmap.BeatmapInfo == target); + } + + [Test] + public void TestPresentNewBeatmapNewRuleset() + { + createSongSelect(); + changeRuleset(2); + + addRulesetImportStep(2); + AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap.RulesetID == 2); + + addRulesetImportStep(0); + addRulesetImportStep(0); + addRulesetImportStep(0); + + BeatmapInfo target = null; + + AddStep("select beatmap/ruleset externally", () => + { + target = manager.GetAllUsableBeatmapSets() + .Last(b => b.Beatmaps.Any(bi => bi.RulesetID == 0)).Beatmaps.Last(); + + Beatmap.Value = manager.GetWorkingBeatmap(target); + Ruleset.Value = rulesets.AvailableRulesets.First(r => r.ID == 0); + }); + + AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap.Equals(target)); + + AddUntilStep("has correct ruleset", () => Ruleset.Value.ID == 0); + + // this is an important check, to make sure updateComponentFromBeatmap() was actually run + AddUntilStep("selection shown on wedge", () => songSelect.CurrentBeatmapDetailsBeatmap.BeatmapInfo == target); + } + [Test] public void TestRulesetChangeResetsMods() { diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 5bc2e1aa56..9d3dc58a26 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -448,8 +448,10 @@ namespace osu.Game.Screens.Select { Mods.Value = Array.Empty(); - // required to return once in order to have the carousel in a good state. - // if the ruleset changed, the rest of the selection update will happen via updateSelectedRuleset. + // the ruleset transfer may cause a deselection of the current beatmap (due to incompatibility). + // this can happen via Carousel.FlushPendingFilterOperations(). + // to ensure a good state, re-transfer no-debounce values. + performUpdateSelected(); return; } From 58a1c6e17186084345feedbd1c5b0d44c2ce8695 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Apr 2020 19:52:58 +0900 Subject: [PATCH 0278/1117] Reapply taiko visibility hack at a higher level --- .../Objects/Drawables/DrawableDrumRoll.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs index 0a6f462607..99f48afff0 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs @@ -15,6 +15,7 @@ using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; using osu.Game.Skinning; +using osuTK; namespace osu.Game.Rulesets.Taiko.Objects.Drawables { @@ -137,6 +138,14 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables } } + protected override void Update() + { + base.Update(); + + OriginPosition = new Vector2(DrawHeight); + Content.X = DrawHeight / 2; + } + protected override DrawableStrongNestedHit CreateStrongHit(StrongHitObject hitObject) => new StrongNestedHit(hitObject, this); private void updateColour() From 5f3ed3e93aab472b9d748d857e4394ec13843742 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Apr 2020 22:25:24 +0900 Subject: [PATCH 0279/1117] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 723844155f..d2bdbc8b61 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 76f7a030f9..5facb04117 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -23,7 +23,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 7a487a6430..dda1ee5c42 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -80,7 +80,7 @@ - + From 61e3491e603daf0a497ec988318043001a4e068a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 18 Apr 2020 12:57:09 +0900 Subject: [PATCH 0280/1117] Fix hard crash in editor on legacy modes without encoder implementation --- osu.Game/Screens/Edit/EditorChangeHandler.cs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/EditorChangeHandler.cs b/osu.Game/Screens/Edit/EditorChangeHandler.cs index 1553c2d2ef..ac889500b4 100644 --- a/osu.Game/Screens/Edit/EditorChangeHandler.cs +++ b/osu.Game/Screens/Edit/EditorChangeHandler.cs @@ -82,12 +82,19 @@ namespace osu.Game.Screens.Edit if (savedStates.Count > MAX_SAVED_STATES) savedStates.RemoveAt(0); - using (var stream = new MemoryStream()) + try { - using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) - new LegacyBeatmapEncoder(editorBeatmap).Encode(sw); + using (var stream = new MemoryStream()) + { + using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) + new LegacyBeatmapEncoder(editorBeatmap).Encode(sw); - savedStates.Add(stream.ToArray()); + savedStates.Add(stream.ToArray()); + } + } + catch (NotImplementedException) + { + // some rulesets don't have encoder implementations yet. } currentState = savedStates.Count - 1; From c00a386ff681a3ba4a037de6654bd1fd199d6770 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 18 Apr 2020 21:46:04 +0900 Subject: [PATCH 0281/1117] Remove exceptions instead --- .../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 21 +------------------ osu.Game/Screens/Edit/EditorChangeHandler.cs | 15 ++++--------- 2 files changed, 5 insertions(+), 31 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 12f2c58e35..8d9dfc318a 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -194,20 +194,7 @@ namespace osu.Game.Beatmaps.Formats handleOsuHitObject(writer, h); break; - case 1: - foreach (var h in beatmap.HitObjects) - handleTaikoHitObject(writer, h); - break; - - case 2: - foreach (var h in beatmap.HitObjects) - handleCatchHitObject(writer, h); - break; - - case 3: - foreach (var h in beatmap.HitObjects) - handleManiaHitObject(writer, h); - break; + // TODO: implement other legacy rulesets } } @@ -328,12 +315,6 @@ namespace osu.Game.Beatmaps.Formats } } - private void handleTaikoHitObject(TextWriter writer, HitObject hitObject) => throw new NotImplementedException(); - - private void handleCatchHitObject(TextWriter writer, HitObject hitObject) => throw new NotImplementedException(); - - private void handleManiaHitObject(TextWriter writer, HitObject hitObject) => throw new NotImplementedException(); - private string getSampleBank(IList samples, bool banksOnly = false, bool zeroBanks = false) { LegacySampleBank normalBank = toLegacySampleBank(samples.SingleOrDefault(s => s.Name == HitSampleInfo.HIT_NORMAL)?.Bank); diff --git a/osu.Game/Screens/Edit/EditorChangeHandler.cs b/osu.Game/Screens/Edit/EditorChangeHandler.cs index ac889500b4..1553c2d2ef 100644 --- a/osu.Game/Screens/Edit/EditorChangeHandler.cs +++ b/osu.Game/Screens/Edit/EditorChangeHandler.cs @@ -82,19 +82,12 @@ namespace osu.Game.Screens.Edit if (savedStates.Count > MAX_SAVED_STATES) savedStates.RemoveAt(0); - try + using (var stream = new MemoryStream()) { - using (var stream = new MemoryStream()) - { - using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) - new LegacyBeatmapEncoder(editorBeatmap).Encode(sw); + using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) + new LegacyBeatmapEncoder(editorBeatmap).Encode(sw); - savedStates.Add(stream.ToArray()); - } - } - catch (NotImplementedException) - { - // some rulesets don't have encoder implementations yet. + savedStates.Add(stream.ToArray()); } currentState = savedStates.Count - 1; From 6b16908c05bafd95e069b986d5efd6d73631875f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 18 Apr 2020 21:51:37 +0900 Subject: [PATCH 0282/1117] Move todo to appease dotnet-format --- osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 8d9dfc318a..fe63eec3f9 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -187,14 +187,13 @@ namespace osu.Game.Beatmaps.Formats writer.WriteLine("[HitObjects]"); + // TODO: implement other legacy rulesets switch (beatmap.BeatmapInfo.RulesetID) { case 0: foreach (var h in beatmap.HitObjects) handleOsuHitObject(writer, h); break; - - // TODO: implement other legacy rulesets } } From fc6c245de5dc362489706fbae2b1c299260f9fb8 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 19 Apr 2020 05:36:04 +0300 Subject: [PATCH 0283/1117] Replace all judged event logic with HasCompleted bindable --- .../TestSceneHoldNoteInput.cs | 8 +------- .../TestSceneOutOfOrderHits.cs | 8 +------- .../TestSceneSliderInput.cs | 8 +------- .../TestSceneSwellJudgements.cs | 2 +- osu.Game/Rulesets/Scoring/JudgementProcessor.cs | 17 +++++++++-------- osu.Game/Screens/Play/BreakTracker.cs | 2 +- osu.Game/Tests/Visual/ModPerfectTestScene.cs | 2 +- 7 files changed, 15 insertions(+), 32 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs index 7b0cf40d45..0d13b85901 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs @@ -27,7 +27,6 @@ namespace osu.Game.Rulesets.Mania.Tests private const double time_after_tail = 5250; private List judgementResults; - private bool allJudgedFired; /// /// -----[ ]----- @@ -283,20 +282,15 @@ namespace osu.Game.Rulesets.Mania.Tests { if (currentPlayer == p) judgementResults.Add(result); }; - p.ScoreProcessor.AllJudged += () => - { - if (currentPlayer == p) allJudgedFired = true; - }; }; LoadScreen(currentPlayer = p); - allJudgedFired = false; judgementResults = new List(); }); AddUntilStep("Beatmap at 0", () => Beatmap.Value.Track.CurrentTime == 0); AddUntilStep("Wait until player is loaded", () => currentPlayer.IsCurrentScreen()); - AddUntilStep("Wait for all judged", () => allJudgedFired); + AddUntilStep("Wait for completion", () => currentPlayer.ScoreProcessor.HasCompleted.Value); } private class ScoreAccessibleReplayPlayer : ReplayPlayer diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOutOfOrderHits.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOutOfOrderHits.cs index d6858f831e..91d5e04f6f 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneOutOfOrderHits.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOutOfOrderHits.cs @@ -316,7 +316,6 @@ namespace osu.Game.Rulesets.Osu.Tests private ScoreAccessibleReplayPlayer currentPlayer; private List judgementResults; - private bool allJudgedFired; private void performTest(List hitObjects, List frames) { @@ -342,20 +341,15 @@ namespace osu.Game.Rulesets.Osu.Tests { if (currentPlayer == p) judgementResults.Add(result); }; - p.ScoreProcessor.AllJudged += () => - { - if (currentPlayer == p) allJudgedFired = true; - }; }; LoadScreen(currentPlayer = p); - allJudgedFired = false; judgementResults = new List(); }); AddUntilStep("Beatmap at 0", () => Beatmap.Value.Track.CurrentTime == 0); AddUntilStep("Wait until player is loaded", () => currentPlayer.IsCurrentScreen()); - AddUntilStep("Wait for all judged", () => allJudgedFired); + AddUntilStep("Wait for completion", () => currentPlayer.ScoreProcessor.HasCompleted.Value); } private class TestHitCircle : HitCircle diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs index 67e1b77770..b0c2e56c3e 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs @@ -47,7 +47,6 @@ namespace osu.Game.Rulesets.Osu.Tests private const double time_slider_end = 4000; private List judgementResults; - private bool allJudgedFired; /// /// Scenario: @@ -375,20 +374,15 @@ namespace osu.Game.Rulesets.Osu.Tests { if (currentPlayer == p) judgementResults.Add(result); }; - p.ScoreProcessor.AllJudged += () => - { - if (currentPlayer == p) allJudgedFired = true; - }; }; LoadScreen(currentPlayer = p); - allJudgedFired = false; judgementResults = new List(); }); AddUntilStep("Beatmap at 0", () => Beatmap.Value.Track.CurrentTime == 0); AddUntilStep("Wait until player is loaded", () => currentPlayer.IsCurrentScreen()); - AddUntilStep("Wait for all judged", () => allJudgedFired); + AddUntilStep("Wait for completion", () => currentPlayer.ScoreProcessor.HasCompleted.Value); } private class ScoreAccessibleReplayPlayer : ReplayPlayer diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneSwellJudgements.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneSwellJudgements.cs index 303f0163b1..923e28a45e 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneSwellJudgements.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneSwellJudgements.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Taiko.Tests [Test] public void TestZeroTickTimeOffsets() { - AddUntilStep("gameplay finished", () => Player.ScoreProcessor.HasCompleted); + AddUntilStep("gameplay finished", () => Player.ScoreProcessor.HasCompleted.Value); AddAssert("all tick offsets are 0", () => Player.Results.Where(r => r.HitObject is SwellTick).All(r => r.TimeOffset == 0)); } diff --git a/osu.Game/Rulesets/Scoring/JudgementProcessor.cs b/osu.Game/Rulesets/Scoring/JudgementProcessor.cs index 334b95f808..d878ef0a5c 100644 --- a/osu.Game/Rulesets/Scoring/JudgementProcessor.cs +++ b/osu.Game/Rulesets/Scoring/JudgementProcessor.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Bindables; using osu.Framework.Extensions.TypeExtensions; using osu.Framework.Graphics; using osu.Game.Beatmaps; @@ -12,11 +13,6 @@ namespace osu.Game.Rulesets.Scoring { public abstract class JudgementProcessor : Component { - /// - /// Invoked when all s have been judged by this . - /// - public event Action AllJudged; - /// /// Invoked when a new judgement has occurred. This occurs after the judgement has been processed by this . /// @@ -32,10 +28,12 @@ namespace osu.Game.Rulesets.Scoring /// public int JudgedHits { get; private set; } + private readonly BindableBool hasCompleted = new BindableBool(); + /// /// Whether all s have been processed. /// - public bool HasCompleted => JudgedHits == MaxHits; + public IBindable HasCompleted => hasCompleted; /// /// Applies a to this . @@ -60,8 +58,8 @@ namespace osu.Game.Rulesets.Scoring NewJudgement?.Invoke(result); - if (HasCompleted) - AllJudged?.Invoke(); + if (JudgedHits == MaxHits) + hasCompleted.Value = true; } /// @@ -72,6 +70,9 @@ namespace osu.Game.Rulesets.Scoring { JudgedHits--; + if (JudgedHits < MaxHits) + hasCompleted.Value = false; + RevertResultInternal(result); } diff --git a/osu.Game/Screens/Play/BreakTracker.cs b/osu.Game/Screens/Play/BreakTracker.cs index 64262d52b5..fcd7ed6b73 100644 --- a/osu.Game/Screens/Play/BreakTracker.cs +++ b/osu.Game/Screens/Play/BreakTracker.cs @@ -51,7 +51,7 @@ namespace osu.Game.Screens.Play isBreakTime.Value = getCurrentBreak()?.HasEffect == true || Clock.CurrentTime < gameplayStartTime - || scoreProcessor?.HasCompleted == true; + || scoreProcessor?.HasCompleted.Value == true; } private BreakPeriod getCurrentBreak() diff --git a/osu.Game/Tests/Visual/ModPerfectTestScene.cs b/osu.Game/Tests/Visual/ModPerfectTestScene.cs index 798947eb40..5948283428 100644 --- a/osu.Game/Tests/Visual/ModPerfectTestScene.cs +++ b/osu.Game/Tests/Visual/ModPerfectTestScene.cs @@ -46,7 +46,7 @@ namespace osu.Game.Tests.Visual public bool CheckFailed(bool failed) { if (!failed) - return ScoreProcessor.HasCompleted && !HealthProcessor.HasFailed; + return ScoreProcessor.HasCompleted.Value && !HealthProcessor.HasFailed; return HealthProcessor.HasFailed; } From 7e64bec94f286b4d1bbc550d4e95f9ca601ca997 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 19 Apr 2020 05:58:22 +0300 Subject: [PATCH 0284/1117] Use HasCompleted in Player --- osu.Game/Screens/Play/Player.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 4597ae760c..542e226809 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -197,7 +197,7 @@ namespace osu.Game.Screens.Play }; // Bind the judgement processors to ourselves - ScoreProcessor.AllJudged += onCompletion; + ScoreProcessor.HasCompleted.ValueChanged += updateCompletionState; HealthProcessor.Failed += onFail; foreach (var mod in Mods.Value.OfType()) @@ -412,7 +412,7 @@ namespace osu.Game.Screens.Play private ScheduledDelegate completionProgressDelegate; - private void onCompletion() + private void updateCompletionState(ValueChangedEvent completionState) { // screen may be in the exiting transition phase. if (!this.IsCurrentScreen()) From 6d276890a7dca11c37855655f8f760c4b3176314 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 19 Apr 2020 05:59:56 +0300 Subject: [PATCH 0285/1117] Fix results screen pushed after rewinding in-between push delay --- osu.Game/Screens/Play/Player.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 542e226809..c6c83e5379 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -418,6 +418,16 @@ namespace osu.Game.Screens.Play if (!this.IsCurrentScreen()) return; + // cancel push delegate in case judges reverted + // after delegate may have already been scheduled. + if (!completionState.NewValue) + { + completionProgressDelegate?.Cancel(); + completionProgressDelegate = null; + ValidForResume = true; + return; + } + // Only show the completion screen if the player hasn't failed if (HealthProcessor.HasFailed || completionProgressDelegate != null) return; From 65a8860a65812dc4de6aae708d5352508898cdb7 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 19 Apr 2020 06:01:09 +0300 Subject: [PATCH 0286/1117] Add test cases to ensure no regression in "cancelling completion" --- .../TestSceneCompletionCancellation.cs | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs new file mode 100644 index 0000000000..54e1ff5345 --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs @@ -0,0 +1,129 @@ +// 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.Audio; +using osu.Framework.Audio.Track; +using osu.Framework.Screens; +using osu.Framework.Testing; +using osu.Framework.Timing; +using osu.Game.Beatmaps; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Storyboards; +using osuTK; + +namespace osu.Game.Tests.Visual.Gameplay +{ + public class TestSceneCompletionCancellation : PlayerTestScene + { + private Track track; + + [Resolved] + private AudioManager audio { get; set; } + + protected override bool AllowFail => false; + + public TestSceneCompletionCancellation() + : base(new OsuRuleset()) + { + } + + [SetUpSteps] + public override void SetUpSteps() + { + base.SetUpSteps(); + + // Ensure track has actually running before attempting to seek + AddUntilStep("wait for track to start running", () => track.IsRunning); + } + + [Test] + public void TestCancelCompletionOnRewind() + { + cancelCompletionSteps(); + + AddAssert("no attempt to push ranking", () => !((FakeRankingPushPlayer)Player).GotoRankingInvoked); + } + + [Test] + public void TestReCompleteAfterCancellation() + { + cancelCompletionSteps(); + + // Attempt completing again. + AddStep("seek to completion again", () => track.Seek(5000)); + AddUntilStep("completion set by processor", () => Player.ScoreProcessor.HasCompleted.Value); + + AddWaitStep("wait", 5); + + AddAssert("attempted to push ranking", () => ((FakeRankingPushPlayer)Player).GotoRankingInvoked); + } + + /// + /// Tests whether can still pause after cancelling completion + /// by reverting back to true. + /// + [Test] + public void TestCanPauseAfterCancellation() + { + cancelCompletionSteps(); + + AddStep("pause", () => Player.Pause()); + AddAssert("paused successfully", () => Player.GameplayClockContainer.IsPaused.Value); + } + + private void cancelCompletionSteps() + { + AddStep("seek to completion", () => track.Seek(5000)); + AddUntilStep("completion set by processor", () => Player.ScoreProcessor.HasCompleted.Value); + + AddStep("rewind to cancel", () => track.Seek(4000)); + AddUntilStep("completion cleared by processor", () => !Player.ScoreProcessor.HasCompleted.Value); + + AddWaitStep("wait", 5); + } + + protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null) + { + var working = new ClockBackedTestWorkingBeatmap(beatmap, storyboard, new FramedClock(new ManualClock { Rate = 1 }), audio); + track = working.Track; + return working; + } + + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) + { + var beatmap = new Beatmap(); + + for (int i = 1; i <= 19; i++) + { + beatmap.HitObjects.Add(new HitCircle + { + Position = new Vector2(256, 192), + StartTime = i * 250, + }); + } + + return beatmap; + } + + protected override TestPlayer CreatePlayer(Ruleset ruleset) => new FakeRankingPushPlayer(); + + public class FakeRankingPushPlayer : TestPlayer + { + public bool GotoRankingInvoked; + + public FakeRankingPushPlayer() + : base(true, true) + { + } + + protected override void GotoRanking() + { + GotoRankingInvoked = true; + } + } + } +} From 1dd471dfcc1ae9465d17c3d21ea2577d7a2b46c0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 19 Apr 2020 15:12:36 +0900 Subject: [PATCH 0287/1117] Add /np (now playing) command support in chat --- .../Visual/Online/TestNowPlayingCommand.cs | 85 +++++++++++++++++++ osu.Game/Online/Chat/ChannelManager.cs | 6 +- osu.Game/Online/Chat/IChannelPostTarget.cs | 19 +++++ osu.Game/Online/Chat/NowPlayingCommand.cs | 55 ++++++++++++ osu.Game/Online/PollingComponent.cs | 4 +- 5 files changed, 166 insertions(+), 3 deletions(-) create mode 100644 osu.Game.Tests/Visual/Online/TestNowPlayingCommand.cs create mode 100644 osu.Game/Online/Chat/IChannelPostTarget.cs create mode 100644 osu.Game/Online/Chat/NowPlayingCommand.cs diff --git a/osu.Game.Tests/Visual/Online/TestNowPlayingCommand.cs b/osu.Game.Tests/Visual/Online/TestNowPlayingCommand.cs new file mode 100644 index 0000000000..60032ab118 --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestNowPlayingCommand.cs @@ -0,0 +1,85 @@ +// 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.Graphics; +using osu.Framework.Testing; +using osu.Game.Beatmaps; +using osu.Game.Online.Chat; +using osu.Game.Rulesets; +using osu.Game.Users; + +namespace osu.Game.Tests.Visual.Online +{ + [HeadlessTest] + public class TestNowPlayingCommand : OsuTestScene + { + [Cached(typeof(IChannelPostTarget))] + private PostTarget postTarget { get; set; } + + public TestNowPlayingCommand() + { + Add(postTarget = new PostTarget()); + } + + [Test] + public void TestGenericActivity() + { + AddStep("Set activity", () => API.Activity.Value = new UserActivity.InLobby()); + + AddStep("Run command", () => Add(new NowPlayingCommand())); + + AddAssert("Check correct response", () => postTarget.LastMessage.Contains("is listening")); + } + + [Test] + public void TestEditActivity() + { + AddStep("Set activity", () => API.Activity.Value = new UserActivity.Editing(new BeatmapInfo())); + + AddStep("Run command", () => Add(new NowPlayingCommand())); + + AddAssert("Check correct response", () => postTarget.LastMessage.Contains("is editing")); + } + + [Test] + public void TestPlayActivity() + { + AddStep("Set activity", () => API.Activity.Value = new UserActivity.SoloGame(new BeatmapInfo(), new RulesetInfo())); + + AddStep("Run command", () => Add(new NowPlayingCommand())); + + AddAssert("Check correct response", () => postTarget.LastMessage.Contains("is playing")); + } + + [TestCase(true)] + [TestCase(false)] + public void TestLinkPresence(bool hasOnlineId) + { + AddStep("Set activity", () => API.Activity.Value = new UserActivity.InLobby()); + + AddStep("Set beatmap", () => Beatmap.Value = new DummyWorkingBeatmap(null, null) + { + BeatmapInfo = { OnlineBeatmapID = hasOnlineId ? 1234 : (int?)null } + }); + + AddStep("Run command", () => Add(new NowPlayingCommand())); + + if (hasOnlineId) + AddAssert("Check link presence", () => postTarget.LastMessage.Contains("https://osu.ppy.sh/b/1234")); + else + AddAssert("Check link not present", () => !postTarget.LastMessage.Contains("https://")); + } + + public class PostTarget : Component, IChannelPostTarget + { + public void PostMessage(string text, bool isAction = false, Channel target = null) + { + LastMessage = text; + } + + public string LastMessage { get; private set; } + } + } +} diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 2c37216fd6..f53beefeb5 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -18,7 +18,7 @@ namespace osu.Game.Online.Chat /// /// Manages everything channel related /// - public class ChannelManager : PollingComponent + public class ChannelManager : PollingComponent, IChannelPostTarget { /// /// The channels the player joins on startup @@ -204,6 +204,10 @@ namespace osu.Game.Online.Chat switch (command) { + case "np": + AddInternal(new NowPlayingCommand()); + break; + case "me": if (string.IsNullOrWhiteSpace(content)) { diff --git a/osu.Game/Online/Chat/IChannelPostTarget.cs b/osu.Game/Online/Chat/IChannelPostTarget.cs new file mode 100644 index 0000000000..5697e918f0 --- /dev/null +++ b/osu.Game/Online/Chat/IChannelPostTarget.cs @@ -0,0 +1,19 @@ +// 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; + +namespace osu.Game.Online.Chat +{ + [Cached(typeof(IChannelPostTarget))] + public interface IChannelPostTarget + { + /// + /// Posts a message to the currently opened channel. + /// + /// The message text that is going to be posted + /// Is true if the message is an action, e.g.: user is currently eating + /// An optional target channel. If null, will be used. + void PostMessage(string text, bool isAction = false, Channel target = null); + } +} diff --git a/osu.Game/Online/Chat/NowPlayingCommand.cs b/osu.Game/Online/Chat/NowPlayingCommand.cs new file mode 100644 index 0000000000..c0b54812b6 --- /dev/null +++ b/osu.Game/Online/Chat/NowPlayingCommand.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 osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Game.Beatmaps; +using osu.Game.Online.API; +using osu.Game.Users; + +namespace osu.Game.Online.Chat +{ + public class NowPlayingCommand : Component + { + [Resolved] + private IChannelPostTarget channelManager { get; set; } + + [Resolved] + private IAPIProvider api { get; set; } + + [Resolved] + private Bindable currentBeatmap { get; set; } + + protected override void LoadComplete() + { + base.LoadComplete(); + + string verb; + BeatmapInfo beatmap; + + switch (api.Activity.Value) + { + case UserActivity.SoloGame solo: + verb = "playing"; + beatmap = solo.Beatmap; + break; + + case UserActivity.Editing edit: + verb = "editing"; + beatmap = edit.Beatmap; + break; + + default: + verb = "listening to"; + beatmap = currentBeatmap.Value.BeatmapInfo; + break; + } + + var beatmapString = beatmap.OnlineBeatmapID.HasValue ? $"[https://osu.ppy.sh/b/{beatmap.OnlineBeatmapID} {beatmap}]" : beatmap.ToString(); + + channelManager.PostMessage($"is {verb} {beatmapString}", true); + Expire(); + } + } +} diff --git a/osu.Game/Online/PollingComponent.cs b/osu.Game/Online/PollingComponent.cs index acbb2c39f4..228f147835 100644 --- a/osu.Game/Online/PollingComponent.cs +++ b/osu.Game/Online/PollingComponent.cs @@ -3,7 +3,7 @@ using System; using System.Threading.Tasks; -using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Threading; namespace osu.Game.Online @@ -11,7 +11,7 @@ namespace osu.Game.Online /// /// A component which requires a constant polling process. /// - public abstract class PollingComponent : Component + public abstract class PollingComponent : CompositeDrawable // switch away from Component because InternalChildren are used in usages. { private double? lastTimePolled; From e4d4040afb90a990eb369ced822d65f07acd1e25 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 19 Apr 2020 16:57:47 +0900 Subject: [PATCH 0288/1117] Rename test to match other classes --- ...TestNowPlayingCommand.cs => TestSceneNowPlayingCommand.cs} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename osu.Game.Tests/Visual/Online/{TestNowPlayingCommand.cs => TestSceneNowPlayingCommand.cs} (96%) diff --git a/osu.Game.Tests/Visual/Online/TestNowPlayingCommand.cs b/osu.Game.Tests/Visual/Online/TestSceneNowPlayingCommand.cs similarity index 96% rename from osu.Game.Tests/Visual/Online/TestNowPlayingCommand.cs rename to osu.Game.Tests/Visual/Online/TestSceneNowPlayingCommand.cs index 60032ab118..103308d34d 100644 --- a/osu.Game.Tests/Visual/Online/TestNowPlayingCommand.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneNowPlayingCommand.cs @@ -13,12 +13,12 @@ using osu.Game.Users; namespace osu.Game.Tests.Visual.Online { [HeadlessTest] - public class TestNowPlayingCommand : OsuTestScene + public class TestSceneNowPlayingCommand : OsuTestScene { [Cached(typeof(IChannelPostTarget))] private PostTarget postTarget { get; set; } - public TestNowPlayingCommand() + public TestSceneNowPlayingCommand() { Add(postTarget = new PostTarget()); } From e3e0cd149f94d033a36d7622c0a8bf91e029fde0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 19 Apr 2020 12:41:00 +0200 Subject: [PATCH 0289/1117] Refactor test code to eliminate boolean flags --- .../TestSceneHyperDashColouring.cs | 127 +++++++++--------- 1 file changed, 65 insertions(+), 62 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs index 846b17f324..a48ecb9b79 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs @@ -27,74 +27,71 @@ namespace osu.Game.Rulesets.Catch.Tests private SkinManager skins { get; set; } [Test] - public void TestHyperDashFruitColour() + public void TestDefaultFruitColour() { - DrawableFruit drawableFruit = null; + var skin = new TestSkin(); - AddStep("setup hyper-dash fruit", () => - { - var fruit = new Fruit { HyperDashTarget = new Banana() }; - fruit.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); - - Child = setupSkinHierarchy(drawableFruit = new DrawableFruit(fruit) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Scale = new Vector2(4f), - }, false, false, false); - }); - - AddAssert("hyper-dash fruit has default colour", () => checkLegacyFruitHyperDashColour(drawableFruit, Catcher.DEFAULT_HYPER_DASH_COLOUR)); - } - - [TestCase(true)] - [TestCase(false)] - public void TestCustomHyperDashFruitColour(bool customCatcherHyperDashColour) - { - DrawableFruit drawableFruit = null; - - AddStep("setup hyper-dash fruit", () => - { - var fruit = new Fruit { HyperDashTarget = new Banana() }; - fruit.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); - - Child = setupSkinHierarchy(drawableFruit = new DrawableFruit(fruit) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Scale = new Vector2(4f), - }, customCatcherHyperDashColour, false, true); - }); - - AddAssert("hyper-dash fruit use fruit colour from skin", () => checkLegacyFruitHyperDashColour(drawableFruit, TestSkin.CUSTOM_HYPER_DASH_FRUIT_COLOUR)); + checkHyperDashFruitColour(skin, Catcher.DEFAULT_HYPER_DASH_COLOUR); } [Test] - public void TestCustomHyperDashFruitColourFallback() + public void TestCustomFruitColour() + { + var skin = new TestSkin + { + HyperDashFruitColour = Color4.Cyan + }; + + checkHyperDashFruitColour(skin, skin.HyperDashFruitColour); + } + + [Test] + public void TestCustomFruitColourPriority() + { + var skin = new TestSkin + { + HyperDashColour = Color4.Goldenrod, + HyperDashFruitColour = Color4.Cyan + }; + + checkHyperDashFruitColour(skin, skin.HyperDashFruitColour); + } + + [Test] + public void TestFruitColourFallback() + { + var skin = new TestSkin + { + HyperDashColour = Color4.Goldenrod + }; + + checkHyperDashFruitColour(skin, skin.HyperDashColour); + } + + private void checkHyperDashFruitColour(ISkin skin, Color4 expectedColour) { DrawableFruit drawableFruit = null; - AddStep("setup hyper-dash fruit", () => + AddStep("create hyper-dash fruit", () => { var fruit = new Fruit { HyperDashTarget = new Banana() }; fruit.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); - Child = setupSkinHierarchy( - drawableFruit = new DrawableFruit(fruit) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Scale = new Vector2(4f), - }, true, false, false); + Child = setupSkinHierarchy(drawableFruit = new DrawableFruit(fruit) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(4f), + }, skin); }); - AddAssert("hyper-dash fruit colour falls back to catcher colour from skin", () => checkLegacyFruitHyperDashColour(drawableFruit, TestSkin.CUSTOM_HYPER_DASH_COLOUR)); + AddAssert("hyper-dash colour is correct", () => checkLegacyFruitHyperDashColour(drawableFruit, expectedColour)); } - private Drawable setupSkinHierarchy(Drawable child, bool customCatcherColour, bool customAfterColour, bool customFruitColour) + private Drawable setupSkinHierarchy(Drawable child, ISkin skin) { var legacySkinProvider = new SkinProvidingContainer(skins.GetSkin(DefaultLegacySkin.Info)); - var testSkinProvider = new SkinProvidingContainer(new TestSkin(customCatcherColour, customAfterColour, customFruitColour)); + var testSkinProvider = new SkinProvidingContainer(skin); var legacySkinTransformer = new SkinProvidingContainer(new CatchLegacySkinTransformer(testSkinProvider)); return legacySkinProvider @@ -108,21 +105,27 @@ namespace osu.Game.Rulesets.Catch.Tests private class TestSkin : LegacySkin { - public static readonly Color4 CUSTOM_HYPER_DASH_COLOUR = Color4.Goldenrod; - public static readonly Color4 CUSTOM_HYPER_DASH_AFTER_COLOUR = Color4.Lime; - public static readonly Color4 CUSTOM_HYPER_DASH_FRUIT_COLOUR = Color4.Cyan; + public Color4 HyperDashColour + { + get => Configuration.CustomColours[CatchSkinColour.HyperDash.ToString()]; + set => Configuration.CustomColours[CatchSkinColour.HyperDash.ToString()] = value; + } - public TestSkin(bool customCatcherColour, bool customAfterColour, bool customFruitColour) + public Color4 HyperDashAfterImageColour + { + get => Configuration.CustomColours[CatchSkinColour.HyperDashAfterImage.ToString()]; + set => Configuration.CustomColours[CatchSkinColour.HyperDashAfterImage.ToString()] = value; + } + + public Color4 HyperDashFruitColour + { + get => Configuration.CustomColours[CatchSkinColour.HyperDashFruit.ToString()]; + set => Configuration.CustomColours[CatchSkinColour.HyperDashFruit.ToString()] = value; + } + + public TestSkin() : base(new SkinInfo(), null, null, string.Empty) { - if (customCatcherColour) - Configuration.CustomColours[CatchSkinColour.HyperDash.ToString()] = CUSTOM_HYPER_DASH_COLOUR; - - if (customAfterColour) - Configuration.CustomColours[CatchSkinColour.HyperDashAfterImage.ToString()] = CUSTOM_HYPER_DASH_AFTER_COLOUR; - - if (customFruitColour) - Configuration.CustomColours[CatchSkinColour.HyperDashFruit.ToString()] = CUSTOM_HYPER_DASH_FRUIT_COLOUR; } } } From a7179d1c8751b179ae8d676ee76cd73b8930336f Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sun, 19 Apr 2020 15:15:04 +0200 Subject: [PATCH 0290/1117] Fix comment wording MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Bartłomiej Dach --- osu.Game/Rulesets/RulesetStore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index 34da2dc2db..d4b41dcb38 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -59,7 +59,7 @@ namespace osu.Game.Rulesets // the requesting assembly may be located out of the executable's base directory, thus requiring manual resolving of its dependencies. // this assumes the only explicit dependency of the ruleset is the game core assembly. - // the ruleset dependency on the game core assembly requires manual resolving, transient dependencies should be resolved automatically + // the ruleset dependency on the game core assembly requires manual resolving, transitive dependencies should be resolved automatically if (asm.Name.Equals(typeof(OsuGame).Assembly.GetName().Name, StringComparison.Ordinal)) return Assembly.GetExecutingAssembly(); From 07b8ef83c95c209a9953013b3cadc01bc529b7aa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 19 Apr 2020 22:15:07 +0900 Subject: [PATCH 0291/1117] Add /np to help line --- osu.Game/Online/Chat/ChannelManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index f53beefeb5..822f628dd2 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -238,7 +238,7 @@ namespace osu.Game.Online.Chat break; case "help": - target.AddNewMessages(new InfoMessage("Supported commands: /help, /me [action], /join [channel]")); + target.AddNewMessages(new InfoMessage("Supported commands: /help, /me [action], /join [channel], /np")); break; default: From ba1c465edf296fe55bf4d623e5543918ab85f7af Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sun, 19 Apr 2020 15:25:21 +0200 Subject: [PATCH 0292/1117] Add comment in regards to the attaching of event handler to the assembly lookup event before call to loadUserRulesets(). --- osu.Game/Rulesets/RulesetStore.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index d4b41dcb38..ab169c741d 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -29,6 +29,10 @@ namespace osu.Game.Rulesets // We cannot read assemblies from cwd, so should check loaded assemblies instead. loadFromAppDomain(); loadFromDisk(); + + // the event handler contains code for resolving dependency on the game assembly for rulesets located outside the base game directory. + // It needs to be attached to the assembly lookup event before the actual call to loadUserRulesets() else rulesets located out of the base game directory will fail + // to load as unable to locate the game core assembly. AppDomain.CurrentDomain.AssemblyResolve += resolveRulesetDependencyAssembly; loadUserRulesets(); addMissingRulesets(); From 1dcb0f53a233452392efadd949c12342842eb7c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 19 Apr 2020 16:29:32 +0200 Subject: [PATCH 0293/1117] Fix whitespace formatting --- osu.Game/Rulesets/RulesetStore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index ab169c741d..7e165311a3 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -29,7 +29,7 @@ namespace osu.Game.Rulesets // We cannot read assemblies from cwd, so should check loaded assemblies instead. loadFromAppDomain(); loadFromDisk(); - + // the event handler contains code for resolving dependency on the game assembly for rulesets located outside the base game directory. // It needs to be attached to the assembly lookup event before the actual call to loadUserRulesets() else rulesets located out of the base game directory will fail // to load as unable to locate the game core assembly. From b57d709d151d4296e35a03fb79130cf55e4a34d3 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Sun, 19 Apr 2020 18:29:06 +0300 Subject: [PATCH 0294/1117] Don't use Parent --- osu.Game/Screens/Select/BeatmapCarousel.cs | 18 +++++++++--------- osu.Game/Screens/Select/SongSelect.cs | 2 ++ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index b5dfcadeaa..cf3a5a7199 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -28,8 +28,8 @@ namespace osu.Game.Screens.Select { public class BeatmapCarousel : CompositeDrawable, IKeyBindingHandler { - private const float bleed_top = FilterControl.HEIGHT; - private const float bleed_bottom = Footer.HEIGHT; + public float BleedTop; + public float BleedBottom; /// /// Triggered when the loaded change and are completely loaded. @@ -373,17 +373,17 @@ namespace osu.Game.Screens.Select /// the beatmap carousel bleeds into the and the /// /// - private float visibleHalfHeight => (DrawHeight + bleed_bottom + bleed_top) / 2; + private float visibleHalfHeight => (DrawHeight + BleedBottom + BleedTop) / 2; /// /// The position of the lower visible bound with respect to the current scroll position. /// - private float visibleBottomBound => scroll.Current + DrawHeight + bleed_bottom; + private float visibleBottomBound => scroll.Current + DrawHeight + BleedBottom; /// /// The position of the upper visible bound with respect to the current scroll position. /// - private float visibleUpperBound => scroll.Current - bleed_top; + private float visibleUpperBound => scroll.Current - BleedTop; public void FlushPendingFilterOperations() { @@ -641,11 +641,11 @@ namespace osu.Game.Screens.Select case DrawableCarouselBeatmap beatmap: { if (beatmap.Item.State.Value == CarouselItemState.Selected) - // scroll position at currentY makes the set panel appear at the very top of the carousel in screen space - // move down by half of parent height (which is the height of the carousel's visible extent, including semi-transparent areas) - // then reapply parent's padding from the top by adding it + // scroll position at currentY makes the set panel appear at the very top of the carousel's screen space + // move down by half of visible height (height of the carousel's visible extent, including semi-transparent areas) + // then reapply the top semi-transparent area (because carousel's screen space starts below it) // and finally add half of the panel's own height to achieve vertical centering of the panel itself - scrollTarget = currentY - Parent.DrawHeight / 2 + Parent.Padding.Top + beatmap.DrawHeight / 2; + scrollTarget = currentY - visibleHalfHeight + BleedTop + beatmap.DrawHeight / 2; void performMove(float y, float? startY = null) { diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 5bc2e1aa56..bd6204d8cd 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -153,6 +153,8 @@ namespace osu.Game.Screens.Select Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, RelativeSizeAxes = Axes.Both, + BleedTop = FilterControl.HEIGHT, + BleedBottom = Footer.HEIGHT, SelectionChanged = updateSelectedBeatmap, BeatmapSetsChanged = carouselBeatmapsLoaded, GetRecommendedBeatmap = recommender.GetRecommendedBeatmap, From f3fee734417154e36ef9594a67e3060983455d91 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 Apr 2020 09:35:00 +0900 Subject: [PATCH 0295/1117] Fix DatabasedKeyBindingContainer not using defaults for non-databased ruleset --- .../Visual/Gameplay/TestSceneKeyBindings.cs | 99 +++++++++++++++++++ .../Bindings/DatabasedKeyBindingContainer.cs | 10 +- 2 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneKeyBindings.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyBindings.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyBindings.cs new file mode 100644 index 0000000000..45d9819c0e --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyBindings.cs @@ -0,0 +1,99 @@ +// 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.Framework.Graphics.Containers; +using osu.Framework.Input.Bindings; +using osu.Framework.Testing; +using osu.Game.Beatmaps; +using osu.Game.Input.Bindings; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Difficulty; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.UI; +using osuTK.Input; + +namespace osu.Game.Tests.Visual.Gameplay +{ + [HeadlessTest] + public class TestSceneKeyBindings : OsuManualInputManagerTestScene + { + private readonly ActionReceiver receiver; + + public TestSceneKeyBindings() + { + Add(new TestKeyBindingContainer + { + Child = receiver = new ActionReceiver() + }); + } + + [Test] + public void TestDefaultsWhenNotDatabased() + { + AddStep("fire key", () => + { + InputManager.PressKey(Key.A); + InputManager.ReleaseKey(Key.A); + }); + + AddAssert("received key", () => receiver.ReceivedAction); + } + + private class TestRuleset : Ruleset + { + public override IEnumerable GetModsFor(ModType type) => + throw new System.NotImplementedException(); + + public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList mods = null) => + throw new System.NotImplementedException(); + + public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => + throw new System.NotImplementedException(); + + public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => + throw new System.NotImplementedException(); + + public override IEnumerable GetDefaultKeyBindings(int variant = 0) + { + return new[] + { + new KeyBinding(InputKey.A, TestAction.Down), + }; + } + + public override string Description => "test"; + public override string ShortName => "test"; + } + + private enum TestAction + { + Down, + } + + private class TestKeyBindingContainer : DatabasedKeyBindingContainer + { + public TestKeyBindingContainer() + : base(new TestRuleset().RulesetInfo, 0) + { + } + } + + private class ActionReceiver : CompositeDrawable, IKeyBindingHandler + { + public bool ReceivedAction; + + public bool OnPressed(TestAction action) + { + ReceivedAction = action == TestAction.Down; + return true; + } + + public void OnReleased(TestAction action) + { + throw new System.NotImplementedException(); + } + } + } +} diff --git a/osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs b/osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs index e83d899469..94edc33099 100644 --- a/osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs +++ b/osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs @@ -62,6 +62,14 @@ namespace osu.Game.Input.Bindings store.KeyBindingChanged -= ReloadMappings; } - protected override void ReloadMappings() => KeyBindings = store.Query(ruleset?.ID, variant).ToList(); + protected override void ReloadMappings() + { + if (ruleset != null && !ruleset.ID.HasValue) + // if the provided ruleset is not stored to the database, we have no way to retrieve custom bindings. + // fallback to defaults instead. + KeyBindings = DefaultKeyBindings; + else + KeyBindings = store.Query(ruleset?.ID, variant).ToList(); + } } } From 2444dd42d0a931a9b6288813956f435b1bc5d4f2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 Apr 2020 09:57:46 +0900 Subject: [PATCH 0296/1117] Remove not-implemented-exception --- osu.Game.Tests/Visual/Gameplay/TestSceneKeyBindings.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyBindings.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyBindings.cs index 45d9819c0e..db65e91d17 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyBindings.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyBindings.cs @@ -92,7 +92,6 @@ namespace osu.Game.Tests.Visual.Gameplay public void OnReleased(TestAction action) { - throw new System.NotImplementedException(); } } } From 28318a0140a4e3854d81896e7a762a451f8d7637 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 Apr 2020 10:59:08 +0900 Subject: [PATCH 0297/1117] Add mention of notelock in xmldoc (potentially easier to find class) --- osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.cs b/osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.cs index cd9838e7bf..176402c831 100644 --- a/osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.cs +++ b/osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.cs @@ -11,7 +11,7 @@ using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Osu.UI { /// - /// Ensures that s are hit in-order. + /// Ensures that s are hit in-order. Affectionately known as "note lock". /// If a is hit out of order: /// /// The hit is blocked if it occurred earlier than the previous 's start time. From e1acfd26a6849be6cee96edc66ae58c17e9a1fac Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 Apr 2020 10:59:44 +0900 Subject: [PATCH 0298/1117] Simplify return logic --- osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.cs b/osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.cs index 176402c831..1f027e9726 100644 --- a/osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.cs +++ b/osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.cs @@ -51,10 +51,7 @@ namespace osu.Game.Rulesets.Osu.UI // 1. The last blocking hitobject has been judged. // 2. The current time is after the last hitobject's start time. // Hits at exactly the same time as the blocking hitobject are allowed for maps that contain simultaneous hitobjects (e.g. /b/372245). - if (blockingObject.Judged || time >= blockingObject.HitObject.StartTime) - return true; - - return false; + return blockingObject.Judged || time >= blockingObject.HitObject.StartTime; } /// From 8c85602ad013001281d4148b6109d606d6885c8c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 Apr 2020 11:00:42 +0900 Subject: [PATCH 0299/1117] Use foreach for conformity --- osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.cs b/osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.cs index 1f027e9726..53dd1127d6 100644 --- a/osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.cs +++ b/osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.cs @@ -93,12 +93,12 @@ namespace osu.Game.Rulesets.Osu.UI yield return obj; - for (int i = 0; i < obj.NestedHitObjects.Count; i++) + foreach (var nestedObj in obj.NestedHitObjects) { - if (obj.NestedHitObjects[i].HitObject.StartTime >= targetTime) + if (nestedObj.HitObject.StartTime >= targetTime) break; - yield return obj.NestedHitObjects[i]; + yield return nestedObj; } } } From 6f233917b18dc05bb5f7d620cfc8fc512a6df9f7 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 20 Apr 2020 06:40:51 +0300 Subject: [PATCH 0300/1117] Centralize updating HasCompleted bindable logic --- osu.Game/Rulesets/Scoring/JudgementProcessor.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/JudgementProcessor.cs b/osu.Game/Rulesets/Scoring/JudgementProcessor.cs index d878ef0a5c..8aef615b5f 100644 --- a/osu.Game/Rulesets/Scoring/JudgementProcessor.cs +++ b/osu.Game/Rulesets/Scoring/JudgementProcessor.cs @@ -58,8 +58,7 @@ namespace osu.Game.Rulesets.Scoring NewJudgement?.Invoke(result); - if (JudgedHits == MaxHits) - hasCompleted.Value = true; + updateHasCompleted(); } /// @@ -70,8 +69,7 @@ namespace osu.Game.Rulesets.Scoring { JudgedHits--; - if (JudgedHits < MaxHits) - hasCompleted.Value = false; + updateHasCompleted(); RevertResultInternal(result); } @@ -135,5 +133,7 @@ namespace osu.Game.Rulesets.Scoring ApplyResult(result); } } + + private void updateHasCompleted() => hasCompleted.Value = JudgedHits == MaxHits; } } From e12e3391fb803f9bdf75ebb51abaf275427c1d07 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 20 Apr 2020 06:42:33 +0300 Subject: [PATCH 0301/1117] Base wait steps duration on the delay used for results display With `* 2` for safety of not potentially going to the next step and the delegate not executed yet. --- .../Visual/Gameplay/TestSceneCompletionCancellation.cs | 6 +++++- osu.Game/Screens/Play/Player.cs | 7 ++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs index 54e1ff5345..aef173c36a 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs @@ -24,6 +24,9 @@ namespace osu.Game.Tests.Visual.Gameplay [Resolved] private AudioManager audio { get; set; } + private int resultsDisplayWaitCount => + (int)((Screens.Play.Player.RESULTS_DISPLAY_DELAY / TimePerAction) * 2); + protected override bool AllowFail => false; public TestSceneCompletionCancellation() @@ -83,7 +86,8 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("rewind to cancel", () => track.Seek(4000)); AddUntilStep("completion cleared by processor", () => !Player.ScoreProcessor.HasCompleted.Value); - AddWaitStep("wait", 5); + // wait to ensure there was no attempt of pushing the results screen. + AddWaitStep("wait", resultsDisplayWaitCount); } protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index c6c83e5379..2f3807753a 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -37,6 +37,11 @@ namespace osu.Game.Screens.Play [Cached] public class Player : ScreenWithBeatmapBackground { + /// + /// The delay upon completion of the beatmap before displaying the results screen. + /// + public const double RESULTS_DISPLAY_DELAY = 1000.0; + public override bool AllowBackButton => false; // handled by HoldForMenuButton protected override UserActivity InitialActivity => new UserActivity.SoloGame(Beatmap.Value.BeatmapInfo, Ruleset.Value); @@ -436,7 +441,7 @@ namespace osu.Game.Screens.Play if (!showResults) return; - using (BeginDelayedSequence(1000)) + using (BeginDelayedSequence(RESULTS_DISPLAY_DELAY)) scheduleGotoRanking(); } From 2c012b9af1106bbc154b25453a8cd71c3eb5001b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 20 Apr 2020 06:43:18 +0300 Subject: [PATCH 0302/1117] Use AddUntilStep whenever possible Avoid redundant usage --- .../Visual/Gameplay/TestSceneCompletionCancellation.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs index aef173c36a..a3fb17942f 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs @@ -60,9 +60,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("seek to completion again", () => track.Seek(5000)); AddUntilStep("completion set by processor", () => Player.ScoreProcessor.HasCompleted.Value); - AddWaitStep("wait", 5); - - AddAssert("attempted to push ranking", () => ((FakeRankingPushPlayer)Player).GotoRankingInvoked); + AddUntilStep("attempted to push ranking", () => ((FakeRankingPushPlayer)Player).GotoRankingInvoked); } /// From 355e682e24557b3b87d791943c0e2523e3331de6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 20 Apr 2020 13:23:27 +0900 Subject: [PATCH 0303/1117] Fix typo in exception --- osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.cs b/osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.cs index 53dd1127d6..8e4f81347d 100644 --- a/osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.cs +++ b/osu.Game.Rulesets.Osu/UI/OrderedHitPolicy.cs @@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Osu.UI return; if (!IsHittable(hitObject, hitObject.HitObject.StartTime + hitObject.Result.TimeOffset)) - throw new InvalidOperationException($"A {hitObject} was hit before it become hittable!"); + throw new InvalidOperationException($"A {hitObject} was hit before it became hittable!"); foreach (var obj in enumerateHitObjectsUpTo(hitObject.HitObject.StartTime)) { From ee1ccb8bcb14c637e6912c9f278b10b7573348af Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 Apr 2020 14:03:55 +0900 Subject: [PATCH 0304/1117] Fix in a slightly different and hopefully more understandable way --- osu.Game/Screens/Select/SongSelect.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 9d3dc58a26..478c46fb36 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -426,7 +426,7 @@ namespace osu.Game.Screens.Select } /// - /// selection has been changed as the result of a user interaction. + /// Selection has been changed as the result of a user interaction. /// private void performUpdateSelected() { @@ -435,7 +435,7 @@ namespace osu.Game.Screens.Select selectionChangedDebounce?.Cancel(); - if (beatmap == null) + if (beatmapNoDebounce == null) run(); else selectionChangedDebounce = Scheduler.AddDelayed(run, 200); @@ -448,11 +448,11 @@ namespace osu.Game.Screens.Select { Mods.Value = Array.Empty(); - // the ruleset transfer may cause a deselection of the current beatmap (due to incompatibility). - // this can happen via Carousel.FlushPendingFilterOperations(). - // to ensure a good state, re-transfer no-debounce values. - performUpdateSelected(); - return; + // transferRulesetValue() may trigger a refilter. If the current selection does not match the new ruleset, we want to switch away from it. + // The default logic on WorkingBeatmap change is to switch to a matching ruleset (see workingBeatmapChanged()), but we don't want that here. + // We perform an early selection attempt and clear out the beatmap selection to avoid a second ruleset change (revert). + if (beatmap != null && !Carousel.SelectBeatmap(beatmap, false)) + beatmap = null; } // We may be arriving here due to another component changing the bindable Beatmap. @@ -716,7 +716,7 @@ namespace osu.Game.Screens.Select if (decoupledRuleset.Value?.Equals(Ruleset.Value) == true) return false; - Logger.Log($"decoupled ruleset transferred (\"{decoupledRuleset.Value}\" -> \"{Ruleset.Value}\""); + Logger.Log($"decoupled ruleset transferred (\"{decoupledRuleset.Value}\" -> \"{Ruleset.Value}\")"); rulesetNoDebounce = decoupledRuleset.Value = Ruleset.Value; // if we have a pending filter operation, we want to run it now. From b881293b98410e47dfde82f3f330f8aeda7d2c96 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 20 Apr 2020 14:08:23 +0900 Subject: [PATCH 0305/1117] Allow 10k to be played on a single stage --- .../Beatmaps/ManiaBeatmapConverter.cs | 2 +- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 67 +++++++++++++------ 2 files changed, 48 insertions(+), 21 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index d904474815..189dd17934 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps { TargetColumns = (int)Math.Max(1, roundedCircleSize); - if (TargetColumns >= 10) + if (TargetColumns > 10) { TargetColumns /= 2; Dual = true; diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 2bd88fee90..e8698ef01c 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -250,7 +250,7 @@ namespace osu.Game.Rulesets.Mania { get { - for (int i = 1; i <= 9; i++) + for (int i = 1; i <= 10; i++) yield return (int)PlayfieldType.Single + i; for (int i = 2; i <= 18; i += 2) yield return (int)PlayfieldType.Dual + i; @@ -262,26 +262,53 @@ namespace osu.Game.Rulesets.Mania switch (getPlayfieldType(variant)) { case PlayfieldType.Single: - return new VariantMappingGenerator + switch (variant) { - LeftKeys = new[] - { - InputKey.A, - InputKey.S, - InputKey.D, - InputKey.F - }, - RightKeys = new[] - { - InputKey.J, - InputKey.K, - InputKey.L, - InputKey.Semicolon - }, - SpecialKey = InputKey.Space, - SpecialAction = ManiaAction.Special1, - NormalActionStart = ManiaAction.Key1, - }.GenerateKeyBindingsFor(variant, out _); + case 10: + // 10K is special because it extents one key towards the centre of the keyboard (V/N), rather than towards the edges of the keyboard. + return new VariantMappingGenerator + { + LeftKeys = new[] + { + InputKey.A, + InputKey.S, + InputKey.D, + InputKey.F, + InputKey.V + }, + RightKeys = new[] + { + InputKey.N, + InputKey.J, + InputKey.K, + InputKey.L, + InputKey.Semicolon, + }, + NormalActionStart = ManiaAction.Key1, + }.GenerateKeyBindingsFor(variant, out _); + + default: + return new VariantMappingGenerator + { + LeftKeys = new[] + { + InputKey.A, + InputKey.S, + InputKey.D, + InputKey.F + }, + RightKeys = new[] + { + InputKey.J, + InputKey.K, + InputKey.L, + InputKey.Semicolon + }, + SpecialKey = InputKey.Space, + SpecialAction = ManiaAction.Special1, + NormalActionStart = ManiaAction.Key1, + }.GenerateKeyBindingsFor(variant, out _); + } case PlayfieldType.Dual: int keys = getDualStageKeyCount(variant); From 5b4f69bb8cabf89c41aff3b16c3c081975474236 Mon Sep 17 00:00:00 2001 From: Tim Oliver Date: Mon, 20 Apr 2020 13:32:51 +0800 Subject: [PATCH 0306/1117] Moved flying hit objects to separate files --- .../Objects/Drawables/DrawableCentreHit.cs | 17 -------------- .../Drawables/DrawableFlyingCentreHit.cs | 23 +++++++++++++++++++ .../Objects/Drawables/DrawableFlyingRimHit.cs | 23 +++++++++++++++++++ .../Objects/Drawables/DrawableRimHit.cs | 14 ----------- 4 files changed, 46 insertions(+), 31 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingCentreHit.cs create mode 100644 osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingRimHit.cs diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs index b6f6f04821..4979135f50 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs @@ -2,10 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; -using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; namespace osu.Game.Rulesets.Taiko.Objects.Drawables @@ -26,18 +23,4 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables MainPiece.AccentColour = colours.PinkDarker; } } - - public class DrawableFlyingCentreHit : DrawableCentreHit - { - protected override void CheckForResult(bool userTriggered, double timeOffset) - { - ApplyResult(r => r.Type = HitResult.Good); - } - - public DrawableFlyingCentreHit(double time, bool isStrong = false) - : base(new IgnoreHit { StartTime = time, IsStrong = isStrong }) - { - HitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); - } - } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingCentreHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingCentreHit.cs new file mode 100644 index 0000000000..826a4467f8 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingCentreHit.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.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Taiko.Objects.Drawables +{ + public class DrawableFlyingCentreHit : DrawableCentreHit + { + protected override void CheckForResult(bool userTriggered, double timeOffset) + { + ApplyResult(r => r.Type = HitResult.Good); + } + + public DrawableFlyingCentreHit(double time, bool isStrong = false) + : base(new IgnoreHit { StartTime = time, IsStrong = isStrong }) + { + HitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingRimHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingRimHit.cs new file mode 100644 index 0000000000..4a6fed8302 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingRimHit.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.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Taiko.Objects.Drawables +{ + public class DrawableFlyingRimHit : DrawableRimHit + { + protected override void CheckForResult(bool userTriggered, double timeOffset) + { + ApplyResult(r => r.Type = HitResult.Good); + } + + public DrawableFlyingRimHit(double time, bool isStrong = false) + : base(new IgnoreHit { StartTime = time, IsStrong = isStrong }) + { + HitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs index e5cfc0562b..c10c195019 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs @@ -26,18 +26,4 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables MainPiece.AccentColour = colours.BlueDarker; } } - - public class DrawableFlyingRimHit : DrawableRimHit - { - protected override void CheckForResult(bool userTriggered, double timeOffset) - { - ApplyResult(r => r.Type = HitResult.Good); - } - - public DrawableFlyingRimHit(double time, bool isStrong = false) - : base(new IgnoreHit { StartTime = time, IsStrong = isStrong }) - { - HitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); - } - } } From 5d96d672268cbdc0b7cb20d4a9adc47a4acd451c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 20 Apr 2020 14:40:37 +0900 Subject: [PATCH 0307/1117] Add special key definition just for sanity --- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index e8698ef01c..2147776d03 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -284,6 +284,8 @@ namespace osu.Game.Rulesets.Mania InputKey.L, InputKey.Semicolon, }, + SpecialKey = InputKey.Space, + SpecialAction = ManiaAction.Special1, NormalActionStart = ManiaAction.Key1, }.GenerateKeyBindingsFor(variant, out _); From 5464746d3d899a136bfa392789d351559d6f5752 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 Apr 2020 15:25:58 +0900 Subject: [PATCH 0308/1117] Switch to using CompositeDrawable --- .../Dashboard/Friends/FriendDisplay.cs | 23 ++++++++----------- osu.Game/Overlays/OverlayView.cs | 14 ++--------- 2 files changed, 12 insertions(+), 25 deletions(-) diff --git a/osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs b/osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs index 9764f82199..7c4a0a4164 100644 --- a/osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs +++ b/osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs @@ -34,16 +34,17 @@ namespace osu.Game.Overlays.Dashboard.Friends private Drawable currentContent; - private readonly FriendOnlineStreamControl onlineStreamControl; - private readonly Box background; - private readonly Box controlBackground; - private readonly UserListToolbar userListToolbar; - private readonly Container itemsPlaceholder; - private readonly LoadingLayer loading; + private FriendOnlineStreamControl onlineStreamControl; + private Box background; + private Box controlBackground; + private UserListToolbar userListToolbar; + private Container itemsPlaceholder; + private LoadingLayer loading; - public FriendDisplay() + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) { - AddRange(new Drawable[] + InternalChildren = new Drawable[] { new Container { @@ -121,12 +122,8 @@ namespace osu.Game.Overlays.Dashboard.Friends } } } - }); - } + }; - [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider) - { background.Colour = colourProvider.Background4; controlBackground.Colour = colourProvider.Background5; } diff --git a/osu.Game/Overlays/OverlayView.cs b/osu.Game/Overlays/OverlayView.cs index e3a07fc2de..724658f22f 100644 --- a/osu.Game/Overlays/OverlayView.cs +++ b/osu.Game/Overlays/OverlayView.cs @@ -9,29 +9,19 @@ using osu.Game.Online.API; namespace osu.Game.Overlays { /// - /// Drawable which used to represent online content in . + /// A subview containing online content, to be displayed inside a . /// /// Response type - public abstract class OverlayView : Container, IOnlineComponent + public abstract class OverlayView : CompositeDrawable, IOnlineComponent where T : class { [Resolved] protected IAPIProvider API { get; private set; } - protected override Container Content => content; - - private readonly FillFlowContainer content; - protected OverlayView() { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; - - AddInternal(content = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - }); } protected override void LoadComplete() From 99e13b8ed9c124484b4410eb9b03da4f2be03997 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 Apr 2020 15:32:50 +0900 Subject: [PATCH 0309/1117] Add better xml documentation and extract fetch method --- osu.Game/Overlays/OverlayView.cs | 33 ++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/OverlayView.cs b/osu.Game/Overlays/OverlayView.cs index 724658f22f..3e2c54c726 100644 --- a/osu.Game/Overlays/OverlayView.cs +++ b/osu.Game/Overlays/OverlayView.cs @@ -11,13 +11,18 @@ namespace osu.Game.Overlays /// /// A subview containing online content, to be displayed inside a . /// - /// Response type + /// + /// Automatically performs a data fetch on load. + /// + /// The type of the API response. public abstract class OverlayView : CompositeDrawable, IOnlineComponent where T : class { [Resolved] protected IAPIProvider API { get; private set; } + private APIRequest request; + protected OverlayView() { RelativeSizeAxes = Axes.X; @@ -30,20 +35,36 @@ namespace osu.Game.Overlays API.Register(this); } - private APIRequest request; - + /// + /// Create the API request for fetching data. + /// protected abstract APIRequest CreateRequest(); + /// + /// Fired when results arrive from the main API request. + /// + /// protected abstract void OnSuccess(T response); + /// + /// Force a re-request for data from the API. + /// + protected void PerformFetch() + { + request?.Cancel(); + + request = CreateRequest(); + request.Success += response => Schedule(() => OnSuccess(response)); + + API.Queue(request); + } + public virtual void APIStateChanged(IAPIProvider api, APIState state) { switch (state) { case APIState.Online: - request = CreateRequest(); - request.Success += response => Schedule(() => OnSuccess(response)); - api.Queue(request); + PerformFetch(); break; } } From 6b89c638c9aa2db2744a4d65bad92774e1e49a3a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 Apr 2020 15:34:48 +0900 Subject: [PATCH 0310/1117] Move load to bdl --- osu.Game/Overlays/DashboardOverlay.cs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/osu.Game/Overlays/DashboardOverlay.cs b/osu.Game/Overlays/DashboardOverlay.cs index 86c0f3bd83..a72c3f4fa5 100644 --- a/osu.Game/Overlays/DashboardOverlay.cs +++ b/osu.Game/Overlays/DashboardOverlay.cs @@ -19,14 +19,19 @@ namespace osu.Game.Overlays { private CancellationTokenSource cancellationToken; - private readonly Box background; - private readonly Container content; - private readonly DashboardOverlayHeader header; - private readonly LoadingLayer loading; - private readonly OverlayScrollContainer scrollFlow; + private Box background; + private Container content; + private DashboardOverlayHeader header; + private LoadingLayer loading; + private OverlayScrollContainer scrollFlow; public DashboardOverlay() : base(OverlayColourScheme.Purple) + { + } + + [BackgroundDependencyLoader] + private void load() { Children = new Drawable[] { @@ -61,17 +66,14 @@ namespace osu.Game.Overlays }, loading = new LoadingLayer(content), }; - } - [BackgroundDependencyLoader] - private void load() - { background.Colour = ColourProvider.Background5; } protected override void LoadComplete() { base.LoadComplete(); + header.Current.BindValueChanged(onTabChanged); } From e61a90d4695ca1ca8ee6aa479b541d29e6cf08f2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 Apr 2020 15:50:48 +0900 Subject: [PATCH 0311/1117] Throw instead of returning zero --- osu.Game/Utils/OrderAttribute.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Utils/OrderAttribute.cs b/osu.Game/Utils/OrderAttribute.cs index 4959caa726..aded7f9814 100644 --- a/osu.Game/Utils/OrderAttribute.cs +++ b/osu.Game/Utils/OrderAttribute.cs @@ -29,7 +29,7 @@ namespace osu.Game.Utils if (type.GetField(i.ToString()).GetCustomAttributes(typeof(OrderAttribute), false).FirstOrDefault() is OrderAttribute attr) return attr.Order; - return 0; + throw new ArgumentException($"Not all values of {nameof(T)} have {nameof(OrderAttribute)} specified."); }); } } From 801f02a3d7bb28b842a7dfd7e1d39b404b7d9690 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 Apr 2020 17:48:02 +0900 Subject: [PATCH 0312/1117] Fix inline executions of APIRequest.Perform not getting result populated early enough --- osu.Game/Online/API/APIRequest.cs | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/osu.Game/Online/API/APIRequest.cs b/osu.Game/Online/API/APIRequest.cs index 47600e4f68..0bba04cac3 100644 --- a/osu.Game/Online/API/APIRequest.cs +++ b/osu.Game/Online/API/APIRequest.cs @@ -18,24 +18,32 @@ namespace osu.Game.Online.API public T Result { get; private set; } - protected APIRequest() - { - base.Success += () => TriggerSuccess(((OsuJsonWebRequest)WebRequest)?.ResponseObject); - } - /// /// Invoked on successful completion of an API request. /// This will be scheduled to the API's internal scheduler (run on update thread automatically). /// public new event APISuccessHandler Success; + protected override void PostProcess() + { + base.PostProcess(); + Result = ((OsuJsonWebRequest)WebRequest)?.ResponseObject; + } + internal void TriggerSuccess(T result) { if (Result != null) throw new InvalidOperationException("Attempted to trigger success more than once"); Result = result; - Success?.Invoke(result); + + TriggerSuccess(); + } + + internal override void TriggerSuccess() + { + base.TriggerSuccess(); + Success?.Invoke(Result); } } @@ -99,6 +107,8 @@ namespace osu.Game.Online.API if (checkAndScheduleFailure()) return; + PostProcess(); + API.Schedule(delegate { if (cancelled) return; @@ -107,7 +117,14 @@ namespace osu.Game.Online.API }); } - internal void TriggerSuccess() + /// + /// Perform any post-processing actions after a successful request. + /// + protected virtual void PostProcess() + { + } + + internal virtual void TriggerSuccess() { Success?.Invoke(); } From 3f3ff5fdb1b145f7ac885a691f7f0fbe93d1b99c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 20 Apr 2020 09:24:40 +0000 Subject: [PATCH 0313/1117] Bump Humanizer from 2.7.9 to 2.8.2 Bumps [Humanizer](https://github.com/Humanizr/Humanizer) from 2.7.9 to 2.8.2. - [Release notes](https://github.com/Humanizr/Humanizer/releases) - [Changelog](https://github.com/Humanizr/Humanizer/blob/master/release_notes.md) - [Commits](https://github.com/Humanizr/Humanizer/compare/v2.7.9...v2.8.2) Signed-off-by: dependabot-preview[bot] --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 5facb04117..35ee0864e1 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -19,7 +19,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index dda1ee5c42..0200fca9a3 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -76,7 +76,7 @@ - + From b3d4b4a3f42696861d8c17cd395423e4a2656f74 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 Apr 2020 18:25:39 +0900 Subject: [PATCH 0314/1117] Add back missing fill flow --- .../Dashboard/Friends/FriendDisplay.cs | 124 +++++++++--------- 1 file changed, 65 insertions(+), 59 deletions(-) diff --git a/osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs b/osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs index 7c4a0a4164..79fda99c73 100644 --- a/osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs +++ b/osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs @@ -44,78 +44,84 @@ namespace osu.Game.Overlays.Dashboard.Friends [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider) { - InternalChildren = new Drawable[] + InternalChild = new FillFlowContainer { - new Container + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Children = new Drawable[] { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new Drawable[] + new Container { - controlBackground = new Box + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both - }, - new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding + controlBackground = new Box { - Top = 20, - Horizontal = 45 + RelativeSizeAxes = Axes.Both }, - Child = onlineStreamControl = new FriendOnlineStreamControl(), - } - } - }, - new Container - { - Name = "User List", - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new Drawable[] - { - background = new Box - { - RelativeSizeAxes = Axes.Both - }, - new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Margin = new MarginPadding { Bottom = 20 }, - Children = new Drawable[] + new Container { - new Container + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding - { - Horizontal = 40, - Vertical = 20 - }, - Child = userListToolbar = new UserListToolbar - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - } + Top = 20, + Horizontal = 45 }, - new Container + Child = onlineStreamControl = new FriendOnlineStreamControl(), + } + } + }, + new Container + { + Name = "User List", + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Margin = new MarginPadding { Bottom = 20 }, + Children = new Drawable[] { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new Drawable[] + new Container { - itemsPlaceholder = new Container + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding { Horizontal = 50 } + Horizontal = 40, + Vertical = 20 }, - loading = new LoadingLayer(itemsPlaceholder) + Child = userListToolbar = new UserListToolbar + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + } + }, + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + itemsPlaceholder = new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Horizontal = 50 } + }, + loading = new LoadingLayer(itemsPlaceholder) + } } } } From 8ebc2ae03dc6080028f5406d361f42f8d68855cc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 Apr 2020 20:48:35 +0900 Subject: [PATCH 0315/1117] Never run subtree masking --- 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 b14927bcd5..e847dcec40 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -398,7 +398,7 @@ namespace osu.Game.Rulesets.Objects.Drawables } } - public override bool UpdateSubTreeMasking(Drawable source, RectangleF maskingBounds) => AllJudged && base.UpdateSubTreeMasking(source, maskingBounds); + public override bool UpdateSubTreeMasking(Drawable source, RectangleF maskingBounds) => false; protected override void UpdateAfterChildren() { From a541f9268205f76c1eb4321faefeb1721d4f0f8f Mon Sep 17 00:00:00 2001 From: Lucas A Date: Mon, 20 Apr 2020 13:56:15 +0200 Subject: [PATCH 0316/1117] Resolve ruleset dependencies on game core / framework assemblies by checking already loaded assemblies in AppDomain. --- osu.Game/Rulesets/RulesetStore.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index 7e165311a3..543134cfb4 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -62,10 +62,13 @@ namespace osu.Game.Rulesets var asm = new AssemblyName(args.Name); // the requesting assembly may be located out of the executable's base directory, thus requiring manual resolving of its dependencies. - // this assumes the only explicit dependency of the ruleset is the game core assembly. - // the ruleset dependency on the game core assembly requires manual resolving, transitive dependencies should be resolved automatically - if (asm.Name.Equals(typeof(OsuGame).Assembly.GetName().Name, StringComparison.Ordinal)) - return Assembly.GetExecutingAssembly(); + // this attempts resolving the ruleset dependencies on game core and framework assemblies by returning assemblies with the same assembly name + // already loaded in the AppDomain. + foreach (var curAsm in AppDomain.CurrentDomain.GetAssemblies()) + { + if (asm.Name.Equals(curAsm.GetName().Name, StringComparison.Ordinal)) + return curAsm; + } return loadedAssemblies.Keys.FirstOrDefault(a => a.FullName == asm.FullName); } From 4e271ff46fcae5132bcc7ef235117518cd8f1268 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 20 Apr 2020 21:28:36 +0900 Subject: [PATCH 0317/1117] Add support for 10K mod + 20K dual stages --- .../DualStageVariantGenerator.cs | 64 ++++++++ osu.Game.Rulesets.Mania/ManiaInputManager.cs | 6 + osu.Game.Rulesets.Mania/ManiaRuleset.cs | 152 +----------------- osu.Game.Rulesets.Mania/Mods/ManiaModKey10.cs | 13 ++ .../SingleStageVariantGenerator.cs | 41 +++++ .../VariantMappingGenerator.cs | 61 +++++++ 6 files changed, 189 insertions(+), 148 deletions(-) create mode 100644 osu.Game.Rulesets.Mania/DualStageVariantGenerator.cs create mode 100644 osu.Game.Rulesets.Mania/Mods/ManiaModKey10.cs create mode 100644 osu.Game.Rulesets.Mania/SingleStageVariantGenerator.cs create mode 100644 osu.Game.Rulesets.Mania/VariantMappingGenerator.cs diff --git a/osu.Game.Rulesets.Mania/DualStageVariantGenerator.cs b/osu.Game.Rulesets.Mania/DualStageVariantGenerator.cs new file mode 100644 index 0000000000..8d39e08b26 --- /dev/null +++ b/osu.Game.Rulesets.Mania/DualStageVariantGenerator.cs @@ -0,0 +1,64 @@ +// 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.Input.Bindings; + +namespace osu.Game.Rulesets.Mania +{ + public class DualStageVariantGenerator + { + private readonly int singleStageVariant; + private readonly InputKey[] stage1LeftKeys; + private readonly InputKey[] stage1RightKeys; + private readonly InputKey[] stage2LeftKeys; + private readonly InputKey[] stage2RightKeys; + + public DualStageVariantGenerator(int singleStageVariant) + { + this.singleStageVariant = singleStageVariant; + + // 10K is special because it expands towards the centre of the keyboard (VM/BN), rather than towards the edges of the keyboard. + if (singleStageVariant == 10) + { + stage1LeftKeys = new[] { InputKey.Q, InputKey.W, InputKey.E, InputKey.R, InputKey.V }; + stage1RightKeys = new[] { InputKey.M, InputKey.I, InputKey.O, InputKey.P, InputKey.BracketLeft }; + + stage2LeftKeys = new[] { InputKey.S, InputKey.D, InputKey.F, InputKey.G, InputKey.B }; + stage2RightKeys = new[] { InputKey.N, InputKey.J, InputKey.K, InputKey.L, InputKey.Semicolon }; + } + else + { + stage1LeftKeys = new[] { InputKey.Q, InputKey.W, InputKey.E, InputKey.R }; + stage1RightKeys = new[] { InputKey.I, InputKey.O, InputKey.P, InputKey.BracketLeft }; + + stage2LeftKeys = new[] { InputKey.S, InputKey.D, InputKey.F, InputKey.G }; + stage2RightKeys = new[] { InputKey.J, InputKey.K, InputKey.L, InputKey.Semicolon }; + } + } + + public IEnumerable GenerateMappings() + { + var stage1Bindings = new VariantMappingGenerator + { + LeftKeys = stage1LeftKeys, + RightKeys = stage1RightKeys, + SpecialKey = InputKey.V, + SpecialAction = ManiaAction.Special1, + NormalActionStart = ManiaAction.Key1 + }.GenerateKeyBindingsFor(singleStageVariant, out var nextNormal); + + var stage2Bindings = new VariantMappingGenerator + { + LeftKeys = stage2LeftKeys, + RightKeys = stage2RightKeys, + SpecialKey = InputKey.B, + SpecialAction = ManiaAction.Special2, + NormalActionStart = nextNormal + }.GenerateKeyBindingsFor(singleStageVariant, out _); + + return stage1Bindings.Concat(stage2Bindings); + } + } +} diff --git a/osu.Game.Rulesets.Mania/ManiaInputManager.cs b/osu.Game.Rulesets.Mania/ManiaInputManager.cs index 292990fd7e..186fc4b15d 100644 --- a/osu.Game.Rulesets.Mania/ManiaInputManager.cs +++ b/osu.Game.Rulesets.Mania/ManiaInputManager.cs @@ -78,5 +78,11 @@ namespace osu.Game.Rulesets.Mania [Description("Key 18")] Key18, + + [Description("Key 19")] + Key19, + + [Description("Key 20")] + Key20, } } diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 2147776d03..21315e4bfb 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -202,6 +202,7 @@ namespace osu.Game.Rulesets.Mania new ManiaModKey7(), new ManiaModKey8(), new ManiaModKey9(), + new ManiaModKey10(), new ManiaModKey1(), new ManiaModKey2(), new ManiaModKey3()), @@ -252,7 +253,7 @@ namespace osu.Game.Rulesets.Mania { for (int i = 1; i <= 10; i++) yield return (int)PlayfieldType.Single + i; - for (int i = 2; i <= 18; i += 2) + for (int i = 2; i <= 20; i += 2) yield return (int)PlayfieldType.Dual + i; } } @@ -262,102 +263,10 @@ namespace osu.Game.Rulesets.Mania switch (getPlayfieldType(variant)) { case PlayfieldType.Single: - switch (variant) - { - case 10: - // 10K is special because it extents one key towards the centre of the keyboard (V/N), rather than towards the edges of the keyboard. - return new VariantMappingGenerator - { - LeftKeys = new[] - { - InputKey.A, - InputKey.S, - InputKey.D, - InputKey.F, - InputKey.V - }, - RightKeys = new[] - { - InputKey.N, - InputKey.J, - InputKey.K, - InputKey.L, - InputKey.Semicolon, - }, - SpecialKey = InputKey.Space, - SpecialAction = ManiaAction.Special1, - NormalActionStart = ManiaAction.Key1, - }.GenerateKeyBindingsFor(variant, out _); - - default: - return new VariantMappingGenerator - { - LeftKeys = new[] - { - InputKey.A, - InputKey.S, - InputKey.D, - InputKey.F - }, - RightKeys = new[] - { - InputKey.J, - InputKey.K, - InputKey.L, - InputKey.Semicolon - }, - SpecialKey = InputKey.Space, - SpecialAction = ManiaAction.Special1, - NormalActionStart = ManiaAction.Key1, - }.GenerateKeyBindingsFor(variant, out _); - } + return new SingleStageVariantGenerator(variant).GenerateMappings(); case PlayfieldType.Dual: - int keys = getDualStageKeyCount(variant); - - var stage1Bindings = new VariantMappingGenerator - { - LeftKeys = new[] - { - InputKey.Q, - InputKey.W, - InputKey.E, - InputKey.R, - }, - RightKeys = new[] - { - InputKey.X, - InputKey.C, - InputKey.V, - InputKey.B - }, - SpecialKey = InputKey.S, - SpecialAction = ManiaAction.Special1, - NormalActionStart = ManiaAction.Key1 - }.GenerateKeyBindingsFor(keys, out var nextNormal); - - var stage2Bindings = new VariantMappingGenerator - { - LeftKeys = new[] - { - InputKey.Number7, - InputKey.Number8, - InputKey.Number9, - InputKey.Number0 - }, - RightKeys = new[] - { - InputKey.K, - InputKey.L, - InputKey.Semicolon, - InputKey.Quote - }, - SpecialKey = InputKey.I, - SpecialAction = ManiaAction.Special2, - NormalActionStart = nextNormal - }.GenerateKeyBindingsFor(keys, out _); - - return stage1Bindings.Concat(stage2Bindings); + return new DualStageVariantGenerator(getDualStageKeyCount(variant)).GenerateMappings(); } return Array.Empty(); @@ -393,59 +302,6 @@ namespace osu.Game.Rulesets.Mania { return (PlayfieldType)Enum.GetValues(typeof(PlayfieldType)).Cast().OrderByDescending(i => i).First(v => variant >= v); } - - private class VariantMappingGenerator - { - /// - /// All the s available to the left hand. - /// - public InputKey[] LeftKeys; - - /// - /// All the s available to the right hand. - /// - public InputKey[] RightKeys; - - /// - /// The for the special key. - /// - public InputKey SpecialKey; - - /// - /// The at which the normal columns should begin. - /// - public ManiaAction NormalActionStart; - - /// - /// The for the special column. - /// - public ManiaAction SpecialAction; - - /// - /// Generates a list of s for a specific number of columns. - /// - /// The number of columns that need to be bound. - /// The next to use for normal columns. - /// The keybindings. - public IEnumerable GenerateKeyBindingsFor(int columns, out ManiaAction nextNormalAction) - { - ManiaAction currentNormalAction = NormalActionStart; - - var bindings = new List(); - - for (int i = LeftKeys.Length - columns / 2; i < LeftKeys.Length; i++) - bindings.Add(new KeyBinding(LeftKeys[i], currentNormalAction++)); - - if (columns % 2 == 1) - bindings.Add(new KeyBinding(SpecialKey, SpecialAction)); - - for (int i = 0; i < columns / 2; i++) - bindings.Add(new KeyBinding(RightKeys[i], currentNormalAction++)); - - nextNormalAction = currentNormalAction; - return bindings; - } - } } public enum PlayfieldType diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModKey10.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModKey10.cs new file mode 100644 index 0000000000..684370fc3d --- /dev/null +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModKey10.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.Rulesets.Mania.Mods +{ + public class ManiaModKey10 : ManiaKeyMod + { + public override int KeyCount => 10; + public override string Name => "Ten Keys"; + public override string Acronym => "10K"; + public override string Description => @"Play with ten keys."; + } +} diff --git a/osu.Game.Rulesets.Mania/SingleStageVariantGenerator.cs b/osu.Game.Rulesets.Mania/SingleStageVariantGenerator.cs new file mode 100644 index 0000000000..2069329d9a --- /dev/null +++ b/osu.Game.Rulesets.Mania/SingleStageVariantGenerator.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 System.Collections.Generic; +using osu.Framework.Input.Bindings; + +namespace osu.Game.Rulesets.Mania +{ + public class SingleStageVariantGenerator + { + private readonly int variant; + private readonly InputKey[] leftKeys; + private readonly InputKey[] rightKeys; + + public SingleStageVariantGenerator(int variant) + { + this.variant = variant; + + // 10K is special because it expands towards the centre of the keyboard (V/N), rather than towards the edges of the keyboard. + if (variant == 10) + { + leftKeys = new[] { InputKey.A, InputKey.S, InputKey.D, InputKey.F, InputKey.V }; + rightKeys = new[] { InputKey.N, InputKey.J, InputKey.K, InputKey.L, InputKey.Semicolon }; + } + else + { + leftKeys = new[] { InputKey.A, InputKey.S, InputKey.D, InputKey.F }; + rightKeys = new[] { InputKey.J, InputKey.K, InputKey.L, InputKey.Semicolon }; + } + } + + public IEnumerable GenerateMappings() => new VariantMappingGenerator + { + LeftKeys = leftKeys, + RightKeys = rightKeys, + SpecialKey = InputKey.Space, + SpecialAction = ManiaAction.Special1, + NormalActionStart = ManiaAction.Key1, + }.GenerateKeyBindingsFor(variant, out _); + } +} diff --git a/osu.Game.Rulesets.Mania/VariantMappingGenerator.cs b/osu.Game.Rulesets.Mania/VariantMappingGenerator.cs new file mode 100644 index 0000000000..878d1088a6 --- /dev/null +++ b/osu.Game.Rulesets.Mania/VariantMappingGenerator.cs @@ -0,0 +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 System.Collections.Generic; +using osu.Framework.Input.Bindings; + +namespace osu.Game.Rulesets.Mania +{ + public class VariantMappingGenerator + { + /// + /// All the s available to the left hand. + /// + public InputKey[] LeftKeys; + + /// + /// All the s available to the right hand. + /// + public InputKey[] RightKeys; + + /// + /// The for the special key. + /// + public InputKey SpecialKey; + + /// + /// The at which the normal columns should begin. + /// + public ManiaAction NormalActionStart; + + /// + /// The for the special column. + /// + public ManiaAction SpecialAction; + + /// + /// Generates a list of s for a specific number of columns. + /// + /// The number of columns that need to be bound. + /// The next to use for normal columns. + /// The keybindings. + public IEnumerable GenerateKeyBindingsFor(int columns, out ManiaAction nextNormalAction) + { + ManiaAction currentNormalAction = NormalActionStart; + + var bindings = new List(); + + for (int i = LeftKeys.Length - columns / 2; i < LeftKeys.Length; i++) + bindings.Add(new KeyBinding(LeftKeys[i], currentNormalAction++)); + + if (columns % 2 == 1) + bindings.Add(new KeyBinding(SpecialKey, SpecialAction)); + + for (int i = 0; i < columns / 2; i++) + bindings.Add(new KeyBinding(RightKeys[i], currentNormalAction++)); + + nextNormalAction = currentNormalAction; + return bindings; + } + } +} From 9b6e26583bdb69c01219ab2b9ceb8d554883d1f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Apr 2020 21:42:43 +0200 Subject: [PATCH 0318/1117] Add xmldocs --- osu.Game/Screens/Select/BeatmapCarousel.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index cf3a5a7199..e21faf321e 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -28,7 +28,14 @@ namespace osu.Game.Screens.Select { public class BeatmapCarousel : CompositeDrawable, IKeyBindingHandler { + /// + /// Height of the area above the carousel that should be treated as visible due to transparency of elements in front of it. + /// public float BleedTop; + + /// + /// Height of the area below the carousel that should be treated as visible due to transparency of elements in front of it. + /// public float BleedBottom; /// From e3cd3cf1da7e8e9458697caccf6bb30c008d5791 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Apr 2020 21:43:07 +0200 Subject: [PATCH 0319/1117] Convert to auto-properties --- 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 e21faf321e..1bbd7c1270 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -31,12 +31,12 @@ namespace osu.Game.Screens.Select /// /// Height of the area above the carousel that should be treated as visible due to transparency of elements in front of it. /// - public float BleedTop; + public float BleedTop { get; set; } /// /// Height of the area below the carousel that should be treated as visible due to transparency of elements in front of it. /// - public float BleedBottom; + public float BleedBottom { get; set; } /// /// Triggered when the loaded change and are completely loaded. From 4c689c6ad2585087d8495514f755e4c25579b9d3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Apr 2020 10:56:04 +0900 Subject: [PATCH 0320/1117] Add constant for max stage keys --- .../Beatmaps/ManiaBeatmapConverter.cs | 2 +- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index 189dd17934..4187e39b43 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps { TargetColumns = (int)Math.Max(1, roundedCircleSize); - if (TargetColumns > 10) + if (TargetColumns > ManiaRuleset.MAX_STAGE_KEYS) { TargetColumns /= 2; Dual = true; diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 21315e4bfb..a37aaa8cc4 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -35,6 +35,11 @@ namespace osu.Game.Rulesets.Mania { public class ManiaRuleset : Ruleset, ILegacyRuleset { + /// + /// The maximum number of supported keys in a single stage. + /// + public const int MAX_STAGE_KEYS = 10; + public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList mods = null) => new DrawableManiaRuleset(this, beatmap, mods); public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(); @@ -251,9 +256,9 @@ namespace osu.Game.Rulesets.Mania { get { - for (int i = 1; i <= 10; i++) + for (int i = 1; i <= MAX_STAGE_KEYS; i++) yield return (int)PlayfieldType.Single + i; - for (int i = 2; i <= 20; i += 2) + for (int i = 2; i <= MAX_STAGE_KEYS * 2; i += 2) yield return (int)PlayfieldType.Dual + i; } } From a91c63819b0f781f1fdba608aa398a6612bb2e61 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Apr 2020 11:51:20 +0900 Subject: [PATCH 0321/1117] Refactor updateCompletionState implementation for legibility and code share --- osu.Game/Screens/Play/Player.cs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 2f3807753a..ece4c6307e 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -423,8 +423,6 @@ namespace osu.Game.Screens.Play if (!this.IsCurrentScreen()) return; - // cancel push delegate in case judges reverted - // after delegate may have already been scheduled. if (!completionState.NewValue) { completionProgressDelegate?.Cancel(); @@ -433,8 +431,11 @@ namespace osu.Game.Screens.Play return; } + if (completionProgressDelegate != null) + throw new InvalidOperationException($"{nameof(updateCompletionState)} was fired more than once"); + // Only show the completion screen if the player hasn't failed - if (HealthProcessor.HasFailed || completionProgressDelegate != null) + if (HealthProcessor.HasFailed) return; ValidForResume = false; @@ -442,7 +443,7 @@ namespace osu.Game.Screens.Play if (!showResults) return; using (BeginDelayedSequence(RESULTS_DISPLAY_DELAY)) - scheduleGotoRanking(); + completionProgressDelegate = Schedule(GotoRanking); } protected virtual ScoreInfo CreateScore() @@ -694,12 +695,6 @@ namespace osu.Game.Screens.Play storyboardReplacesBackground.Value = false; } - private void scheduleGotoRanking() - { - completionProgressDelegate?.Cancel(); - completionProgressDelegate = Schedule(GotoRanking); - } - #endregion } } From 9373520bca1ebc0f38d16bca5163eb9ed2e28a5c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 21 Apr 2020 05:59:37 +0300 Subject: [PATCH 0322/1117] Add constant for special colour of catcher on default skin --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index f37dae29dd..97d0fb0ada 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -25,6 +25,14 @@ namespace osu.Game.Rulesets.Catch.UI { public static readonly Color4 DEFAULT_HYPER_DASH_COLOUR = Color4.Red; + /// + /// The default colour used directly for . + /// + /// + /// This colour is only used when no skin overrides . + /// + public static readonly Color4 DEFAULT_CATCHER_HYPER_DASH_COLOUR = Color4.OrangeRed; + /// /// Whether we are hyper-dashing or not. /// @@ -285,7 +293,13 @@ namespace osu.Game.Rulesets.Catch.UI if (hyperDashing) { - this.FadeColour(hyperDashColour == DefaultHyperDashColour ? Color4.OrangeRed : hyperDashColour, hyper_dash_transition_length, Easing.OutQuint); + // special behaviour for catcher colour if no skin overrides. + var catcherColour = + hyperDashColour == DEFAULT_HYPER_DASH_COLOUR + ? DEFAULT_CATCHER_HYPER_DASH_COLOUR + : hyperDashColour; + + this.FadeColour(catcherColour, hyper_dash_transition_length, Easing.OutQuint); this.FadeTo(0.2f, hyper_dash_transition_length, Easing.OutQuint); } else From 282d1001093ac757eac1e65933577d1ac035e9d7 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 21 Apr 2020 06:09:57 +0300 Subject: [PATCH 0323/1117] Fix XMLDoc references --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 97d0fb0ada..056e838419 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Catch.UI public static readonly Color4 DEFAULT_HYPER_DASH_COLOUR = Color4.Red; /// - /// The default colour used directly for . + /// The default colour used directly for this 's . /// /// /// This colour is only used when no skin overrides . From 3b0099c687edce710dfcac9ac2d8d0e1a67153f4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Apr 2020 12:26:43 +0900 Subject: [PATCH 0324/1117] Refactor tests --- .../TestSceneCompletionCancellation.cs | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs index a3fb17942f..512584bd42 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs @@ -46,46 +46,54 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestCancelCompletionOnRewind() { - cancelCompletionSteps(); + complete(); + cancel(); - AddAssert("no attempt to push ranking", () => !((FakeRankingPushPlayer)Player).GotoRankingInvoked); + checkNoRanking(); } [Test] public void TestReCompleteAfterCancellation() { - cancelCompletionSteps(); - - // Attempt completing again. - AddStep("seek to completion again", () => track.Seek(5000)); - AddUntilStep("completion set by processor", () => Player.ScoreProcessor.HasCompleted.Value); + complete(); + cancel(); + complete(); AddUntilStep("attempted to push ranking", () => ((FakeRankingPushPlayer)Player).GotoRankingInvoked); } /// - /// Tests whether can still pause after cancelling completion - /// by reverting back to true. + /// Tests whether can still pause after cancelling completion by reverting back to true. /// [Test] public void TestCanPauseAfterCancellation() { - cancelCompletionSteps(); + complete(); + cancel(); AddStep("pause", () => Player.Pause()); AddAssert("paused successfully", () => Player.GameplayClockContainer.IsPaused.Value); + + checkNoRanking(); } - private void cancelCompletionSteps() + private void complete() { AddStep("seek to completion", () => track.Seek(5000)); AddUntilStep("completion set by processor", () => Player.ScoreProcessor.HasCompleted.Value); + } + private void cancel() + { AddStep("rewind to cancel", () => track.Seek(4000)); AddUntilStep("completion cleared by processor", () => !Player.ScoreProcessor.HasCompleted.Value); + } + private void checkNoRanking() + { // wait to ensure there was no attempt of pushing the results screen. AddWaitStep("wait", resultsDisplayWaitCount); + AddAssert("no attempt to push ranking", () => !((FakeRankingPushPlayer)Player).GotoRankingInvoked); } protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null) From 9252b7876b6221c5a9cc3128de91796278a4d2dd Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Apr 2020 13:58:23 +0900 Subject: [PATCH 0325/1117] Don't serialise AllControlPoints --- osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs index d33a922a32..af6ca24165 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs @@ -56,6 +56,7 @@ namespace osu.Game.Beatmaps.ControlPoints /// /// All control points, of all types. /// + [JsonIgnore] public IEnumerable AllControlPoints => Groups.SelectMany(g => g.ControlPoints).ToArray(); /// From 72fb34f82cf1ae65ad7a96ed98a40a12aae7b26b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Apr 2020 14:19:05 +0900 Subject: [PATCH 0326/1117] Fix overriding control points incorrectly --- .../Formats/LegacyBeatmapDecoderTest.cs | 5 ++++ .../Beatmaps/Formats/LegacyBeatmapDecoder.cs | 30 +++++++++---------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index 33f484a9aa..acb30a6277 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -241,6 +241,11 @@ namespace osu.Game.Tests.Beatmaps.Formats { var controlPoints = decoder.Decode(stream).ControlPointInfo; + Assert.That(controlPoints.TimingPoints.Count, Is.EqualTo(4)); + Assert.That(controlPoints.DifficultyPoints.Count, Is.EqualTo(3)); + Assert.That(controlPoints.EffectPoints.Count, Is.EqualTo(3)); + Assert.That(controlPoints.SamplePoints.Count, Is.EqualTo(3)); + Assert.That(controlPoints.DifficultyPointAt(500).SpeedMultiplier, Is.EqualTo(1.5).Within(0.1)); Assert.That(controlPoints.DifficultyPointAt(1500).SpeedMultiplier, Is.EqualTo(1.5).Within(0.1)); Assert.That(controlPoints.DifficultyPointAt(2500).SpeedMultiplier, Is.EqualTo(0.75).Within(0.1)); diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 33bb9774df..388abf4648 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -386,17 +386,10 @@ namespace osu.Game.Beatmaps.Formats SampleVolume = sampleVolume, CustomSampleBank = customSampleBank, }, timingChange); - - // To handle the scenario where a non-timing line shares the same time value as a subsequent timing line but - // appears earlier in the file, we buffer non-timing control points and rewrite them *after* control points from the timing line - // with the same time value (allowing them to overwrite as necessary). - // - // The expected outcome is that we prefer the non-timing line's adjustments over the timing line's adjustments when time is equal. - if (timingChange) - flushPendingPoints(); } private readonly List pendingControlPoints = new List(); + private readonly HashSet pendingControlPointTypes = new HashSet(); private double pendingControlPointsTime; private void addControlPoint(double time, ControlPoint point, bool timingChange) @@ -405,21 +398,28 @@ namespace osu.Game.Beatmaps.Formats flushPendingPoints(); if (timingChange) - { - beatmap.ControlPointInfo.Add(time, point); - return; - } + pendingControlPoints.Insert(0, point); + else + pendingControlPoints.Add(point); - pendingControlPoints.Add(point); pendingControlPointsTime = time; } private void flushPendingPoints() { - foreach (var p in pendingControlPoints) - beatmap.ControlPointInfo.Add(pendingControlPointsTime, p); + // Changes from non-timing-points are added to the end of the list (see addControlPoint()) and should override any changes from timing-points (added to the start of the list). + for (int i = pendingControlPoints.Count - 1; i >= 0; i--) + { + var type = pendingControlPoints[i].GetType(); + if (pendingControlPointTypes.Contains(type)) + continue; + + pendingControlPointTypes.Add(type); + beatmap.ControlPointInfo.Add(pendingControlPointsTime, pendingControlPoints[i]); + } pendingControlPoints.Clear(); + pendingControlPointTypes.Clear(); } private void handleHitObject(string line) From 89320b510c10a2aad660ab9c3938565828520fde Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Apr 2020 15:13:19 +0900 Subject: [PATCH 0327/1117] Apply class renaming --- .../Online/TestSceneBeatmapListingOverlay.cs | 2 +- ...> TestSceneBeatmapListingSearchControl.cs} | 26 ++++++++--------- ... TestSceneBeatmapListingSortTabControl.cs} | 5 ++-- ...dler.cs => BeatmapListingFilterControl.cs} | 28 +++++++++---------- ...tion.cs => BeatmapListingSearchControl.cs} | 4 +-- osu.Game/Overlays/BeatmapListingOverlay.cs | 2 +- 6 files changed, 33 insertions(+), 34 deletions(-) rename osu.Game.Tests/Visual/UserInterface/{TestSceneBeatmapListingSearchSection.cs => TestSceneBeatmapListingSearchControl.cs} (76%) rename osu.Game.Tests/Visual/UserInterface/{TestSceneBeatmapListingSort.cs => TestSceneBeatmapListingSortTabControl.cs} (91%) rename osu.Game/Overlays/BeatmapListing/{BeatmapListingSearchHandler.cs => BeatmapListingFilterControl.cs} (85%) rename osu.Game/Overlays/BeatmapListing/{BeatmapListingSearchSection.cs => BeatmapListingSearchControl.cs} (97%) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs index f80687e142..64d1a9ddcd 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs @@ -14,7 +14,7 @@ namespace osu.Game.Tests.Visual.Online public override IReadOnlyList RequiredTypes => new[] { typeof(BeatmapListingOverlay), - typeof(BeatmapListingSearchHandler) + typeof(BeatmapListingFilterControl) }; protected override bool UseOnlineAPI => true; diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchSection.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchControl.cs similarity index 76% rename from osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchSection.cs rename to osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchControl.cs index 69e3fbd75f..d6ede950df 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchSection.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchControl.cs @@ -15,19 +15,19 @@ using osuTK; namespace osu.Game.Tests.Visual.UserInterface { - public class TestSceneBeatmapListingSearchSection : OsuTestScene + public class TestSceneBeatmapListingSearchControl : OsuTestScene { public override IReadOnlyList RequiredTypes => new[] { - typeof(BeatmapListingSearchSection), + typeof(BeatmapListingSearchControl), }; [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); - private readonly BeatmapListingSearchSection section; + private readonly BeatmapListingSearchControl control; - public TestSceneBeatmapListingSearchSection() + public TestSceneBeatmapListingSearchControl() { OsuSpriteText query; OsuSpriteText ruleset; @@ -35,7 +35,7 @@ namespace osu.Game.Tests.Visual.UserInterface OsuSpriteText genre; OsuSpriteText language; - Add(section = new BeatmapListingSearchSection + Add(control = new BeatmapListingSearchControl { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -56,19 +56,19 @@ namespace osu.Game.Tests.Visual.UserInterface } }); - section.Query.BindValueChanged(q => query.Text = $"Query: {q.NewValue}", true); - section.Ruleset.BindValueChanged(r => ruleset.Text = $"Ruleset: {r.NewValue}", true); - section.Category.BindValueChanged(c => category.Text = $"Category: {c.NewValue}", true); - section.Genre.BindValueChanged(g => genre.Text = $"Genre: {g.NewValue}", true); - section.Language.BindValueChanged(l => language.Text = $"Language: {l.NewValue}", true); + control.Query.BindValueChanged(q => query.Text = $"Query: {q.NewValue}", true); + control.Ruleset.BindValueChanged(r => ruleset.Text = $"Ruleset: {r.NewValue}", true); + control.Category.BindValueChanged(c => category.Text = $"Category: {c.NewValue}", true); + control.Genre.BindValueChanged(g => genre.Text = $"Genre: {g.NewValue}", true); + control.Language.BindValueChanged(l => language.Text = $"Language: {l.NewValue}", true); } [Test] public void TestCovers() { - AddStep("Set beatmap", () => section.BeatmapSet = beatmap_set); - AddStep("Set beatmap (no cover)", () => section.BeatmapSet = no_cover_beatmap_set); - AddStep("Set null beatmap", () => section.BeatmapSet = null); + AddStep("Set beatmap", () => control.BeatmapSet = beatmap_set); + AddStep("Set beatmap (no cover)", () => control.BeatmapSet = no_cover_beatmap_set); + AddStep("Set null beatmap", () => control.BeatmapSet = null); } private static readonly BeatmapSetInfo beatmap_set = new BeatmapSetInfo diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSort.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSortTabControl.cs similarity index 91% rename from osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSort.cs rename to osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSortTabControl.cs index a5fa085abf..f643d4e3fe 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSort.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSortTabControl.cs @@ -13,18 +13,17 @@ using osuTK; namespace osu.Game.Tests.Visual.UserInterface { - public class TestSceneBeatmapListingSort : OsuTestScene + public class TestSceneBeatmapListingSortTabControl : OsuTestScene { public override IReadOnlyList RequiredTypes => new[] { - typeof(BeatmapListingSortTabControl), typeof(OverlaySortTabControl<>), }; [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); - public TestSceneBeatmapListingSort() + public TestSceneBeatmapListingSortTabControl() { BeatmapListingSortTabControl control; OsuSpriteText current; diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchHandler.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs similarity index 85% rename from osu.Game/Overlays/BeatmapListing/BeatmapListingSearchHandler.cs rename to osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs index ce3d37fb98..8817031bce 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchHandler.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs @@ -21,7 +21,7 @@ using osuTK.Graphics; namespace osu.Game.Overlays.BeatmapListing { - public class BeatmapListingSearchHandler : CompositeDrawable + public class BeatmapListingFilterControl : CompositeDrawable { public Action> SearchFinished; public Action SearchStarted; @@ -32,13 +32,13 @@ namespace osu.Game.Overlays.BeatmapListing [Resolved] private RulesetStore rulesets { get; set; } - private readonly BeatmapListingSearchSection searchSection; + private readonly BeatmapListingSearchControl searchControl; private readonly BeatmapListingSortTabControl sortControl; private readonly Box sortControlBackground; private SearchBeatmapSetsRequest getSetsRequest; - public BeatmapListingSearchHandler() + public BeatmapListingFilterControl() { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; @@ -62,7 +62,7 @@ namespace osu.Game.Overlays.BeatmapListing Radius = 3, Offset = new Vector2(0f, 1f), }, - Child = searchSection = new BeatmapListingSearchSection(), + Child = searchControl = new BeatmapListingSearchControl(), }, new Container { @@ -99,17 +99,17 @@ namespace osu.Game.Overlays.BeatmapListing var sortCriteria = sortControl.Current; var sortDirection = sortControl.SortDirection; - searchSection.Query.BindValueChanged(query => + searchControl.Query.BindValueChanged(query => { sortCriteria.Value = string.IsNullOrEmpty(query.NewValue) ? DirectSortCriteria.Ranked : DirectSortCriteria.Relevance; sortDirection.Value = SortDirection.Descending; queueUpdateSearch(true); }); - searchSection.Ruleset.BindValueChanged(_ => queueUpdateSearch()); - searchSection.Category.BindValueChanged(_ => queueUpdateSearch()); - searchSection.Genre.BindValueChanged(_ => queueUpdateSearch()); - searchSection.Language.BindValueChanged(_ => queueUpdateSearch()); + searchControl.Ruleset.BindValueChanged(_ => queueUpdateSearch()); + searchControl.Category.BindValueChanged(_ => queueUpdateSearch()); + searchControl.Genre.BindValueChanged(_ => queueUpdateSearch()); + searchControl.Language.BindValueChanged(_ => queueUpdateSearch()); sortCriteria.BindValueChanged(_ => queueUpdateSearch()); sortDirection.BindValueChanged(_ => queueUpdateSearch()); @@ -129,13 +129,13 @@ namespace osu.Game.Overlays.BeatmapListing private void updateSearch() { - getSetsRequest = new SearchBeatmapSetsRequest(searchSection.Query.Value, searchSection.Ruleset.Value) + getSetsRequest = new SearchBeatmapSetsRequest(searchControl.Query.Value, searchControl.Ruleset.Value) { - SearchCategory = searchSection.Category.Value, + SearchCategory = searchControl.Category.Value, SortCriteria = sortControl.Current.Value, SortDirection = sortControl.SortDirection.Value, - Genre = searchSection.Genre.Value, - Language = searchSection.Language.Value + Genre = searchControl.Genre.Value, + Language = searchControl.Language.Value }; getSetsRequest.Success += response => Schedule(() => onSearchFinished(response)); @@ -147,7 +147,7 @@ namespace osu.Game.Overlays.BeatmapListing { var beatmaps = response.BeatmapSets.Select(r => r.ToBeatmapSet(rulesets)).ToList(); - searchSection.BeatmapSet = response.Total == 0 ? null : beatmaps.First(); + searchControl.BeatmapSet = response.Total == 0 ? null : beatmaps.First(); SearchFinished?.Invoke(beatmaps); } diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchSection.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs similarity index 97% rename from osu.Game/Overlays/BeatmapListing/BeatmapListingSearchSection.cs rename to osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs index 3f9cc211df..9ae2696a22 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchSection.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs @@ -17,7 +17,7 @@ using osu.Game.Rulesets; namespace osu.Game.Overlays.BeatmapListing { - public class BeatmapListingSearchSection : CompositeDrawable + public class BeatmapListingSearchControl : CompositeDrawable { public Bindable Query => textBox.Current; @@ -53,7 +53,7 @@ namespace osu.Game.Overlays.BeatmapListing private readonly Box background; private readonly UpdateableBeatmapSetCover beatmapCover; - public BeatmapListingSearchSection() + public BeatmapListingSearchControl() { AutoSizeAxes = Axes.Y; RelativeSizeAxes = Axes.X; diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index 31dd692528..e16924464d 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -57,7 +57,7 @@ namespace osu.Game.Overlays Children = new Drawable[] { new BeatmapListingHeader(), - new BeatmapListingSearchHandler + new BeatmapListingFilterControl { SearchStarted = onSearchStarted, SearchFinished = onSearchFinished, From 5e3fad86cffb380f93eb78d1e541bb050b94094c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Apr 2020 15:28:25 +0900 Subject: [PATCH 0328/1117] Fix relax replays playing back incorrectly --- osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs | 13 +++++++++++-- osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs | 18 ++++++++++++++++-- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs b/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs index 1ef235f764..16414261a5 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs @@ -9,17 +9,26 @@ using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI; +using osu.Game.Screens.Play; using osuTK; namespace osu.Game.Rulesets.Catch.Mods { - public class CatchModRelax : ModRelax, IApplicableToDrawableRuleset + public class CatchModRelax : ModRelax, IApplicableToDrawableRuleset, IApplicableToPlayer { public override string Description => @"Use the mouse to control the catcher."; + private DrawableRuleset drawableRuleset; + public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { - drawableRuleset.Cursor.Add(new MouseInputHelper((CatchPlayfield)drawableRuleset.Playfield)); + this.drawableRuleset = drawableRuleset; + } + + public void ApplyToPlayer(Player player) + { + if (!drawableRuleset.HasReplayLoaded.Value) + drawableRuleset.Cursor.Add(new MouseInputHelper((CatchPlayfield)drawableRuleset.Playfield)); } private class MouseInputHelper : Drawable, IKeyBindingHandler, IRequireHighFrequencyMousePosition diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs index 9b0759d9d2..9a7b967117 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs @@ -11,11 +11,12 @@ using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.UI; +using osu.Game.Screens.Play; using static osu.Game.Input.Handlers.ReplayInputHandler; namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModRelax : ModRelax, IUpdatableByPlayfield, IApplicableToDrawableRuleset + public class OsuModRelax : ModRelax, IUpdatableByPlayfield, IApplicableToDrawableRuleset, IApplicableToPlayer { public override string Description => @"You don't need to click. Give your clicking/tapping fingers a break from the heat of things."; public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).ToArray(); @@ -33,15 +34,28 @@ namespace osu.Game.Rulesets.Osu.Mods private ReplayState state; private double lastStateChangeTime; + private bool hasReplay; + public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { // grab the input manager for future use. osuInputManager = (OsuInputManager)drawableRuleset.KeyBindingInputManager; - osuInputManager.AllowUserPresses = false; + } + + public void ApplyToPlayer(Player player) + { + if (osuInputManager.ReplayInputHandler != null) + { + hasReplay = true; + return; + } } public void Update(Playfield playfield) { + if (hasReplay) + return; + bool requiresHold = false; bool requiresHit = false; From c2ed6491a9953dce878acf3d36c2c4b9d35b0716 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Apr 2020 15:37:50 +0900 Subject: [PATCH 0329/1117] Move and shorten enum names --- .../TestSceneBeatmapSearchFilter.cs | 5 +- .../API/Requests/SearchBeatmapSetsRequest.cs | 96 ++----------------- .../BeatmapListingSearchControl.cs | 21 ++-- .../Overlays/BeatmapListing/SearchCategory.cs | 26 +++++ .../Overlays/BeatmapListing/SearchGenre.cs | 25 +++++ .../Overlays/BeatmapListing/SearchLanguage.cs | 47 +++++++++ 6 files changed, 119 insertions(+), 101 deletions(-) create mode 100644 osu.Game/Overlays/BeatmapListing/SearchCategory.cs create mode 100644 osu.Game/Overlays/BeatmapListing/SearchGenre.cs create mode 100644 osu.Game/Overlays/BeatmapListing/SearchLanguage.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapSearchFilter.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapSearchFilter.cs index fac58a6754..283fe03af3 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapSearchFilter.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapSearchFilter.cs @@ -8,7 +8,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.Containers; -using osu.Game.Online.API.Requests; using osu.Game.Overlays; using osu.Game.Overlays.BeatmapListing; using osuTK; @@ -41,8 +40,8 @@ namespace osu.Game.Tests.Visual.UserInterface Children = new Drawable[] { new BeatmapSearchRulesetFilterRow(), - new BeatmapSearchFilterRow("Categories"), - new BeatmapSearchFilterRow("Header Name") + new BeatmapSearchFilterRow("Categories"), + new BeatmapSearchFilterRow("Header Name") } }); } diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs index 1206563b18..8345be5f82 100644 --- a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs +++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs @@ -1,26 +1,25 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.ComponentModel; using osu.Framework.IO.Network; using osu.Game.Overlays; +using osu.Game.Overlays.BeatmapListing; using osu.Game.Overlays.Direct; using osu.Game.Rulesets; -using osu.Game.Utils; namespace osu.Game.Online.API.Requests { public class SearchBeatmapSetsRequest : APIRequest { - public BeatmapSearchCategory SearchCategory { get; set; } + public SearchCategory SearchCategory { get; set; } public DirectSortCriteria SortCriteria { get; set; } public SortDirection SortDirection { get; set; } - public BeatmapSearchGenre Genre { get; set; } + public SearchGenre Genre { get; set; } - public BeatmapSearchLanguage Language { get; set; } + public SearchLanguage Language { get; set; } private readonly string query; private readonly RulesetInfo ruleset; @@ -32,11 +31,11 @@ namespace osu.Game.Online.API.Requests this.query = string.IsNullOrEmpty(query) ? string.Empty : System.Uri.EscapeDataString(query); this.ruleset = ruleset; - SearchCategory = BeatmapSearchCategory.Any; + SearchCategory = SearchCategory.Any; SortCriteria = DirectSortCriteria.Ranked; SortDirection = SortDirection.Descending; - Genre = BeatmapSearchGenre.Any; - Language = BeatmapSearchLanguage.Any; + Genre = SearchGenre.Any; + Language = SearchLanguage.Any; } protected override WebRequest CreateWebRequest() @@ -49,10 +48,10 @@ namespace osu.Game.Online.API.Requests req.AddParameter("s", SearchCategory.ToString().ToLowerInvariant()); - if (Genre != BeatmapSearchGenre.Any) + if (Genre != SearchGenre.Any) req.AddParameter("g", ((int)Genre).ToString()); - if (Language != BeatmapSearchLanguage.Any) + if (Language != SearchLanguage.Any) req.AddParameter("l", ((int)Language).ToString()); req.AddParameter("sort", $"{SortCriteria.ToString().ToLowerInvariant()}_{directionString}"); @@ -62,81 +61,4 @@ namespace osu.Game.Online.API.Requests protected override string Target => @"beatmapsets/search"; } - - public enum BeatmapSearchCategory - { - Any, - - [Description("Has Leaderboard")] - Leaderboard, - Ranked, - Qualified, - Loved, - Favourites, - - [Description("Pending & WIP")] - Pending, - Graveyard, - - [Description("My Maps")] - Mine, - } - - public enum BeatmapSearchGenre - { - Any = 0, - Unspecified = 1, - - [Description("Video Game")] - VideoGame = 2, - Anime = 3, - Rock = 4, - Pop = 5, - Other = 6, - Novelty = 7, - - [Description("Hip Hop")] - HipHop = 9, - Electronic = 10 - } - - [HasOrderedElements] - public enum BeatmapSearchLanguage - { - [Order(0)] - Any, - - [Order(11)] - Other, - - [Order(1)] - English, - - [Order(6)] - Japanese, - - [Order(2)] - Chinese, - - [Order(10)] - Instrumental, - - [Order(7)] - Korean, - - [Order(3)] - French, - - [Order(4)] - German, - - [Order(9)] - Swedish, - - [Order(8)] - Spanish, - - [Order(5)] - Italian - } } diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs index 9ae2696a22..2ecdb18667 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs @@ -5,7 +5,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Game.Online.API.Requests; using osuTK; using osu.Framework.Bindables; using osu.Game.Beatmaps.Drawables; @@ -23,11 +22,11 @@ namespace osu.Game.Overlays.BeatmapListing public Bindable Ruleset => modeFilter.Current; - public Bindable Category => categoryFilter.Current; + public Bindable Category => categoryFilter.Current; - public Bindable Genre => genreFilter.Current; + public Bindable Genre => genreFilter.Current; - public Bindable Language => languageFilter.Current; + public Bindable Language => languageFilter.Current; public BeatmapSetInfo BeatmapSet { @@ -46,9 +45,9 @@ namespace osu.Game.Overlays.BeatmapListing private readonly BeatmapSearchTextBox textBox; private readonly BeatmapSearchRulesetFilterRow modeFilter; - private readonly BeatmapSearchFilterRow categoryFilter; - private readonly BeatmapSearchFilterRow genreFilter; - private readonly BeatmapSearchFilterRow languageFilter; + private readonly BeatmapSearchFilterRow categoryFilter; + private readonly BeatmapSearchFilterRow genreFilter; + private readonly BeatmapSearchFilterRow languageFilter; private readonly Box background; private readonly UpdateableBeatmapSetCover beatmapCover; @@ -103,9 +102,9 @@ namespace osu.Game.Overlays.BeatmapListing Children = new Drawable[] { modeFilter = new BeatmapSearchRulesetFilterRow(), - categoryFilter = new BeatmapSearchFilterRow(@"Categories"), - genreFilter = new BeatmapSearchFilterRow(@"Genre"), - languageFilter = new BeatmapSearchFilterRow(@"Language"), + categoryFilter = new BeatmapSearchFilterRow(@"Categories"), + genreFilter = new BeatmapSearchFilterRow(@"Genre"), + languageFilter = new BeatmapSearchFilterRow(@"Language"), } } } @@ -113,7 +112,7 @@ namespace osu.Game.Overlays.BeatmapListing } }); - categoryFilter.Current.Value = BeatmapSearchCategory.Leaderboard; + categoryFilter.Current.Value = SearchCategory.Leaderboard; } [BackgroundDependencyLoader] diff --git a/osu.Game/Overlays/BeatmapListing/SearchCategory.cs b/osu.Game/Overlays/BeatmapListing/SearchCategory.cs new file mode 100644 index 0000000000..84859bf5b5 --- /dev/null +++ b/osu.Game/Overlays/BeatmapListing/SearchCategory.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 System.ComponentModel; + +namespace osu.Game.Overlays.BeatmapListing +{ + public enum SearchCategory + { + Any, + + [Description("Has Leaderboard")] + Leaderboard, + Ranked, + Qualified, + Loved, + Favourites, + + [Description("Pending & WIP")] + Pending, + Graveyard, + + [Description("My Maps")] + Mine, + } +} diff --git a/osu.Game/Overlays/BeatmapListing/SearchGenre.cs b/osu.Game/Overlays/BeatmapListing/SearchGenre.cs new file mode 100644 index 0000000000..b12bba6249 --- /dev/null +++ b/osu.Game/Overlays/BeatmapListing/SearchGenre.cs @@ -0,0 +1,25 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.ComponentModel; + +namespace osu.Game.Overlays.BeatmapListing +{ + public enum SearchGenre + { + Any = 0, + Unspecified = 1, + + [Description("Video Game")] + VideoGame = 2, + Anime = 3, + Rock = 4, + Pop = 5, + Other = 6, + Novelty = 7, + + [Description("Hip Hop")] + HipHop = 9, + Electronic = 10 + } +} diff --git a/osu.Game/Overlays/BeatmapListing/SearchLanguage.cs b/osu.Game/Overlays/BeatmapListing/SearchLanguage.cs new file mode 100644 index 0000000000..dac7e4f1a2 --- /dev/null +++ b/osu.Game/Overlays/BeatmapListing/SearchLanguage.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 osu.Game.Utils; + +namespace osu.Game.Overlays.BeatmapListing +{ + [HasOrderedElements] + public enum SearchLanguage + { + [Order(0)] + Any, + + [Order(11)] + Other, + + [Order(1)] + English, + + [Order(6)] + Japanese, + + [Order(2)] + Chinese, + + [Order(10)] + Instrumental, + + [Order(7)] + Korean, + + [Order(3)] + French, + + [Order(4)] + German, + + [Order(9)] + Swedish, + + [Order(8)] + Spanish, + + [Order(5)] + Italian + } +} From eeb76120106b08a108f8036c95391a77fc5f968f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Apr 2020 15:40:08 +0900 Subject: [PATCH 0330/1117] Update DirectOverlay implementation --- osu.Game/Overlays/Direct/FilterControl.cs | 6 +++--- osu.Game/Overlays/DirectOverlay.cs | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Direct/FilterControl.cs b/osu.Game/Overlays/Direct/FilterControl.cs index e5b2b5cc34..4ab5544550 100644 --- a/osu.Game/Overlays/Direct/FilterControl.cs +++ b/osu.Game/Overlays/Direct/FilterControl.cs @@ -6,20 +6,20 @@ using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Game.Graphics; -using osu.Game.Online.API.Requests; +using osu.Game.Overlays.BeatmapListing; using osu.Game.Overlays.SearchableList; using osu.Game.Rulesets; using osuTK.Graphics; namespace osu.Game.Overlays.Direct { - public class FilterControl : SearchableListFilterControl + public class FilterControl : SearchableListFilterControl { private DirectRulesetSelector rulesetSelector; protected override Color4 BackgroundColour => Color4Extensions.FromHex(@"384552"); protected override DirectSortCriteria DefaultTab => DirectSortCriteria.Ranked; - protected override BeatmapSearchCategory DefaultCategory => BeatmapSearchCategory.Leaderboard; + protected override SearchCategory DefaultCategory => SearchCategory.Leaderboard; protected override Drawable CreateSupplementaryControls() => rulesetSelector = new DirectRulesetSelector(); diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs index 3eb88be690..5ed39af0dc 100644 --- a/osu.Game/Overlays/DirectOverlay.cs +++ b/osu.Game/Overlays/DirectOverlay.cs @@ -16,6 +16,7 @@ using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Online.API.Requests; +using osu.Game.Overlays.BeatmapListing; using osu.Game.Overlays.Direct; using osu.Game.Overlays.SearchableList; using osu.Game.Rulesets; @@ -24,7 +25,7 @@ using osuTK.Graphics; namespace osu.Game.Overlays { - public class DirectOverlay : SearchableListOverlay + public class DirectOverlay : SearchableListOverlay { private const float panel_padding = 10f; @@ -40,7 +41,7 @@ namespace osu.Game.Overlays protected override Color4 TrianglesColourDark => Color4Extensions.FromHex(@"3f5265"); protected override SearchableListHeader CreateHeader() => new Header(); - protected override SearchableListFilterControl CreateFilterControl() => new FilterControl(); + protected override SearchableListFilterControl CreateFilterControl() => new FilterControl(); private IEnumerable beatmapSets; From 1f0b7465e2410c8c69d16f31becd52e076602901 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Apr 2020 16:06:40 +0900 Subject: [PATCH 0331/1117] Add back missing line --- osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs index 9a7b967117..7b1941b7f9 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs @@ -49,6 +49,8 @@ namespace osu.Game.Rulesets.Osu.Mods hasReplay = true; return; } + + osuInputManager.AllowUserPresses = false; } public void Update(Playfield playfield) From 594cef14738f99d3f0fa3466756bcf151c3df4ac Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Apr 2020 15:47:43 +0900 Subject: [PATCH 0332/1117] Fix BeatmapListingOverlay not taking focus --- .../BeatmapListing/BeatmapListingFilterControl.cs | 2 ++ .../BeatmapListing/BeatmapListingSearchControl.cs | 2 ++ osu.Game/Overlays/BeatmapListingOverlay.cs | 12 +++++++++++- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs index 8817031bce..8c50409783 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs @@ -159,5 +159,7 @@ namespace osu.Game.Overlays.BeatmapListing base.Dispose(isDisposing); } + + public void TakeFocus() => searchControl.TakeFocus(); } } diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs index 2ecdb18667..29c4fe0d2e 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs @@ -121,6 +121,8 @@ namespace osu.Game.Overlays.BeatmapListing background.Colour = colourProvider.Dark6; } + public void TakeFocus() => textBox.TakeFocus(); + private class BeatmapSearchTextBox : SearchTextBox { protected override Color4 SelectionColour => Color4.Gray; diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index e16924464d..000ca6b91c 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; +using osu.Framework.Input.Events; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Graphics.Containers; @@ -35,6 +36,8 @@ namespace osu.Game.Overlays { } + private BeatmapListingFilterControl filterControl; + [BackgroundDependencyLoader] private void load() { @@ -57,7 +60,7 @@ namespace osu.Game.Overlays Children = new Drawable[] { new BeatmapListingHeader(), - new BeatmapListingFilterControl + filterControl = new BeatmapListingFilterControl { SearchStarted = onSearchStarted, SearchFinished = onSearchFinished, @@ -88,6 +91,13 @@ namespace osu.Game.Overlays }; } + protected override void OnFocus(FocusEvent e) + { + base.OnFocus(e); + + filterControl.TakeFocus(); + } + private CancellationTokenSource cancellationToken; private void onSearchStarted() From 1cec0575b78203dbc257b4d72851012d37eeac91 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Apr 2020 16:00:00 +0900 Subject: [PATCH 0333/1117] Remove unused classes and replace overlay in game --- .../Visual/Online/TestSceneDirectOverlay.cs | 215 ------------- osu.Game.Tests/Visual/TestSceneOsuGame.cs | 7 + .../API/Requests/SearchBeatmapSetsRequest.cs | 5 +- osu.Game/OsuGame.cs | 8 +- .../BeatmapListingFilterControl.cs | 3 +- .../BeatmapListingSortTabControl.cs | 13 +- .../Overlays/BeatmapListing/SortCriteria.cs | 17 + .../Overlays/Direct/DirectRulesetSelector.cs | 93 ------ osu.Game/Overlays/Direct/FilterControl.cs | 47 --- osu.Game/Overlays/Direct/Header.cs | 43 --- osu.Game/Overlays/DirectOverlay.cs | 299 ------------------ osu.Game/Overlays/SocialOverlay.cs | 6 - osu.Game/Overlays/SortDirection.cs | 11 + osu.Game/Overlays/Toolbar/Toolbar.cs | 2 +- ...tton.cs => ToolbarBeatmapListingButton.cs} | 8 +- osu.Game/Screens/Menu/ButtonSystem.cs | 4 +- osu.Game/Screens/Menu/MainMenu.cs | 4 +- 17 files changed, 57 insertions(+), 728 deletions(-) delete mode 100644 osu.Game.Tests/Visual/Online/TestSceneDirectOverlay.cs create mode 100644 osu.Game/Overlays/BeatmapListing/SortCriteria.cs delete mode 100644 osu.Game/Overlays/Direct/DirectRulesetSelector.cs delete mode 100644 osu.Game/Overlays/Direct/FilterControl.cs delete mode 100644 osu.Game/Overlays/Direct/Header.cs delete mode 100644 osu.Game/Overlays/DirectOverlay.cs create mode 100644 osu.Game/Overlays/SortDirection.cs rename osu.Game/Overlays/Toolbar/{ToolbarDirectButton.cs => ToolbarBeatmapListingButton.cs} (63%) diff --git a/osu.Game.Tests/Visual/Online/TestSceneDirectOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneDirectOverlay.cs deleted file mode 100644 index d9873ea243..0000000000 --- a/osu.Game.Tests/Visual/Online/TestSceneDirectOverlay.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.Collections.Generic; -using NUnit.Framework; -using osu.Game.Beatmaps; -using osu.Game.Overlays; - -namespace osu.Game.Tests.Visual.Online -{ - [TestFixture] - public class TestSceneDirectOverlay : OsuTestScene - { - private DirectOverlay direct; - - protected override bool UseOnlineAPI => true; - - protected override void LoadComplete() - { - base.LoadComplete(); - - Add(direct = new DirectOverlay()); - newBeatmaps(); - - AddStep(@"toggle", direct.ToggleVisibility); - AddStep(@"result counts", () => direct.ResultAmounts = new DirectOverlay.ResultCounts(1, 4, 13)); - AddStep(@"trigger disabled", () => Ruleset.Disabled = !Ruleset.Disabled); - } - - private void newBeatmaps() - { - direct.BeatmapSets = new[] - { - new BeatmapSetInfo - { - OnlineBeatmapSetID = 578332, - Metadata = new BeatmapMetadata - { - Title = @"OrVid", - Artist = @"An", - AuthorString = @"RLC", - Source = @"", - Tags = @"acuticnotes an-fillnote revid tear tearvid encrpted encryption axi axivid quad her hervid recoll", - }, - OnlineInfo = new BeatmapSetOnlineInfo - { - Covers = new BeatmapSetOnlineCovers - { - Card = @"https://assets.ppy.sh/beatmaps/578332/covers/card.jpg?1494591390", - Cover = @"https://assets.ppy.sh/beatmaps/578332/covers/cover.jpg?1494591390", - }, - Preview = @"https://b.ppy.sh/preview/578332.mp3", - PlayCount = 97, - FavouriteCount = 72, - }, - Beatmaps = new List - { - new BeatmapInfo - { - Ruleset = Ruleset.Value, - StarDifficulty = 5.35f, - Metadata = new BeatmapMetadata(), - }, - }, - }, - new BeatmapSetInfo - { - OnlineBeatmapSetID = 599627, - Metadata = new BeatmapMetadata - { - Title = @"tiny lamp", - Artist = @"fhana", - AuthorString = @"Sotarks", - Source = @"ぎんぎつね", - Tags = @"lantis junichi sato yuxuki waga kevin mitsunaga towana gingitsune opening op full ver version kalibe collab collaboration", - }, - OnlineInfo = new BeatmapSetOnlineInfo - { - Covers = new BeatmapSetOnlineCovers - { - Card = @"https://assets.ppy.sh/beatmaps/599627/covers/card.jpg?1494539318", - Cover = @"https://assets.ppy.sh/beatmaps/599627/covers/cover.jpg?1494539318", - }, - Preview = @"https//b.ppy.sh/preview/599627.mp3", - PlayCount = 3082, - FavouriteCount = 14, - }, - Beatmaps = new List - { - new BeatmapInfo - { - Ruleset = Ruleset.Value, - StarDifficulty = 5.81f, - Metadata = new BeatmapMetadata(), - }, - }, - }, - new BeatmapSetInfo - { - OnlineBeatmapSetID = 513268, - Metadata = new BeatmapMetadata - { - Title = @"At Gwanghwamun", - Artist = @"KYUHYUN", - AuthorString = @"Cerulean Veyron", - Source = @"", - Tags = @"soul ballad kh super junior sj suju 슈퍼주니어 kt뮤직 sm엔터테인먼트 s.m.entertainment kt music 1st mini album ep", - }, - OnlineInfo = new BeatmapSetOnlineInfo - { - Covers = new BeatmapSetOnlineCovers - { - Card = @"https://assets.ppy.sh/beatmaps/513268/covers/card.jpg?1494502863", - Cover = @"https://assets.ppy.sh/beatmaps/513268/covers/cover.jpg?1494502863", - }, - Preview = @"https//b.ppy.sh/preview/513268.mp3", - PlayCount = 2762, - FavouriteCount = 15, - }, - Beatmaps = new List - { - new BeatmapInfo - { - Ruleset = Ruleset.Value, - StarDifficulty = 0.9f, - Metadata = new BeatmapMetadata(), - }, - new BeatmapInfo - { - Ruleset = Ruleset.Value, - StarDifficulty = 1.1f, - }, - new BeatmapInfo - { - Ruleset = Ruleset.Value, - StarDifficulty = 2.02f, - }, - new BeatmapInfo - { - Ruleset = Ruleset.Value, - StarDifficulty = 3.49f, - }, - }, - }, - new BeatmapSetInfo - { - OnlineBeatmapSetID = 586841, - Metadata = new BeatmapMetadata - { - Title = @"RHAPSODY OF BLUE SKY", - Artist = @"fhana", - AuthorString = @"[Kamiya]", - Source = @"小林さんちのメイドラゴン", - Tags = @"kobayashi san chi no maidragon aozora no opening anime maid dragon oblivion karen dynamix imoutosan pata-mon gxytcgxytc", - }, - OnlineInfo = new BeatmapSetOnlineInfo - { - Covers = new BeatmapSetOnlineCovers - { - Card = @"https://assets.ppy.sh/beatmaps/586841/covers/card.jpg?1494052741", - Cover = @"https://assets.ppy.sh/beatmaps/586841/covers/cover.jpg?1494052741", - }, - Preview = @"https//b.ppy.sh/preview/586841.mp3", - PlayCount = 62317, - FavouriteCount = 161, - }, - Beatmaps = new List - { - new BeatmapInfo - { - Ruleset = Ruleset.Value, - StarDifficulty = 1.26f, - Metadata = new BeatmapMetadata(), - }, - new BeatmapInfo - { - Ruleset = Ruleset.Value, - StarDifficulty = 2.01f, - }, - new BeatmapInfo - { - Ruleset = Ruleset.Value, - StarDifficulty = 2.87f, - }, - new BeatmapInfo - { - Ruleset = Ruleset.Value, - StarDifficulty = 3.76f, - }, - new BeatmapInfo - { - Ruleset = Ruleset.Value, - StarDifficulty = 3.93f, - }, - new BeatmapInfo - { - Ruleset = Ruleset.Value, - StarDifficulty = 4.37f, - }, - new BeatmapInfo - { - Ruleset = Ruleset.Value, - StarDifficulty = 5.13f, - }, - new BeatmapInfo - { - Ruleset = Ruleset.Value, - StarDifficulty = 5.42f, - }, - }, - }, - }; - } - } -} diff --git a/osu.Game.Tests/Visual/TestSceneOsuGame.cs b/osu.Game.Tests/Visual/TestSceneOsuGame.cs index 8793d880e3..d68217dcfd 100644 --- a/osu.Game.Tests/Visual/TestSceneOsuGame.cs +++ b/osu.Game.Tests/Visual/TestSceneOsuGame.cs @@ -47,8 +47,15 @@ namespace osu.Game.Tests.Visual typeof(IdleTracker), typeof(OnScreenDisplay), typeof(NotificationOverlay), +<<<<<<< HEAD typeof(DirectOverlay), typeof(DashboardOverlay), +||||||| parent of 96a3a08a9... Remove unused classes and replace overlay in game + typeof(DirectOverlay), + typeof(SocialOverlay), +======= + typeof(SocialOverlay), +>>>>>>> 96a3a08a9... Remove unused classes and replace overlay in game typeof(ChannelManager), typeof(ChatOverlay), typeof(SettingsOverlay), diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs index 8345be5f82..047496b473 100644 --- a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs +++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs @@ -4,7 +4,6 @@ using osu.Framework.IO.Network; using osu.Game.Overlays; using osu.Game.Overlays.BeatmapListing; -using osu.Game.Overlays.Direct; using osu.Game.Rulesets; namespace osu.Game.Online.API.Requests @@ -13,7 +12,7 @@ namespace osu.Game.Online.API.Requests { public SearchCategory SearchCategory { get; set; } - public DirectSortCriteria SortCriteria { get; set; } + public SortCriteria SortCriteria { get; set; } public SortDirection SortDirection { get; set; } @@ -32,7 +31,7 @@ namespace osu.Game.Online.API.Requests this.ruleset = ruleset; SearchCategory = SearchCategory.Any; - SortCriteria = DirectSortCriteria.Ranked; + SortCriteria = SortCriteria.Ranked; SortDirection = SortDirection.Descending; Genre = SearchGenre.Any; Language = SearchLanguage.Any; diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index c861b84835..f5f7d0cef4 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -65,7 +65,7 @@ namespace osu.Game private NowPlayingOverlay nowPlaying; - private DirectOverlay direct; + private BeatmapListingOverlay beatmapListing; private DashboardOverlay dashboard; @@ -610,7 +610,7 @@ namespace osu.Game loadComponentSingleFile(screenshotManager, Add); //overlay elements - loadComponentSingleFile(direct = new DirectOverlay(), overlayContent.Add, true); + loadComponentSingleFile(beatmapListing = new BeatmapListingOverlay(), overlayContent.Add, true); loadComponentSingleFile(dashboard = new DashboardOverlay(), overlayContent.Add, true); var rankingsOverlay = loadComponentSingleFile(new RankingsOverlay(), overlayContent.Add, true); loadComponentSingleFile(channelManager = new ChannelManager(), AddInternal, true); @@ -670,7 +670,7 @@ namespace osu.Game } // ensure only one of these overlays are open at once. - var singleDisplayOverlays = new OverlayContainer[] { chatOverlay, dashboard, direct, changelogOverlay, rankingsOverlay }; + var singleDisplayOverlays = new OverlayContainer[] { chatOverlay, dashboard, beatmapListing, changelogOverlay, rankingsOverlay }; foreach (var overlay in singleDisplayOverlays) { @@ -865,7 +865,7 @@ namespace osu.Game return true; case GlobalAction.ToggleDirect: - direct.ToggleVisibility(); + beatmapListing.ToggleVisibility(); return true; case GlobalAction.ToggleGameplayMouseButtons: diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs index 8c50409783..4dd60c7113 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs @@ -14,7 +14,6 @@ using osu.Framework.Threading; using osu.Game.Beatmaps; using osu.Game.Online.API; using osu.Game.Online.API.Requests; -using osu.Game.Overlays.Direct; using osu.Game.Rulesets; using osuTK; using osuTK.Graphics; @@ -101,7 +100,7 @@ namespace osu.Game.Overlays.BeatmapListing searchControl.Query.BindValueChanged(query => { - sortCriteria.Value = string.IsNullOrEmpty(query.NewValue) ? DirectSortCriteria.Ranked : DirectSortCriteria.Relevance; + sortCriteria.Value = string.IsNullOrEmpty(query.NewValue) ? SortCriteria.Ranked : SortCriteria.Relevance; sortDirection.Value = SortDirection.Descending; queueUpdateSearch(true); }); diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingSortTabControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingSortTabControl.cs index 27c43b092a..4c77a736ac 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingSortTabControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingSortTabControl.cs @@ -8,17 +8,16 @@ using osu.Framework.Graphics; using osuTK.Graphics; using osuTK; using osu.Framework.Input.Events; -using osu.Game.Overlays.Direct; namespace osu.Game.Overlays.BeatmapListing { - public class BeatmapListingSortTabControl : OverlaySortTabControl + public class BeatmapListingSortTabControl : OverlaySortTabControl { public readonly Bindable SortDirection = new Bindable(Overlays.SortDirection.Descending); public BeatmapListingSortTabControl() { - Current.Value = DirectSortCriteria.Ranked; + Current.Value = SortCriteria.Ranked; } protected override SortTabControl CreateControl() => new BeatmapSortTabControl @@ -30,7 +29,7 @@ namespace osu.Game.Overlays.BeatmapListing { public readonly Bindable SortDirection = new Bindable(); - protected override TabItem CreateTabItem(DirectSortCriteria value) => new BeatmapSortTabItem(value) + protected override TabItem CreateTabItem(SortCriteria value) => new BeatmapSortTabItem(value) { SortDirection = { BindTarget = SortDirection } }; @@ -40,12 +39,12 @@ namespace osu.Game.Overlays.BeatmapListing { public readonly Bindable SortDirection = new Bindable(); - public BeatmapSortTabItem(DirectSortCriteria value) + public BeatmapSortTabItem(SortCriteria value) : base(value) { } - protected override TabButton CreateTabButton(DirectSortCriteria value) => new BeatmapTabButton(value) + protected override TabButton CreateTabButton(SortCriteria value) => new BeatmapTabButton(value) { Active = { BindTarget = Active }, SortDirection = { BindTarget = SortDirection } @@ -67,7 +66,7 @@ namespace osu.Game.Overlays.BeatmapListing private readonly SpriteIcon icon; - public BeatmapTabButton(DirectSortCriteria value) + public BeatmapTabButton(SortCriteria value) : base(value) { Add(icon = new SpriteIcon diff --git a/osu.Game/Overlays/BeatmapListing/SortCriteria.cs b/osu.Game/Overlays/BeatmapListing/SortCriteria.cs new file mode 100644 index 0000000000..e409cbdda7 --- /dev/null +++ b/osu.Game/Overlays/BeatmapListing/SortCriteria.cs @@ -0,0 +1,17 @@ +// 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.Overlays.BeatmapListing +{ + public enum SortCriteria + { + Title, + Artist, + Difficulty, + Ranked, + Rating, + Plays, + Favourites, + Relevance + } +} diff --git a/osu.Game/Overlays/Direct/DirectRulesetSelector.cs b/osu.Game/Overlays/Direct/DirectRulesetSelector.cs deleted file mode 100644 index 106aaa616b..0000000000 --- a/osu.Game/Overlays/Direct/DirectRulesetSelector.cs +++ /dev/null @@ -1,93 +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; -using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input.Events; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.UserInterface; -using osu.Game.Rulesets; -using osuTK; -using osuTK.Graphics; - -namespace osu.Game.Overlays.Direct -{ - public class DirectRulesetSelector : RulesetSelector - { - public override bool HandleNonPositionalInput => !Current.Disabled && base.HandleNonPositionalInput; - - public override bool HandlePositionalInput => !Current.Disabled && base.HandlePositionalInput; - - public override bool PropagatePositionalInputSubTree => !Current.Disabled && base.PropagatePositionalInputSubTree; - - public DirectRulesetSelector() - { - TabContainer.Masking = false; - TabContainer.Spacing = new Vector2(10, 0); - AutoSizeAxes = Axes.Both; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - Current.BindDisabledChanged(value => SelectedTab.FadeColour(value ? Color4.DarkGray : Color4.White, 200, Easing.OutQuint), true); - } - - protected override TabItem CreateTabItem(RulesetInfo value) => new DirectRulesetTabItem(value); - - protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer - { - Direction = FillDirection.Horizontal, - AutoSizeAxes = Axes.Both, - }; - - private class DirectRulesetTabItem : TabItem - { - private readonly ConstrainedIconContainer iconContainer; - - public DirectRulesetTabItem(RulesetInfo value) - : base(value) - { - AutoSizeAxes = Axes.Both; - - Children = new Drawable[] - { - iconContainer = new ConstrainedIconContainer - { - Icon = value.CreateInstance().CreateIcon(), - Size = new Vector2(32), - }, - new HoverClickSounds() - }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - updateState(); - } - - protected override bool OnHover(HoverEvent e) - { - base.OnHover(e); - updateState(); - return true; - } - - protected override void OnHoverLost(HoverLostEvent e) - { - base.OnHoverLost(e); - updateState(); - } - - protected override void OnActivated() => updateState(); - - protected override void OnDeactivated() => updateState(); - - private void updateState() => iconContainer.FadeColour(IsHovered || Active.Value ? Color4.White : Color4.Gray, 120, Easing.InQuad); - } - } -} diff --git a/osu.Game/Overlays/Direct/FilterControl.cs b/osu.Game/Overlays/Direct/FilterControl.cs deleted file mode 100644 index 4ab5544550..0000000000 --- a/osu.Game/Overlays/Direct/FilterControl.cs +++ /dev/null @@ -1,47 +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.Framework.Bindables; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Game.Graphics; -using osu.Game.Overlays.BeatmapListing; -using osu.Game.Overlays.SearchableList; -using osu.Game.Rulesets; -using osuTK.Graphics; - -namespace osu.Game.Overlays.Direct -{ - public class FilterControl : SearchableListFilterControl - { - private DirectRulesetSelector rulesetSelector; - - protected override Color4 BackgroundColour => Color4Extensions.FromHex(@"384552"); - protected override DirectSortCriteria DefaultTab => DirectSortCriteria.Ranked; - protected override SearchCategory DefaultCategory => SearchCategory.Leaderboard; - - protected override Drawable CreateSupplementaryControls() => rulesetSelector = new DirectRulesetSelector(); - - public Bindable Ruleset => rulesetSelector.Current; - - [BackgroundDependencyLoader(true)] - private void load(OsuColour colours, Bindable ruleset) - { - DisplayStyleControl.Dropdown.AccentColour = colours.BlueDark; - rulesetSelector.Current.BindTo(ruleset); - } - } - - public enum DirectSortCriteria - { - Title, - Artist, - Difficulty, - Ranked, - Rating, - Plays, - Favourites, - Relevance, - } -} diff --git a/osu.Game/Overlays/Direct/Header.cs b/osu.Game/Overlays/Direct/Header.cs deleted file mode 100644 index 5b3e394a18..0000000000 --- a/osu.Game/Overlays/Direct/Header.cs +++ /dev/null @@ -1,43 +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.ComponentModel; -using osu.Framework.Extensions.Color4Extensions; -using osuTK.Graphics; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Overlays.SearchableList; - -namespace osu.Game.Overlays.Direct -{ - public class Header : SearchableListHeader - { - protected override Color4 BackgroundColour => Color4Extensions.FromHex(@"252f3a"); - - protected override DirectTab DefaultTab => DirectTab.Search; - protected override Drawable CreateHeaderText() => new OsuSpriteText { Text = @"osu!direct", Font = OsuFont.GetFont(size: 25) }; - protected override IconUsage Icon => OsuIcon.ChevronDownCircle; - - public Header() - { - Tabs.Current.Value = DirectTab.NewestMaps; - Tabs.Current.TriggerChange(); - } - } - - public enum DirectTab - { - Search, - - [Description("Newest Maps")] - NewestMaps = DirectSortCriteria.Ranked, - - [Description("Top Rated")] - TopRated = DirectSortCriteria.Rating, - - [Description("Most Played")] - MostPlayed = DirectSortCriteria.Plays, - } -} diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs deleted file mode 100644 index 5ed39af0dc..0000000000 --- a/osu.Game/Overlays/DirectOverlay.cs +++ /dev/null @@ -1,299 +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.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Humanizer; -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.Threading; -using osu.Game.Audio; -using osu.Game.Beatmaps; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Online.API.Requests; -using osu.Game.Overlays.BeatmapListing; -using osu.Game.Overlays.Direct; -using osu.Game.Overlays.SearchableList; -using osu.Game.Rulesets; -using osuTK; -using osuTK.Graphics; - -namespace osu.Game.Overlays -{ - public class DirectOverlay : SearchableListOverlay - { - private const float panel_padding = 10f; - - [Resolved] - private RulesetStore rulesets { get; set; } - - private readonly FillFlowContainer resultCountsContainer; - private readonly OsuSpriteText resultCountsText; - private FillFlowContainer panels; - - protected override Color4 BackgroundColour => Color4Extensions.FromHex(@"485e74"); - protected override Color4 TrianglesColourLight => Color4Extensions.FromHex(@"465b71"); - protected override Color4 TrianglesColourDark => Color4Extensions.FromHex(@"3f5265"); - - protected override SearchableListHeader CreateHeader() => new Header(); - protected override SearchableListFilterControl CreateFilterControl() => new FilterControl(); - - private IEnumerable beatmapSets; - - public IEnumerable BeatmapSets - { - get => beatmapSets; - set - { - if (ReferenceEquals(beatmapSets, value)) return; - - beatmapSets = value?.ToList(); - - if (beatmapSets == null) return; - - var artists = new List(); - var songs = new List(); - var tags = new List(); - - foreach (var s in beatmapSets) - { - artists.Add(s.Metadata.Artist); - songs.Add(s.Metadata.Title); - tags.AddRange(s.Metadata.Tags.Split(' ')); - } - - ResultAmounts = new ResultCounts(distinctCount(artists), distinctCount(songs), distinctCount(tags)); - } - } - - private ResultCounts resultAmounts; - - public ResultCounts ResultAmounts - { - get => resultAmounts; - set - { - if (value == ResultAmounts) return; - - resultAmounts = value; - - updateResultCounts(); - } - } - - public DirectOverlay() - : base(OverlayColourScheme.Blue) - { - ScrollFlow.Children = new Drawable[] - { - resultCountsContainer = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Margin = new MarginPadding { Top = 5 }, - Children = new Drawable[] - { - new OsuSpriteText - { - Text = "Found ", - Font = OsuFont.GetFont(size: 15) - }, - resultCountsText = new OsuSpriteText - { - Font = OsuFont.GetFont(size: 15, weight: FontWeight.Bold) - }, - } - }, - }; - - Filter.Search.Current.ValueChanged += text => - { - if (!string.IsNullOrEmpty(text.NewValue)) - { - Header.Tabs.Current.Value = DirectTab.Search; - - if (Filter.Tabs.Current.Value == DirectSortCriteria.Ranked) - Filter.Tabs.Current.Value = DirectSortCriteria.Relevance; - } - else - { - Header.Tabs.Current.Value = DirectTab.NewestMaps; - - if (Filter.Tabs.Current.Value == DirectSortCriteria.Relevance) - Filter.Tabs.Current.Value = DirectSortCriteria.Ranked; - } - }; - ((FilterControl)Filter).Ruleset.ValueChanged += _ => queueUpdateSearch(); - Filter.DisplayStyleControl.DisplayStyle.ValueChanged += style => recreatePanels(style.NewValue); - Filter.DisplayStyleControl.Dropdown.Current.ValueChanged += _ => queueUpdateSearch(); - - Header.Tabs.Current.ValueChanged += tab => - { - if (tab.NewValue != DirectTab.Search) - { - currentQuery.Value = string.Empty; - Filter.Tabs.Current.Value = (DirectSortCriteria)Header.Tabs.Current.Value; - queueUpdateSearch(); - } - }; - - currentQuery.ValueChanged += text => queueUpdateSearch(!string.IsNullOrEmpty(text.NewValue)); - - currentQuery.BindTo(Filter.Search.Current); - - Filter.Tabs.Current.ValueChanged += tab => - { - if (Header.Tabs.Current.Value != DirectTab.Search && tab.NewValue != (DirectSortCriteria)Header.Tabs.Current.Value) - Header.Tabs.Current.Value = DirectTab.Search; - - queueUpdateSearch(); - }; - - updateResultCounts(); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - resultCountsContainer.Colour = colours.Yellow; - } - - private void updateResultCounts() - { - resultCountsContainer.FadeTo(ResultAmounts == null ? 0f : 1f, 200, Easing.OutQuint); - if (ResultAmounts == null) return; - - resultCountsText.Text = "Artist".ToQuantity(ResultAmounts.Artists) + ", " + - "Song".ToQuantity(ResultAmounts.Songs) + ", " + - "Tag".ToQuantity(ResultAmounts.Tags); - } - - private void recreatePanels(PanelDisplayStyle displayStyle) - { - if (panels != null) - { - panels.FadeOut(200); - panels.Expire(); - panels = null; - } - - if (BeatmapSets == null) return; - - var newPanels = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Spacing = new Vector2(panel_padding), - Margin = new MarginPadding { Top = 10 }, - ChildrenEnumerable = BeatmapSets.Select(b => - { - switch (displayStyle) - { - case PanelDisplayStyle.Grid: - return new DirectGridPanel(b) - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - }; - - default: - return new DirectListPanel(b); - } - }) - }; - - LoadComponentAsync(newPanels, p => - { - if (panels != null) ScrollFlow.Remove(panels); - ScrollFlow.Add(panels = newPanels); - }); - } - - protected override void PopIn() - { - base.PopIn(); - - // Queries are allowed to be run only on the first pop-in - if (getSetsRequest == null) - queueUpdateSearch(); - } - - private SearchBeatmapSetsRequest getSetsRequest; - - private readonly Bindable currentQuery = new Bindable(string.Empty); - - private ScheduledDelegate queryChangedDebounce; - - [Resolved] - private PreviewTrackManager previewTrackManager { get; set; } - - private void queueUpdateSearch(bool queryTextChanged = false) - { - BeatmapSets = null; - ResultAmounts = null; - - getSetsRequest?.Cancel(); - - queryChangedDebounce?.Cancel(); - queryChangedDebounce = Scheduler.AddDelayed(updateSearch, queryTextChanged ? 500 : 100); - } - - private void updateSearch() - { - if (!IsLoaded) - return; - - if (State.Value == Visibility.Hidden) - return; - - if (API == null) - return; - - previewTrackManager.StopAnyPlaying(this); - - getSetsRequest = new SearchBeatmapSetsRequest(currentQuery.Value, ((FilterControl)Filter).Ruleset.Value) - { - SearchCategory = Filter.DisplayStyleControl.Dropdown.Current.Value, - SortCriteria = Filter.Tabs.Current.Value - }; - - getSetsRequest.Success += response => - { - Task.Run(() => - { - var sets = response.BeatmapSets.Select(r => r.ToBeatmapSet(rulesets)).ToList(); - - // may not need scheduling; loads async internally. - Schedule(() => - { - BeatmapSets = sets; - recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value); - }); - }); - }; - - API.Queue(getSetsRequest); - } - - private int distinctCount(List list) => list.Distinct().ToArray().Length; - - public class ResultCounts - { - public readonly int Artists; - public readonly int Songs; - public readonly int Tags; - - public ResultCounts(int artists, int songs, int tags) - { - Artists = artists; - Songs = songs; - Tags = tags; - } - } - } -} diff --git a/osu.Game/Overlays/SocialOverlay.cs b/osu.Game/Overlays/SocialOverlay.cs index 02f7c9b0d3..9548573b4f 100644 --- a/osu.Game/Overlays/SocialOverlay.cs +++ b/osu.Game/Overlays/SocialOverlay.cs @@ -239,10 +239,4 @@ namespace osu.Game.Overlays } } } - - public enum SortDirection - { - Ascending, - Descending - } } diff --git a/osu.Game/Overlays/SortDirection.cs b/osu.Game/Overlays/SortDirection.cs new file mode 100644 index 0000000000..3af9614972 --- /dev/null +++ b/osu.Game/Overlays/SortDirection.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.Overlays +{ + public enum SortDirection + { + Ascending, + Descending + } +} diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs index 897587d198..227347112c 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -71,7 +71,7 @@ namespace osu.Game.Overlays.Toolbar { new ToolbarChangelogButton(), new ToolbarRankingsButton(), - new ToolbarDirectButton(), + new ToolbarBeatmapListingButton(), new ToolbarChatButton(), new ToolbarSocialButton(), new ToolbarMusicButton(), diff --git a/osu.Game/Overlays/Toolbar/ToolbarDirectButton.cs b/osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs similarity index 63% rename from osu.Game/Overlays/Toolbar/ToolbarDirectButton.cs rename to osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs index 1d07a3ae70..eecb368ee9 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarDirectButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs @@ -6,17 +6,17 @@ using osu.Game.Graphics; namespace osu.Game.Overlays.Toolbar { - public class ToolbarDirectButton : ToolbarOverlayToggleButton + public class ToolbarBeatmapListingButton : ToolbarOverlayToggleButton { - public ToolbarDirectButton() + public ToolbarBeatmapListingButton() { SetIcon(OsuIcon.ChevronDownCircle); } [BackgroundDependencyLoader(true)] - private void load(DirectOverlay direct) + private void load(BeatmapListingOverlay beatmapListing) { - StateContainer = direct; + StateContainer = beatmapListing; } } } diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index fe538728e3..30e5e9702e 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -39,7 +39,7 @@ namespace osu.Game.Screens.Menu public Action OnEdit; public Action OnExit; - public Action OnDirect; + public Action OnBeatmapListing; public Action OnSolo; public Action OnSettings; public Action OnMulti; @@ -130,7 +130,7 @@ namespace osu.Game.Screens.Menu buttonsTopLevel.Add(new Button(@"play", @"button-play-select", OsuIcon.Logo, new Color4(102, 68, 204, 255), () => State = ButtonSystemState.Play, WEDGE_WIDTH, Key.P)); buttonsTopLevel.Add(new Button(@"osu!editor", @"button-generic-select", OsuIcon.EditCircle, new Color4(238, 170, 0, 255), () => OnEdit?.Invoke(), 0, Key.E)); - buttonsTopLevel.Add(new Button(@"osu!direct", @"button-direct-select", OsuIcon.ChevronDownCircle, new Color4(165, 204, 0, 255), () => OnDirect?.Invoke(), 0, Key.D)); + buttonsTopLevel.Add(new Button(@"osu!direct", @"button-direct-select", OsuIcon.ChevronDownCircle, new Color4(165, 204, 0, 255), () => OnBeatmapListing?.Invoke(), 0, Key.D)); if (host.CanExit) buttonsTopLevel.Add(new Button(@"exit", string.Empty, OsuIcon.CrossCircle, new Color4(238, 51, 153, 255), () => OnExit?.Invoke(), 0, Key.Q)); diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 174eadfe26..0589e4d12b 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -72,7 +72,7 @@ namespace osu.Game.Screens.Menu private SongTicker songTicker; [BackgroundDependencyLoader(true)] - private void load(DirectOverlay direct, SettingsOverlay settings, RankingsOverlay rankings, OsuConfigManager config, SessionStatics statics) + private void load(BeatmapListingOverlay beatmapListing, SettingsOverlay settings, RankingsOverlay rankings, OsuConfigManager config, SessionStatics statics) { holdDelay = config.GetBindable(OsuSetting.UIHoldActivationDelay); loginDisplayed = statics.GetBindable(Static.LoginOverlayDisplayed); @@ -133,7 +133,7 @@ namespace osu.Game.Screens.Menu }; buttons.OnSettings = () => settings?.ToggleVisibility(); - buttons.OnDirect = () => direct?.ToggleVisibility(); + buttons.OnBeatmapListing = () => beatmapListing?.ToggleVisibility(); buttons.OnChart = () => rankings?.ShowSpotlights(); LoadComponentAsync(background = new BackgroundScreenDefault()); From 9b9b710ded76bfc99d326f94c9fd6a40c69f9412 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Apr 2020 16:03:18 +0900 Subject: [PATCH 0334/1117] Move and rename remaining direct classes --- .../Online/TestSceneDirectDownloadButton.cs | 6 ++--- .../Visual/Online/TestSceneDirectPanel.cs | 18 +++++++-------- osu.Game.Tests/Visual/TestSceneOsuGame.cs | 9 +------- .../BeatmapDownloadTrackingComposite.cs | 2 +- .../Panels/BeatmapPanel.cs} | 8 +++---- .../Panels/BeatmapPanelDownloadButton.cs} | 6 ++--- .../Panels/BeatmapPanelGrid.cs} | 16 +++++++------- .../Panels/BeatmapPanelList.cs} | 22 +++++++++---------- .../Panels}/DownloadProgressBar.cs | 2 +- .../Panels}/IconPill.cs | 2 +- .../Panels}/PlayButton.cs | 2 +- osu.Game/Overlays/BeatmapListingOverlay.cs | 6 ++--- .../Buttons/HeaderDownloadButton.cs | 2 +- .../BeatmapSet/Buttons/PreviewButton.cs | 2 +- osu.Game/Overlays/BeatmapSet/Header.cs | 4 ++-- .../Beatmaps/PaginatedBeatmapContainer.cs | 4 ++-- .../Overlays/Rankings/SpotlightsLayout.cs | 4 ++-- .../Screens/Multi/DrawableRoomPlaylistItem.cs | 4 ++-- 18 files changed, 56 insertions(+), 63 deletions(-) rename osu.Game/Overlays/{Direct => }/BeatmapDownloadTrackingComposite.cs (94%) rename osu.Game/Overlays/{Direct/DirectPanel.cs => BeatmapListing/Panels/BeatmapPanel.cs} (96%) rename osu.Game/Overlays/{Direct/PanelDownloadButton.cs => BeatmapListing/Panels/BeatmapPanelDownloadButton.cs} (93%) rename osu.Game/Overlays/{Direct/DirectGridPanel.cs => BeatmapListing/Panels/BeatmapPanelGrid.cs} (97%) rename osu.Game/Overlays/{Direct/DirectListPanel.cs => BeatmapListing/Panels/BeatmapPanelList.cs} (97%) rename osu.Game/Overlays/{Direct => BeatmapListing/Panels}/DownloadProgressBar.cs (97%) rename osu.Game/Overlays/{Direct => BeatmapListing/Panels}/IconPill.cs (96%) rename osu.Game/Overlays/{Direct => BeatmapListing/Panels}/PlayButton.cs (98%) diff --git a/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs b/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs index f612992bf6..9fe873cb6a 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs @@ -9,7 +9,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Online; -using osu.Game.Overlays.Direct; +using osu.Game.Overlays.BeatmapListing.Panels; using osu.Game.Rulesets.Osu; using osu.Game.Tests.Resources; using osuTK; @@ -20,7 +20,7 @@ namespace osu.Game.Tests.Visual.Online { public override IReadOnlyList RequiredTypes => new[] { - typeof(PanelDownloadButton) + typeof(BeatmapPanelDownloadButton) }; private TestDownloadButton downloadButton; @@ -143,7 +143,7 @@ namespace osu.Game.Tests.Visual.Online return beatmap; } - private class TestDownloadButton : PanelDownloadButton + private class TestDownloadButton : BeatmapPanelDownloadButton { public new bool DownloadEnabled => base.DownloadEnabled; diff --git a/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs index cb08cded37..5809f93d90 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs @@ -8,7 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Audio; using osu.Game.Beatmaps; -using osu.Game.Overlays.Direct; +using osu.Game.Overlays.BeatmapListing.Panels; using osu.Game.Rulesets; using osu.Game.Users; using osuTK; @@ -20,8 +20,8 @@ namespace osu.Game.Tests.Visual.Online { public override IReadOnlyList RequiredTypes => new[] { - typeof(DirectGridPanel), - typeof(DirectListPanel), + typeof(BeatmapPanelGrid), + typeof(BeatmapPanelList), typeof(IconPill) }; @@ -126,12 +126,12 @@ namespace osu.Game.Tests.Visual.Online Spacing = new Vector2(5, 20), Children = new Drawable[] { - new DirectGridPanel(normal), - new DirectGridPanel(undownloadable), - new DirectGridPanel(manyDifficulties), - new DirectListPanel(normal), - new DirectListPanel(undownloadable), - new DirectListPanel(manyDifficulties), + new BeatmapPanelGrid(normal), + new BeatmapPanelGrid(undownloadable), + new BeatmapPanelGrid(manyDifficulties), + new BeatmapPanelList(normal), + new BeatmapPanelList(undownloadable), + new BeatmapPanelList(manyDifficulties), }, }, }; diff --git a/osu.Game.Tests/Visual/TestSceneOsuGame.cs b/osu.Game.Tests/Visual/TestSceneOsuGame.cs index d68217dcfd..2eaac2a45f 100644 --- a/osu.Game.Tests/Visual/TestSceneOsuGame.cs +++ b/osu.Game.Tests/Visual/TestSceneOsuGame.cs @@ -47,15 +47,8 @@ namespace osu.Game.Tests.Visual typeof(IdleTracker), typeof(OnScreenDisplay), typeof(NotificationOverlay), -<<<<<<< HEAD - typeof(DirectOverlay), + typeof(BeatmapListingOverlay), typeof(DashboardOverlay), -||||||| parent of 96a3a08a9... Remove unused classes and replace overlay in game - typeof(DirectOverlay), - typeof(SocialOverlay), -======= - typeof(SocialOverlay), ->>>>>>> 96a3a08a9... Remove unused classes and replace overlay in game typeof(ChannelManager), typeof(ChatOverlay), typeof(SettingsOverlay), diff --git a/osu.Game/Overlays/Direct/BeatmapDownloadTrackingComposite.cs b/osu.Game/Overlays/BeatmapDownloadTrackingComposite.cs similarity index 94% rename from osu.Game/Overlays/Direct/BeatmapDownloadTrackingComposite.cs rename to osu.Game/Overlays/BeatmapDownloadTrackingComposite.cs index fd04a1541e..f6b5b181c3 100644 --- a/osu.Game/Overlays/Direct/BeatmapDownloadTrackingComposite.cs +++ b/osu.Game/Overlays/BeatmapDownloadTrackingComposite.cs @@ -5,7 +5,7 @@ using osu.Framework.Bindables; using osu.Game.Beatmaps; using osu.Game.Online; -namespace osu.Game.Overlays.Direct +namespace osu.Game.Overlays { public abstract class BeatmapDownloadTrackingComposite : DownloadTrackingComposite { diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs similarity index 96% rename from osu.Game/Overlays/Direct/DirectPanel.cs rename to osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs index 4ad8e95512..f260bf1573 100644 --- a/osu.Game/Overlays/Direct/DirectPanel.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs @@ -26,9 +26,9 @@ using osu.Game.Graphics.UserInterface; using osuTK; using osuTK.Graphics; -namespace osu.Game.Overlays.Direct +namespace osu.Game.Overlays.BeatmapListing.Panels { - public abstract class DirectPanel : OsuClickableContainer, IHasContextMenu + public abstract class BeatmapPanel : OsuClickableContainer, IHasContextMenu { public readonly BeatmapSetInfo SetInfo; @@ -49,7 +49,7 @@ namespace osu.Game.Overlays.Direct protected Action ViewBeatmap; - protected DirectPanel(BeatmapSetInfo setInfo) + protected BeatmapPanel(BeatmapSetInfo setInfo) { Debug.Assert(setInfo.OnlineBeatmapSetID != null); @@ -148,7 +148,7 @@ namespace osu.Game.Overlays.Direct if (SetInfo.Beatmaps.Count > maximum_difficulty_icons) { foreach (var ruleset in SetInfo.Beatmaps.Select(b => b.Ruleset).Distinct()) - icons.Add(new GroupedDifficultyIcon(SetInfo.Beatmaps.FindAll(b => b.Ruleset.Equals(ruleset)), ruleset, this is DirectListPanel ? Color4.White : colours.Gray5)); + icons.Add(new GroupedDifficultyIcon(SetInfo.Beatmaps.FindAll(b => b.Ruleset.Equals(ruleset)), ruleset, this is BeatmapPanelList ? Color4.White : colours.Gray5)); } else { diff --git a/osu.Game/Overlays/Direct/PanelDownloadButton.cs b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs similarity index 93% rename from osu.Game/Overlays/Direct/PanelDownloadButton.cs rename to osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs index 387ced6acb..589f2d5072 100644 --- a/osu.Game/Overlays/Direct/PanelDownloadButton.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs @@ -11,9 +11,9 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online; -namespace osu.Game.Overlays.Direct +namespace osu.Game.Overlays.BeatmapListing.Panels { - public class PanelDownloadButton : BeatmapDownloadTrackingComposite + public class BeatmapPanelDownloadButton : BeatmapDownloadTrackingComposite { protected bool DownloadEnabled => button.Enabled.Value; @@ -26,7 +26,7 @@ namespace osu.Game.Overlays.Direct private readonly DownloadButton button; private Bindable noVideoSetting; - public PanelDownloadButton(BeatmapSetInfo beatmapSet) + public BeatmapPanelDownloadButton(BeatmapSetInfo beatmapSet) : base(beatmapSet) { InternalChild = shakeContainer = new ShakeContainer diff --git a/osu.Game/Overlays/Direct/DirectGridPanel.cs b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelGrid.cs similarity index 97% rename from osu.Game/Overlays/Direct/DirectGridPanel.cs rename to osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelGrid.cs index 2528ccec41..caa7eb6441 100644 --- a/osu.Game/Overlays/Direct/DirectGridPanel.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelGrid.cs @@ -1,25 +1,25 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osuTK; -using osuTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; +using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osuTK; +using osuTK.Graphics; -namespace osu.Game.Overlays.Direct +namespace osu.Game.Overlays.BeatmapListing.Panels { - public class DirectGridPanel : DirectPanel + public class BeatmapPanelGrid : BeatmapPanel { private const float horizontal_padding = 10; private const float vertical_padding = 5; @@ -31,7 +31,7 @@ namespace osu.Game.Overlays.Direct protected override PlayButton PlayButton => playButton; protected override Box PreviewBar => progressBar; - public DirectGridPanel(BeatmapSetInfo beatmap) + public BeatmapPanelGrid(BeatmapSetInfo beatmap) : base(beatmap) { Width = 380; @@ -156,7 +156,7 @@ namespace osu.Game.Overlays.Direct }, }, }, - new PanelDownloadButton(SetInfo) + new BeatmapPanelDownloadButton(SetInfo) { Size = new Vector2(50, 30), Margin = new MarginPadding(horizontal_padding), diff --git a/osu.Game/Overlays/Direct/DirectListPanel.cs b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelList.cs similarity index 97% rename from osu.Game/Overlays/Direct/DirectListPanel.cs rename to osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelList.cs index b64142dfe7..3245ddea99 100644 --- a/osu.Game/Overlays/Direct/DirectListPanel.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelList.cs @@ -1,25 +1,25 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osuTK; -using osuTK.Graphics; +using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Colour; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Framework.Allocation; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; +using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osuTK; +using osuTK.Graphics; -namespace osu.Game.Overlays.Direct +namespace osu.Game.Overlays.BeatmapListing.Panels { - public class DirectListPanel : DirectPanel + public class BeatmapPanelList : BeatmapPanel { private const float transition_duration = 120; private const float horizontal_padding = 10; @@ -27,7 +27,7 @@ namespace osu.Game.Overlays.Direct private const float height = 70; private FillFlowContainer statusContainer; - protected PanelDownloadButton DownloadButton; + protected BeatmapPanelDownloadButton DownloadButton; private PlayButton playButton; private Box progressBar; @@ -36,7 +36,7 @@ namespace osu.Game.Overlays.Direct protected override PlayButton PlayButton => playButton; protected override Box PreviewBar => progressBar; - public DirectListPanel(BeatmapSetInfo beatmap) + public BeatmapPanelList(BeatmapSetInfo beatmap) : base(beatmap) { RelativeSizeAxes = Axes.X; @@ -151,7 +151,7 @@ namespace osu.Game.Overlays.Direct Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, AutoSizeAxes = Axes.Both, - Child = DownloadButton = new PanelDownloadButton(SetInfo) + Child = DownloadButton = new BeatmapPanelDownloadButton(SetInfo) { Size = new Vector2(height - vertical_padding * 3), Margin = new MarginPadding { Left = vertical_padding * 2, Right = vertical_padding }, diff --git a/osu.Game/Overlays/Direct/DownloadProgressBar.cs b/osu.Game/Overlays/BeatmapListing/Panels/DownloadProgressBar.cs similarity index 97% rename from osu.Game/Overlays/Direct/DownloadProgressBar.cs rename to osu.Game/Overlays/BeatmapListing/Panels/DownloadProgressBar.cs index 9a8644efd2..93cf8799b5 100644 --- a/osu.Game/Overlays/Direct/DownloadProgressBar.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/DownloadProgressBar.cs @@ -10,7 +10,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Online; using osuTK.Graphics; -namespace osu.Game.Overlays.Direct +namespace osu.Game.Overlays.BeatmapListing.Panels { public class DownloadProgressBar : BeatmapDownloadTrackingComposite { diff --git a/osu.Game/Overlays/Direct/IconPill.cs b/osu.Game/Overlays/BeatmapListing/Panels/IconPill.cs similarity index 96% rename from osu.Game/Overlays/Direct/IconPill.cs rename to osu.Game/Overlays/BeatmapListing/Panels/IconPill.cs index d63bb2a292..1cb6c84f13 100644 --- a/osu.Game/Overlays/Direct/IconPill.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/IconPill.cs @@ -8,7 +8,7 @@ using osu.Framework.Graphics.Sprites; using osuTK; using osuTK.Graphics; -namespace osu.Game.Overlays.Direct +namespace osu.Game.Overlays.BeatmapListing.Panels { public class IconPill : CircularContainer { diff --git a/osu.Game/Overlays/Direct/PlayButton.cs b/osu.Game/Overlays/BeatmapListing/Panels/PlayButton.cs similarity index 98% rename from osu.Game/Overlays/Direct/PlayButton.cs rename to osu.Game/Overlays/BeatmapListing/Panels/PlayButton.cs index d9f335b6a7..e95fdeecf4 100644 --- a/osu.Game/Overlays/Direct/PlayButton.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/PlayButton.cs @@ -14,7 +14,7 @@ using osu.Game.Graphics.UserInterface; using osuTK; using osuTK.Graphics; -namespace osu.Game.Overlays.Direct +namespace osu.Game.Overlays.BeatmapListing.Panels { public class PlayButton : Container { diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index 000ca6b91c..a024e2c74e 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -17,7 +17,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.BeatmapListing; -using osu.Game.Overlays.Direct; +using osu.Game.Overlays.BeatmapListing.Panels; using osuTK; namespace osu.Game.Overlays @@ -118,14 +118,14 @@ namespace osu.Game.Overlays return; } - var newPanels = new FillFlowContainer + var newPanels = new FillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Spacing = new Vector2(10), Alpha = 0, Margin = new MarginPadding { Vertical = 15 }, - ChildrenEnumerable = beatmaps.Select(b => new DirectGridPanel(b) + ChildrenEnumerable = beatmaps.Select(b => new BeatmapPanelGrid(b) { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs index e64256b850..56c0052bfe 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs @@ -13,7 +13,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Online; using osu.Game.Online.API; -using osu.Game.Overlays.Direct; +using osu.Game.Overlays.BeatmapListing.Panels; using osu.Game.Users; using osuTK; using osuTK.Graphics; diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs index 7eae05e4a9..6accce7d77 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs @@ -11,7 +11,7 @@ using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; -using osu.Game.Overlays.Direct; +using osu.Game.Overlays.BeatmapListing.Panels; using osuTK; namespace osu.Game.Overlays.BeatmapSet.Buttons diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs index 11dc424183..17fa689cd2 100644 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ b/osu.Game/Overlays/BeatmapSet/Header.cs @@ -15,8 +15,8 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Online; +using osu.Game.Overlays.BeatmapListing.Panels; using osu.Game.Overlays.BeatmapSet.Buttons; -using osu.Game.Overlays.Direct; using osu.Game.Rulesets; using osuTK; using osuTK.Graphics; @@ -274,7 +274,7 @@ namespace osu.Game.Overlays.BeatmapSet { case DownloadState.LocallyAvailable: // temporary for UX until new design is implemented. - downloadButtonsContainer.Child = new PanelDownloadButton(BeatmapSet.Value) + downloadButtonsContainer.Child = new BeatmapPanelDownloadButton(BeatmapSet.Value) { Width = 50, RelativeSizeAxes = Axes.Y, diff --git a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs index fcd12e2b54..5f70dc4d75 100644 --- a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs @@ -7,7 +7,7 @@ using osu.Framework.Graphics; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; -using osu.Game.Overlays.Direct; +using osu.Game.Overlays.BeatmapListing.Panels; using osu.Game.Users; using osuTK; @@ -33,7 +33,7 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps protected override Drawable CreateDrawableItem(APIBeatmapSet model) => !model.OnlineBeatmapSetID.HasValue ? null - : new DirectGridPanel(model.ToBeatmapSet(Rulesets)) + : new BeatmapPanelGrid(model.ToBeatmapSet(Rulesets)) { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, diff --git a/osu.Game/Overlays/Rankings/SpotlightsLayout.cs b/osu.Game/Overlays/Rankings/SpotlightsLayout.cs index 6f06eecd6e..895fa94af5 100644 --- a/osu.Game/Overlays/Rankings/SpotlightsLayout.cs +++ b/osu.Game/Overlays/Rankings/SpotlightsLayout.cs @@ -12,10 +12,10 @@ using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Overlays.Rankings.Tables; using System.Linq; -using osu.Game.Overlays.Direct; using System.Threading; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays.BeatmapListing.Panels; namespace osu.Game.Overlays.Rankings { @@ -140,7 +140,7 @@ namespace osu.Game.Overlays.Rankings AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, Spacing = new Vector2(10), - Children = response.BeatmapSets.Select(b => new DirectGridPanel(b.ToBeatmapSet(rulesets)) + Children = response.BeatmapSets.Select(b => new BeatmapPanelGrid(b.ToBeatmapSet(rulesets)) { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, diff --git a/osu.Game/Screens/Multi/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/Multi/DrawableRoomPlaylistItem.cs index d7dcca9809..c024304856 100644 --- a/osu.Game/Screens/Multi/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/Multi/DrawableRoomPlaylistItem.cs @@ -21,7 +21,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Online; using osu.Game.Online.Chat; using osu.Game.Online.Multiplayer; -using osu.Game.Overlays.Direct; +using osu.Game.Overlays.BeatmapListing.Panels; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Play.HUD; @@ -210,7 +210,7 @@ namespace osu.Game.Screens.Multi return true; } - private class PlaylistDownloadButton : PanelDownloadButton + private class PlaylistDownloadButton : BeatmapPanelDownloadButton { public PlaylistDownloadButton(BeatmapSetInfo beatmapSet) : base(beatmapSet) From b8a1831d98feb8fc6752a2755b66df55de66c832 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Apr 2020 17:14:04 +0900 Subject: [PATCH 0335/1117] Read line widths from skin --- osu.Game/Skinning/LegacySkin.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 91f970d19f..003fa24d5b 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -249,6 +249,14 @@ namespace osu.Game.Skinning case LegacyManiaSkinConfigurationLookups.RightStageImage: return SkinUtils.As(getManiaImage(existing, "StageRight")); + + case LegacyManiaSkinConfigurationLookups.LeftLineWidth: + Debug.Assert(maniaLookup.TargetColumn != null); + return SkinUtils.As(new Bindable(existing.ColumnLineWidth[maniaLookup.TargetColumn.Value])); + + case LegacyManiaSkinConfigurationLookups.RightLineWidth: + Debug.Assert(maniaLookup.TargetColumn != null); + return SkinUtils.As(new Bindable(existing.ColumnLineWidth[maniaLookup.TargetColumn.Value + 1])); } return null; From 0a2b585c65ca963a1fac0eb917328f329b722d54 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Apr 2020 17:14:49 +0900 Subject: [PATCH 0336/1117] Apply missing scale --- osu.Game.Rulesets.Mania/Skinning/LegacyColumnBackground.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Mania/Skinning/LegacyColumnBackground.cs b/osu.Game.Rulesets.Mania/Skinning/LegacyColumnBackground.cs index 6504321bb2..1a097405ac 100644 --- a/osu.Game.Rulesets.Mania/Skinning/LegacyColumnBackground.cs +++ b/osu.Game.Rulesets.Mania/Skinning/LegacyColumnBackground.cs @@ -67,6 +67,7 @@ namespace osu.Game.Rulesets.Mania.Skinning { RelativeSizeAxes = Axes.Y, Width = leftLineWidth, + Scale = new Vector2(0.740f, 1), Colour = lineColour, Alpha = hasLeftLine ? 1 : 0 }, @@ -76,6 +77,7 @@ namespace osu.Game.Rulesets.Mania.Skinning Origin = Anchor.TopRight, RelativeSizeAxes = Axes.Y, Width = rightLineWidth, + Scale = new Vector2(0.740f, 1), Colour = lineColour, Alpha = hasRightLine ? 1 : 0 }, From a41ac50e2f052854032131e62d12407e6fd3242d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Apr 2020 17:15:06 +0900 Subject: [PATCH 0337/1117] Line widths should not receive scale factor --- osu.Game/Skinning/LegacyManiaSkinDecoder.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Skinning/LegacyManiaSkinDecoder.cs b/osu.Game/Skinning/LegacyManiaSkinDecoder.cs index 2db902c182..a988bd589f 100644 --- a/osu.Game/Skinning/LegacyManiaSkinDecoder.cs +++ b/osu.Game/Skinning/LegacyManiaSkinDecoder.cs @@ -74,7 +74,7 @@ namespace osu.Game.Skinning switch (pair.Key) { case "ColumnLineWidth": - parseArrayValue(pair.Value, currentConfig.ColumnLineWidth); + parseArrayValue(pair.Value, currentConfig.ColumnLineWidth, false); break; case "ColumnSpacing": @@ -124,7 +124,7 @@ namespace osu.Game.Skinning pendingLines.Clear(); } - private void parseArrayValue(string value, float[] output) + private void parseArrayValue(string value, float[] output, bool applyScaleFactor = true) { string[] values = value.Split(','); @@ -133,7 +133,7 @@ namespace osu.Game.Skinning if (i >= output.Length) break; - output[i] = float.Parse(values[i], CultureInfo.InvariantCulture) * LegacyManiaSkinConfiguration.POSITION_SCALE_FACTOR; + output[i] = float.Parse(values[i], CultureInfo.InvariantCulture) * (applyScaleFactor ? LegacyManiaSkinConfiguration.POSITION_SCALE_FACTOR : 1); } } } From 4642a6093c2b6e5fa559059567e77863125e2c2a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Apr 2020 17:15:13 +0900 Subject: [PATCH 0338/1117] Add test --- .../Resources/special-skin/mania-key1@2x.png | Bin 0 -> 12914 bytes .../Resources/special-skin/skin.ini | 6 ++++++ .../Skinning/TestSceneColumnBackground.cs | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Rulesets.Mania.Tests/Resources/special-skin/mania-key1@2x.png create mode 100644 osu.Game.Rulesets.Mania.Tests/Resources/special-skin/skin.ini diff --git a/osu.Game.Rulesets.Mania.Tests/Resources/special-skin/mania-key1@2x.png b/osu.Game.Rulesets.Mania.Tests/Resources/special-skin/mania-key1@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..aa681f6f223ea44319b3a383935237d1a2e4b345 GIT binary patch literal 12914 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 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Mania.Tests/Resources/special-skin/skin.ini b/osu.Game.Rulesets.Mania.Tests/Resources/special-skin/skin.ini new file mode 100644 index 0000000000..56564776b3 --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests/Resources/special-skin/skin.ini @@ -0,0 +1,6 @@ +[General] +Version: 2.4 + +[Mania] +Keys: 4 +ColumnLineWidth: 3,1,3,1,1 \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneColumnBackground.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneColumnBackground.cs index d6bacbe59e..bde323f187 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneColumnBackground.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneColumnBackground.cs @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning { RelativeSizeAxes = Axes.Both, Width = 0.5f, - Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.ColumnBackground, 0), _ => new DefaultColumnBackground()) + Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.ColumnBackground, 1), _ => new DefaultColumnBackground()) { RelativeSizeAxes = Axes.Both } From a82efa626e9d95f98ccad220f14a3ab4fdb76690 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 21 Apr 2020 11:36:09 +0300 Subject: [PATCH 0339/1117] Add XMLDoc for default hyper-dash colour constant --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 056e838419..6f5e8be92c 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -23,6 +23,9 @@ namespace osu.Game.Rulesets.Catch.UI { public class Catcher : SkinReloadableDrawable, IKeyBindingHandler { + /// + /// The default colour used for all hyper-dashing components. (catcher drawables and fruit) + /// public static readonly Color4 DEFAULT_HYPER_DASH_COLOUR = Color4.Red; /// From ee62739b08e09feeda55b78d5a49dd68a857de0e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 21 Apr 2020 11:41:53 +0300 Subject: [PATCH 0340/1117] Simplify process of adding catcher trails --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 29 +++++++---------------- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 4 +--- 2 files changed, 10 insertions(+), 23 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 6f5e8be92c..92f2977c40 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Catch.UI public Container ExplodingFruitTarget; - private Container additiveTarget; + private readonly Container additiveTarget; private Container dashTrails; private Container hyperDashTrails; private Container endGlowSprites; @@ -116,8 +116,10 @@ namespace osu.Game.Rulesets.Catch.UI private int hyperDashDirection; private float hyperDashTargetPosition; - public Catcher(BeatmapDifficulty difficulty = null) + public Catcher(BeatmapDifficulty difficulty = null, Container additiveTarget = null) { + this.additiveTarget = additiveTarget; + RelativePositionAxes = Axes.X; X = 0.5f; @@ -155,27 +157,14 @@ namespace osu.Game.Rulesets.Catch.UI } }; - updateCatcher(); - } - - /// - /// Sets container target to provide catcher additive trails content in. - /// - /// The container to add catcher trails in. - public void SetAdditiveTarget(Container target) - { - if (additiveTarget == target) - return; - - additiveTarget?.RemoveRange(new[] { dashTrails, hyperDashTrails, endGlowSprites }); - - additiveTarget = target; additiveTarget?.AddRange(new[] { - dashTrails ??= new Container { RelativeSizeAxes = Axes.Both, Colour = Color4.White }, - hyperDashTrails ??= new Container { RelativeSizeAxes = Axes.Both, Colour = hyperDashColour }, - endGlowSprites ??= new Container { RelativeSizeAxes = Axes.Both, Colour = hyperDashEndGlowColour }, + dashTrails = new Container { RelativeSizeAxes = Axes.Both, Colour = Color4.White }, + hyperDashTrails = new Container { RelativeSizeAxes = Axes.Both, Colour = hyperDashColour }, + endGlowSprites = new Container { RelativeSizeAxes = Axes.Both, Colour = hyperDashEndGlowColour } }); + + updateCatcher(); } /// diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 641b81599e..1dd94afa9e 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -33,9 +33,7 @@ namespace osu.Game.Rulesets.Catch.UI { RelativeSizeAxes = Axes.X; Height = CATCHER_SIZE; - - Child = MovableCatcher = new Catcher(difficulty); - MovableCatcher.SetAdditiveTarget(this); + Child = MovableCatcher = new Catcher(difficulty, this); } public static float GetCatcherSize(BeatmapDifficulty difficulty) From c8c2b51108a1a0b80c80d5b846c2925231e80e3a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 21 Apr 2020 11:44:10 +0300 Subject: [PATCH 0341/1117] Remove redundant property set Co-Authored-By: Dean Herbert --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 92f2977c40..7ecb245617 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -159,7 +159,7 @@ namespace osu.Game.Rulesets.Catch.UI additiveTarget?.AddRange(new[] { - dashTrails = new Container { RelativeSizeAxes = Axes.Both, Colour = Color4.White }, + dashTrails = new Container { RelativeSizeAxes = Axes.Both }, hyperDashTrails = new Container { RelativeSizeAxes = Axes.Both, Colour = hyperDashColour }, endGlowSprites = new Container { RelativeSizeAxes = Axes.Both, Colour = hyperDashEndGlowColour } }); From d1c701a9972a42af10021bccb5d528ddb51f71c9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Apr 2020 18:34:56 +0900 Subject: [PATCH 0342/1117] Rename existing test to something more relevant --- .../{TestSceneTaikoPlayfield.cs => TestSceneHits.cs} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename osu.Game.Rulesets.Taiko.Tests/{TestSceneTaikoPlayfield.cs => TestSceneHits.cs} (99%) diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs similarity index 99% rename from osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs rename to osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs index 0d9e813c60..c2ca578dfa 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs @@ -24,7 +24,7 @@ using osuTK; namespace osu.Game.Rulesets.Taiko.Tests { [TestFixture] - public class TestSceneTaikoPlayfield : OsuTestScene + public class TestSceneHits : OsuTestScene { private const double default_duration = 1000; private const float scroll_time = 1000; From e74f9024836b56b4485d724d867fa1f71ea0a5b6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Apr 2020 18:40:18 +0900 Subject: [PATCH 0343/1117] Add playfield test scene --- .../Skinning/TestSceneTaikoPlayfield.cs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs new file mode 100644 index 0000000000..e255baf459 --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs @@ -0,0 +1,32 @@ +// 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.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Taiko.UI; +using osu.Game.Rulesets.UI.Scrolling; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Taiko.Tests.Skinning +{ + public class TestSceneTaikoPlayfield : TaikoSkinnableTestScene + { + [Cached(typeof(IScrollingInfo))] + private ScrollingTestContainer.TestScrollingInfo info = new ScrollingTestContainer.TestScrollingInfo + { + Direction = { Value = ScrollingDirection.Left }, + TimeRange = { Value = 5000 }, + }; + + public TestSceneTaikoPlayfield() + { + AddStep("Load playfield", () => SetContents(() => new TaikoPlayfield(new ControlPointInfo()) + { + Height = 0.4f, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + })); + } + } +} From bfc17bf4c09ac2493748c3751f3921b374746086 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Apr 2020 19:00:34 +0900 Subject: [PATCH 0344/1117] Add taiko hit target skinning --- .../metrics-skin/approachcircle@2x.png | Bin 0 -> 13816 bytes .../metrics-skin/taikobigcircle@2x.png | Bin 0 -> 12145 bytes .../Resources/old-skin/approachcircle.png | Bin 0 -> 10333 bytes .../Resources/special-skin/approachcircle.png | Bin 0 -> 4504 bytes .../special-skin/taikobigcircle@2x.png | Bin 0 -> 12374 bytes .../Skinning/TestSceneTaikoPlayfield.cs | 10 +++++ .../Skinning/LegacyHitTarget.cs | 41 ++++++++++++++++++ .../Skinning/TaikoLegacySkinTransformer.cs | 6 +++ .../TaikoSkinComponents.cs | 3 +- osu.Game.Rulesets.Taiko/UI/HitTarget.cs | 2 + osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 7 +-- 11 files changed, 65 insertions(+), 4 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/metrics-skin/approachcircle@2x.png create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/metrics-skin/taikobigcircle@2x.png create mode 100755 osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/approachcircle.png create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/special-skin/approachcircle.png create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/special-skin/taikobigcircle@2x.png create mode 100644 osu.Game.Rulesets.Taiko/Skinning/LegacyHitTarget.cs diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/metrics-skin/approachcircle@2x.png b/osu.Game.Rulesets.Taiko.Tests/Resources/metrics-skin/approachcircle@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..72ef665478b758c364197aa79f238230e229b3c4 GIT binary patch literal 13816 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 X;|*6ltxe$d|7PiG>1$T1+eQB$HM;D+ literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/metrics-skin/taikobigcircle@2x.png b/osu.Game.Rulesets.Taiko.Tests/Resources/metrics-skin/taikobigcircle@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..440e5b55e52c8629f69272993a75b1a56002fb60 GIT binary patch literal 12145 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 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/approachcircle.png b/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/approachcircle.png new file mode 100755 index 0000000000000000000000000000000000000000..5aba6887564782a216c030b6a0143830612193ee GIT binary patch literal 10333 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 H<7ECnYE>!V literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/special-skin/approachcircle.png b/osu.Game.Rulesets.Taiko.Tests/Resources/special-skin/approachcircle.png new file mode 100644 index 0000000000000000000000000000000000000000..56d6d34c1abf6ab153c9cef62eecff3016d21ed9 GIT binary patch literal 4504 zcmV;J5ohj+P)(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRa29!W$&RCwC#oquedRT;-Wy>0K-t!uZAjs2o*V|6%w%J?mFfU+Q<$ggN1GZ=}9 zi9~~m(MU8B4MZX#F)`7YNF)-8MubR2CkVQM3~?KS;l^ZRTeogqyOy{``(>z@}~FR_q})T&htIb^L?Ik-g7Fl*(_y%3ZPO)wR5DMzxH*Nu1o0v z7}PNU^f^aHN1yYzU&kPj_{OVBxWTJ{X~1-#2AJs`Gn}JF*IFW2!el+;XMkRy2RPvz z-Ogb@>(%xB`hGSLpma#-e$$SzX90D<932hL>)AlP&es984iVChCqTyQ2TsbUCx9;C zL!eVf2XIVByZ(0Q+;I`ZCB$GLKyi#-DTB>$j6D}P1DFq-2`m5>>R15GE7AQN0a}1V zI$D6k`ur%+CSn{Hi83NrY{^g&Qw?Bad5vTA`7-blpwT(bmN%FR90xuC_UYIU91uB< zIArKico_%;7~Sw`x$D{L-HT=D<-iJHC2-ale|LwB&;m3&pYPGX+30-MC=XJre`_q* z3+w`R1MiCz2OTnWJ6<6Wz!P-V@D0Es_3S3#Twqm!#@GpLl)?AN5Zfnse;12*%XA;t z=>2GTSNHK>-N#;ehISF*WE_Yv)dZ-NGOtx!U#y1yC~%<~f9?q1-vev_UIJcG51$My z2G#;M0M~mS#78Tz6?jXe*ds60E}=Q49$~5oU}OAD$M7qF^MH>5t4E0IyMZTxC*)$| zfEB=c;NB6)@FDOfumyMr_@BZwDhaEK35|IA5n!Z8>m(jDEEUko4=JYXR;9p6B$&oxsmk0CVxG>KmJ-6Ar6x&>v?4RI8}7ST27J@aa4;?vhGs_`#>NBK{C)|heETWjR(S*0<=>;;A22*_27c{@gihd(YP+ot z2{Li;0BQC7vn2Lz0B*z7_+53oPJ$+2WIV6X`Db!v<$B;Q*()WFdjL~j?wd*i*qCpP zzYe&=``yjJEto-(fWolDA;BtrHbZS&p}K&b$~GZQP&GmF)H*T$Q^4)sZ*I_MUBRZ~ z(gkaPXEB8{R9Cs3+$7dPd4h3g2dN1dexa$7RVM=ljQ+Yx`}z1ar?EvXNivDJtM&vl>Wrgqh9|fr)X5GHt)q$3GH;V6O3_4 zc{~J2V@B>)C~v(9Gkwd=@xKs^UsPn2RlSTEJq0tcc9T5p2@#=Rq$pVku(^ICaETbX z#5uoRY|R9lmBbU=qKwMoY}w!v*<_X%hKuR}Dy2jhD)y|!Oysa=##H?}gRM%2R<-e~ z**x@mvRl=*J1(skcgq4L7phCU>48@bxdE8B? z^T!Vv;G16dwi@%`jnx7L5ulRXIRAN=Qlndd2*NMkf^f6*{!+|{?XVd^J_4jM<;0~# zYyCF%2gDoS_k{d~vSr@7Kq}As8JOi*niT&nUh&u!5Kp`yon~>7LV4cmU}~7uZwY2) zj%l9A2uO|~w5XRY7sP4jFy)EAnQ|595MAlw z$)Nlbmk~VWykCJCADFKYpUOvoD!GeAn9?sE0RoZ*-)2pi$vlyvVwV8Z<^CHLGb~>7 z3Jd|sz?+!o3oPvNdUM<`kn+_3Jf#foV@XehwoEKs+btru2I5dQkaFDrEX9bW&X)#& z--mWgv>0@;R5l(OAhHCgCbxLgGSB_DhjvUX_7PnubGbx*y=*>2fa#dI{R`zPEJEs+ zd_3=YfQ7R4G)sUKxpn&%j_~DxWWt>{SRotFu{yx6!EaaWbP3SusmOq2Vl&YPbu0m9 z_ylP1-2ZEY;!ioCM=7C8fCkyT+OFeQo5usZ7TT_?yns)DT1x=CT6Y7{hY7Zab}Jif zbzaZ(J%GDTKY;*c3E!4;Wb?G^0cJZFWC-i|loNJ(?`O*cOtT(frbmE1K>(H&x`+-B z*URQZ9$+S>66g{jpuFG?=+!9=Oj{37gBc632rGD&A6%QxlFf%gz;vQpw1nsV$`9>c zCNSM*0q*vzfdHq62e8}dR#^g6=MfD7soEt~hH#DpF%O_&$j ztE>soC7Wkbm}SPhG5gP0EDP;bc39!OK8}`{cL~rD2vF7;fE(63MS#q31h8ua1(Z+k zcgy`93IiFd0lGN#5wHtFyOj-mTOL!V4+J<(aIy3Hq--7W0LS&AJ7#=&Xt%O~yVhfi zLx4V82k6FZIdags@S4zWWr0efn^GT?t-CD&28JQP0xxw4NUr;L*Lgl9573QD!GlT( zj_IU}8$$af9=;6^DGTUDgF#sdgASc^=L6mx+Ar~NZP+XgU?Ujv(Hk-BtutKm^7oBjvv_7sC@wP^5qe+KxM z)c;|n_k&|0fbRwVmVCwK=q8of}SMT3FT;4xc0@%7hw{ppCz{^CRBmA0|dj?DnZd2P^ zbYS*(ehV||*G&P-{T8}LP)7g2|KtTWc;|mbC=*X6R;tZi{7trbU$*Kirc6Ei;W2JlzRTr-Q$Vv0lorNOT-Gqo*_0)LS$_TtR; zEfoQ>m~k&FfX$Z;~zAWSi_*eza;FH!^@J6CcKmHr433 z!@1^r(hQFTyA=fwbLxbgXO)q?qqyJ0I5zpH8s`mJJ2z;}W!h{XHH^N77xrFt(*@Evg6eo+ipOafRERAY9fS`3_znWnT3ILEtoGjI>l zYU1gLRhSB@zbMyEOrdQPxrv=!#qKo4CV++Akoru_z{O?q23LF6*;>J)r=dWs#muUD zfK%sG{6oF|4RUk(yC*96CX)bGU(CSlO1=~`cVG=>$r67);cno5;Dyt~__xXBH+r9U zU`F-+DVM(&Q+4ebZ=OGu1h9pSX-aDr1Lpx(0M`N^&2#$DfdkxuELCq+&E17ya_XXdz->wv%)~hT>exN z08F*e?p8imG4NdA5`~6U!+h@*;7Nsu&X^m$LHYdp5s1BAJ$?&j?*4w9#TzMbGDi{# z;A?|AV7^4+xe5)J=yg8ysEG8W^5eLmQgQuW{T*(m^bUEmw`BMaFe7;-kM(gQfYk;z zEYwRMG%7TlFEWg@V0EiJ!v^4`DX1r`R7P_>re;6VK1 z$B_VTSg6KqUR{qFI66yWaiubc^M(`^H@rZeVvz_^>NO1yV;;O{ zR?j}9&yL98ZRECO>6NM~PfR=%CLeS{yi$ zPyS-A)8$5o%A}Y;f)J24 zk2oP!Xb>?V^*&mr0FY9E1Z8|<)&a870E_-Nuh#%gPQZdCaC008%D7DNg8;@~=|~{i zaR7|yZMYm@D+-hjzlxLx47dSeYt`Svz+X;)TUp;)3HVtLv<=~7R{>BQfLkj(^f`d$ z2UzqlF!%t76o6RaSYPajy&Qjw4b)Uhl~^OAuzavFl+z7UU!RMXbx4_vomjvU#WGcj ztHU>if+v^{Z~0^Z0J7sqz}cSM`3&Kf4-E-K*WsFT?jkWB=wH8HyWbctag_pq6_0?? zdk%OtWsoRFkkfrG(*c^T8FsekMU-_Fo>VQ6y)&zS;`*o?`JC9snVF6C^}i~=WsEKR z^grC&beOm4-#K0dh}~YCEw`>R2J@H(E23R4cMP1WyGVJrBds>BjM@Y7xb;upvH!}Ja6xhf03 zfk)fw1ppj3xwKEPLQ#WkLso{p9}Xq&6tkItAZz7#Hvlk`W8~89tCsAB0)Sk0FxyXQ zs?&CIjy4SHcJ%pn>{|-~gbd^F4jFuzCssk!?iSBWKgzI%4gREIvw%-X(eSlt*@h*! z;&OH9HQO#R$k`exvBjH>k&x}?ZrdiuDjUj9}30i$N*Bbdb!#6m@Ab+FJU{7|i7 z{zY*0oBTD;i%$kh91K&0F+^~Dzi`>#f{cU_vOFw}u_(%w-*v|b#}vk#wHbCeiqiO$ zC>auZXRREnFonNzz}S02*E7~T*Ll~e*6EIopJcq2a{GR%v&yR7XRkuBfxp4Gf${bm zkF;K1s`kQ<99;tb7$cdooI33atxrFQ_&igE2SZ4eRrAYo6UQlS$!ss|Dz-g-5iBIq zzDyp`AM4*y+)my`J@v+U8b%V*9d5%)kw%V5ZbRWsUhz$>fMPs~qFa|+@WtM@trWZz z7d->EIyQfjxV|TeDv8XARBUkFqT+($#bRu|6TN=jm?ATs59JDaygF+|SQAMldc_%< zdpcA)HbtU8J{8;iaM6|g9$Ix{*8 zg-;4e)x|~5%DIXi@~Lu~hRDUb_}yH@XyDy*M_RI`ol>&-mWh_hD~PzpQL(&9g)a(tpY8sbtTC=R{c-w} z$Be;D$E>QptA3EGt=?Ghyg*bqZ5J2X)QPp+>@n`38DMrqv2%8D&gUO=V4m1ci++6JC~NPT6$+JgV!hE2^jX zx$zfcuDs5v?t`VxYciYquJX=)S>hP z$q!wpNjq>GgJJyPzr1U8ME%cy+hypajVs!a*)+ZjISG%7Q2p4?l`O%PM)hZ*r)6Em zC2~6~l?G_Y zY6RQVZZW{$z~l7GrhnGwdlz3=9+F9%OSosPNvw$pF7hlInhyxJ)%Vo*YjnC~nujjQ z1~NQYJ-mMy1qy?hAU3EG!P3FTC^why{ppa=k|J|&&#Pwkr9WpkN|Q*(d@TF8(OyPp z+w)&+8QsO!x1qQpJKRvsfbFRfOv_EnsLlW!me5b3`eBmMuHgohqc4*Ay+`L|-z~{q z%O%MzrrEsE5U$`<;b)U5=CT)?75v5bOY#qwwc)^|L56{{qbjv97hAc2vt#Sh?f%K9 z)r?i_H9xX%@E%iF-<+Y2)6hcFA}&G>kt8X#@Rveg|4HMs{UH6!bWG&U6#c}vgQmlV z9zH5C8va+-gpKy)u@am5`}qIjn)!VhyLj_CHX0EuMX}9lPt_LEL^4TJyl(36vtd@* zyw$fx4o36|CzWKf5AF|C_szu$rXOZK>=SknuEf{nhpFmw(im*El-dhdiMjdCJy|0` zXAgUYQ7YCDS;yuysZp}8pIjc$!u(#{A=s?F?_+x{#9|vdWI6F1Su#oBU)Fh_4XRHreaMvguQf#_zZ)G^dM zOa|P#>BfmknlW)*?U*U{Jw@E^XRY@chu)k|1)l1CN&mCT;40_4zH@=KbV2)6WY+7m zU+D_w_%kmW8ydL1OzLr}LYhwMjqz*aUm61vqf@(!?{5|7p5dkQi5dE;oapx2&F*|0 zPp~;2{+yAR@gacgYTjZlx6W_t&CrrPp3C7ce2=!v~TmyJ{g@?yub(z z8NQD@i@}pck4cH?4|^K6mnkG6EW9e|ez$X5C_8L5%$!MfpL^e5PDTn3xSZUZ9vb~N znv;B-oPc!l&%fJk+oPzM`_u7MVm&x2|sUF)-=>J#?>?tJyYb6h zXoPb|hVHFhwO#ouYIi#PCoG|3e4N;J0)Fv?C^=~fv?y1XgrqX#vDl5|bIR*cR>nzA zhDjWA@d{%Xit$X}BRo^Qtrsc8k?({sWBE^pM2AoOGM5fN*vRE;KSu*m4H2Ij2%o_o z5Ck>=|M3e2Y*LRM5Y7C5bPap#{2vd+K2g|((E2O~Bd-E?7GJpDuXzMAoV~jmmhKP| z6%mQV&n?g)PF2#Mw%rb*Tl)QXSi&lWnc^HCy2 ztxG0HlQl){I-%FffhVO@BVW5)yJ*4&l;S|GTyWYiYromr{h>S{Q}s~8`9!5*j7kkGr{YsGW*T*g*ISy`D;5Tz?E=#o%e@O7Wn zJ^vI$(|`yI??zSK3z0GrhK!Dm#$`@XlTaH!rNIbUF{+*s3$rBmyKYRD7*c~WQFUaq zrV5hdY(R6|5hK)y5e<1Q9E_0m0y%m&juf?nJl5TX)z#N8ZWy~po7}adFVo_5+mvP`MCgyD(@oXRu>2u z6mI43i=*@WV!4QvmLZNbz`<_T2k+P+*54XRDJCJ^YZR*Er$jiN^PJEQ;*fTWA!fr0 zU9EhIg54_&eM-zuZtF11a#^TpvJzteArx(wFyi;39DQSBsUyNT(m!EpRrTZ@4;>C+ zDd0#G6hA3R*3qs`zVk7+w)RXpr2SuZ*Xxw7s#4x$xqkWTLIxa@{aR{-TQH8Z6m$U= zxymapE^Z!~due89IFr_>8^CYmPfe4yDFS#VU9Iz9yBqr2Iy;vOkmH;PM>ow`SL^9) zO)HU%{?|yPuo=OYpKHyoXprPt#o_18miUVoA9|a{8tF$A@%v94gQ7rR{eSFtM%ChB zTp~c@7oo-`)FK+SckfQbaXJU84LItL#msZfsG@D0Oj4gId;LEZpQlI+_CC$UU7RhQ*Cp&DRf{XkmmIAyx+%& z`?`ixkqf`w37o;m%*<5-Yh*=Ya`K_x7-rv@h{pn?r{eI43*GGA_syF({QXtzXwsKl z!za;>3F-%Zk)OD~_Yv43f_0*QayVEUo8h}I9it{mW}{G!*$f^Asl!-Zh452Wj+cBS507CNZR$;mOEot>>qPELmF&$7h- z8Z=4(?d{sEd{E)bmoG8@#E@+(t^*3mE!^_QHefg{y##rq23;q&gjvHE-bX(E zvhwm>xYVgM7ra~|yTI4+!-iz4y}i9CiZI_9``UYkZntyCUc8HPiFxCo4Q3AQjqrAh zY);btFQCr6ot*X`VwJ+9*xA{eT)pgsl6CkOAw9_>?g5J)pN{I<) zBbjg`Nv?Ac((UFIy%xa7&COi`Ziutu;$p$czriZ01mQ=PFn2XreC|3&tLd;fR27#DB5pw?`)AUs*Ch9l^|DkSoilP+@NN}vwQ1wi4WHV zQC(GX_=vr)Q-|&x`73G$jkQg)h+en!Hqa_DF)#}F1qCaf@mdc=|95Dqa!x){gPH9i zV?*@s?CcCa=UDfQ;>({^lRK>E`$Aa8!5j0X-)jQn9K02n>Gh~mM-=~$7n|9(dh5q(xz!}!$cOQx>lSO8&29Qa3sVRl#U`5>d>-R@@cqLW+l z;(d!w?&$VMEF$J~t+gv+8XsV{?8ru2S|d~JC>#duN0fr9rfIA8p7Tl=%Gd3;5j0gl{XL5?BJu6<1x?2-qq(_DB``o0$ZvvMLIO9-U1 zd6d|i9A}^+H8r)kJDR5HRm9&(C(P`7S@DU`^@^L8&J~|i`@^OFubQrXB=Ll=oE|>b z45}!+k*4V9QB6xrWB(IPbMtlHC3?&a)797Fxhk|f<{5m`_ui>+{L1vYiY2LMd}NoY zboL%S!X5F}uzJDy!A(_F)sTZUP2bdXfhpNLe+^eEK^Zu{>b378}kxR2~IUYSK2z(A(l~$p-r=?pVK$uNQ(Rq zRZ+Cxo{=Kjo~1OtgclqpCa(qO7Z9Lid6|A$NByGW2vO^HENWk zTXF@B`YoS&1t?Y_vqom~3;n9`!B-Yc=MWRlqSo8v{=#7vWFtq~fvn216}{qjJ>fh2 zFtexKCtYFglhe~IEZ`XB=mSi#Ail))ksMi#zx!U(U3HS8YwV(pGNhKVZ-fevUUyuD zscL_C5ZdJx$a@hH&)tc^5eVxxgj&R7JFcUnV+&oHJW+KyzL`I{)HLjhLvf6yX2y|L zUTfl{P$Mf^Elt1)^QAB5 zBn_LX4(A&pbD1OhV6Qj;eJ{Z4VJRtRUgN*-N=Ed zm!eQh_+JV#F)>{{y?ElTttQDSys=}X!ylnt;{MyYeo0?n|01nOr|_Ow48TFS{#$N0 zCng~gAL7QzLm!7D*Ta+Uuzfc(3he$^SBnQ6ulE-0j29wNXoAqj563kmTZVT$?gy-R zux8a$9XFttWbXK3@mpN1toWs`_J*UMNiMoBcuwz6oehWBj6v3`lwUvGqcS)O%&9aj zm}QzP=b0^AuWGPvnA?63o^%vQ?p`}eRE(w;7IT>5Fc&x~Zo-8npAR|;AhmRLJ;c8S zs9opyrH-vBxLM?WAwF{yhe>^qG{QB;Xfx&)@P6Bfmpb^`j z4W#fow4?ScpWNMCEUkHnJcOq`X2 zI6QD8e9-!)5RSZcfB0{v+IzbXHC99z6PMZoR3aQr+Vw{k-}Tj`SuBH<_^cyO3bm}P zEKRM9&*DM`ZL?c?_cftG+wW&KQU3t^H^fpO*4RYqla z&qO?IFVqDWYV-w}Ej9%==cJjS9rS5~uViw=rf#k*h4pBJUspnzK#`(YBgZ5lCg;_D zhU)*dez-}})zuxKJ0s8;BYM?;FW(9;Bqb#s6869RM~E1C&yN)pzpLQ9@Qc4ByD5C3 z$y-!KRaNqxNJhfDs{rPn^JL5FJCj}VDs_X~X^n%c{GpcA>CBxsE3c>@9OGGs2&ZzWT%c_H{Vw7jz2Kpb`zf5@pu`nkmrCeBRLt08C!I} zRBbct`S$JG3!4NED>O%uj-DmNWc-fen0qk{dhxJ~De1GP5qLeWrS1*P=mu8LH2-z3 zPgZtHK09Neoaravratl@v%e-2NGo%+u&{U`v;AQE3$-WrdP&%R;zv`t2kHh6`7t}S zvoQS^?$gt>u`cLltUI)%8OO3>xZFqIzwSoW|Xgz_4y>+mdCz zJD;XjAd@$GSplWnCy=r>4j$I%Iap{qY?-afs@dfc>tvHAzsg^#uS!qH>-H0%?RmV)N#m;xP5b5OV2-G&Zx#bJ`SaG zB9JPe3YNHB4iRNzXW#nXbktycnqa|`l36*oFYjL8?q2^<1(Th&qfOOd zeb2jUU8ycC4+fQQxA;c^W?_8e5FGCWuY-9zE)adO-xOl>jt#nh4(1bqtDjq?QszIq?K%a$i5pz46Tg;HEQ5QhlRM~ZFLJW8mnDR;q^i*VA_E_mOa|(l z=K4Uq;x}(yE$miTdu`V)9lb2}X*R4d{RkiTlb;yv@WB3Dy_0}w2PPXX3t`U&A26`K1Rqd@wkS2DHC!%{Mr{nK1p->-cULonVJ7S- z%jT>ZrB=$h9&dUW1->Y(Ww;XrSz-MHJ?@7+lzPXR)o?d5);s+Nb2o5By43j3KUSd9coEPpb+?enY zqjhm~Y_{o7;>^OIgMe>q^2xMOF4;cT_q^*P&^B^qS`Vj86d!L~7K-L%Wo^KwljDMX z8{ABT2MXCXV0DeUJE)_?%8~y}XUnoq4&er&_~36}Ai0%jfxdHp`#k<`Fo+347Hb8Z~@Y7Re9nQ$=_Zptsy z4hsYQq1<1;G8mn}*BRmYNjl0W@F!26l(USXa;K;f@nFH2N*cccXb&awf^erKl3*CVz0ZXmE)IoD9fLk$mdB! z=hjh#bDOE@W{@LM)Ru-xEb=HncLK(rdUqvN+lNq()(7S!Z|D|DwuXEG$ipmoW_n1;WO*kk*FpSh6%BWI_y|tY$}TyW`;=X=X2m>qpI|w@ zN9Z#`m)TJcMHw^$C34;#0u>USSLhG)@@No{T4C(3!VqVs@gRdKDxJaQwA2Wt0g#qd z*7_(-{&KjvMNH8-W>-b>SPlNGXjzuvF?>^N3 zUYyG64cIanoj8C?~I6x7ng2F_P{$dWut$u9ZAzerHH%k*~R~h zRXt{xB|nbFzQwNU=EQ6-l6KBzJqHz4X;7+R|M7X^x5i!5fAFst@a!h?dzZB%pJ&er zvyJWXd)3L;`w0HQgSg$PLMEC!XCNM|S`mr%EA2?2V+6$#)CVp-6c(rCQ{_vk8S%TX zC3HF_(9P}QTY83eYb<)iy}TIh1->*eYI|zJT!E%DMg=pCUcaV^Wf(Cvxt;S73y6lI zfV8OjT6vqjKi;}h072MPvd;L1>7VWd5v);L;{AoD`X&3?RAra7iR;Yx>B0%`bqP$n zX}Yb;YH=?i>ga(C(d5t59~lsufq{V)gVwz4AM(B?kt{!&9(7M0)z0DJVV9>f2ON!p zoP0Z*wb&$DfCd`E@qn@(evNn`B=lFB7=rWkY2)+AUzQD62^~X5Fc8Yr$dS*Qy`-U} zbSIJrLPA138{#n)JJ?f{V2Tq742?t>hI8%JEn&8NMv6EP7($bS^rnQeH)QPwrdab_ z@pK)GXD)w5f#|h!%#e0hOUoRXen;#W-F6Ch4Wr`mByxLi5BnW;{WD`Oyt)e5Mo-AN=-Om_mfOhlze;k-Q{beoX?6AzxUZO`;>t+^)PK_>9N zYhw%K^=M+m&;KgriUg?=;9v~1DFY2`l#kn?2Bvs@eLb`qO;L&l!UH^dL~95X9UlWf zED0+QiF|;KqTxr#Nkl5-J^VQli}C>e!T$|?(!NI@a$xB~hIhR_3JwmA`MIPsAACF{ zddT;MR{Yl!NE;G(K$-JxF>^Uti;B#iU(^}?{FGv#}nNHiq?|f z@0)_W4k{|@oK*^$@ZYOJ!KGWP-HD<=OgreK*Be8cgQ@dFrtfCT96#No{>gCyKI5aH z0KzElXd&OBp)$|ho3xFSMA4YegDC79ai*-6xmvz0Z>iocqg{4g*u>g*!{dk2S> zjEszKx>JZdGp5~rDE0P#|B1EQPqI~*oDBAOE^D&ZP|=-U;Ew^Z42}J9*uIQZwiGS{ zhaer=jGqgLdy%X3#GRje!9l)foDsrd(%{;2coB$8hY38;3wHdwxyynZ)z~T}patQ} zeE9HT?)v5?4_^U*Mg0e504=0*<)6SzH^+s@?&e_HgNWPeZz`A=5Hv74GU9p}!IJrn z^TOA*QDbC${KVW<6aw&^D0rcelav2lKJrOjNkRcq+SiWw@NjS(N6;Y?Q&S2eqN0)? z=bivoVM9YhG2r|de!&0){*zD{WGE&A2s#BtTFBAdu2;#>AV6_#H0hq3o5O3{M+d?b z{rqkq$^D)t`j5hbOok2&AKK1|0hFgQLR z15i3*D4LnGi_2kgDCtx_WVgicb%;bR1q_g6nB+*x;H2MRTP{;xhe}2U6KIP@*8S(b?JS zp|N~b?^r(ONQAk5i-(vC^6q>KBsYIyb)aXnOiWJ)zHnJ=e)xh<&$eRQvH5&*W~Py{ z4h!`Z0hXzvLHn5e&LkOu7L>$kd~WIBu!mHnC%BX*2-+lwkjpJAD|={loTChPI^vGWq44~aZgPA=WJ)^%ZU#~(0vDt zq(Pgjuc>K%1sX`&Y30~=JlD}&`HF^xgR)8kHN*%?Y_tIf^Dppc6e;I>dF(Z&DYRy4 zDnV!ez|Db_3)ygp(x0QXZtuY?$p=W;=j5qADb(OLB2Q1xho^KHFp|d(4HPYNWb7Ni zENILn!&@hVlJ7PL+@Sn`ozP<^YgiC8oihleM-inET)5nVf&vAoF!0>g-roMyM=Z*O zYx;TFmyD^|Akg+(!RjFiZA>AF3|_W?7_Ekbs%rm}{J=4G7c9^#Cc({HJNLaLmq0on zgAS^R4VGJ-oa=9{Hu{qfM|Sx~jA$;a-u4R!^7EI4VtxYbv%!6nS5#!T|9IEoO2n2E zFW_jkD{bCcFcy$HTK8^Ra3F)5H7oB;%Q)Pjkb>Q#`Nnq+L1=&q(?jFCttk$(W}iIT zaz$RM;4#a)>$4LuoZA~rWSs|N=MYx|SoOXY=oV~?9?BnsZ@GYpo$%qS%TZgHT-0B*Xt-+4)0 zU0we7)(KSTGEb>l%k}K>@v$JYda4I60xtDrcW)2xeaoVagM&lN&e64pGPr+FJFwxz zq8UT=g=#6sreT)0(6>?o`0_6dL7%>rnVIPeZWPn^EiV@c+vJdhrAl{%W zg`~iUpU6{EQPqoI%)isvxij+Ac>KrRl8RrCWJ(ORa1~Am6*u+l+?=S9k&yrw=#ZTx zOkB+HK?hZ4Tz?P;inn2bi&7p2l2HL0XYZ%&d>!EUZQKU6_LGRQv9YH_EH9g*wtPut zXhAPHYRmUnVbZd(+4`8suAkXk=rGKV(h&ZXN}$=q(C{h`q&px(%P{Y&1 zqq)_pH(t1sdmBHT+7Vtejz@d5OW}0U>WpYW)&C1 z?G~ZSN`{)n?wbfFemZWOl+vs+W%$@ zgPwpIKA;tU@4??3p(FyP9Ph<4|A4PO>3MeP-wF?DU-tXyLLHCfj|!6b#j7us=<&gmE%Qw57+qX9hx z4pXHR{oDhb3VBCHd80zTjsp4y97%-I_@xSYaXRnCK#X4rGWolQhZl?bl#? zlgk=_u}OmuNYRNs+@8qebgJ1HdeL{gB~o`MzN6Ofs45Lv6Qb&Nvrc;J8@=X}^p+yb z5{FNH93uog*%PUfuU!Q4Eh@|j%38!kM?NN>yIzli`QDeV*Kr0MQ)DsSsFel-&!W1T z!BmWiHbyW!*Jt%=E9)>!^nVAB_@4YN0uT5<9`%QT?f*KA^nX3{d-VYiz>Um*z)H8# Qd;A||`ImC#G8P~IA7%MrKL7v# literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs index e255baf459..730eed0e0f 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs @@ -1,9 +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.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Taiko.Skinning; using osu.Game.Rulesets.Taiko.UI; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Tests.Visual; @@ -12,6 +16,12 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning { public class TestSceneTaikoPlayfield : TaikoSkinnableTestScene { + public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] + { + typeof(HitTarget), + typeof(LegacyHitTarget), + }).ToList(); + [Cached(typeof(IScrollingInfo))] private ScrollingTestContainer.TestScrollingInfo info = new ScrollingTestContainer.TestScrollingInfo { diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyHitTarget.cs b/osu.Game.Rulesets.Taiko/Skinning/LegacyHitTarget.cs new file mode 100644 index 0000000000..51aea9b9ab --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Skinning/LegacyHitTarget.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 osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Skinning; +using osuTK; + +namespace osu.Game.Rulesets.Taiko.Skinning +{ + public class LegacyHitTarget : CompositeDrawable + { + [BackgroundDependencyLoader] + private void load(ISkinSource skin) + { + RelativeSizeAxes = Axes.Both; + + InternalChildren = new Drawable[] + { + new Sprite + { + Texture = skin.GetTexture("approachcircle"), + Scale = new Vector2(0.73f), + Alpha = 0.7f, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + new Sprite + { + Texture = skin.GetTexture("taikobigcircle"), + Scale = new Vector2(0.7f), + Alpha = 0.5f, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + }; + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs index 3af7df07c4..6b59718173 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs @@ -49,6 +49,12 @@ namespace osu.Game.Rulesets.Taiko.Skinning case TaikoSkinComponents.DrumRollTick: return this.GetAnimation("sliderscorepoint", false, false); + + case TaikoSkinComponents.HitTarget: + if (GetTexture("taikobigcircle") != null) + return new LegacyHitTarget(); + + return null; } return source.GetDrawableComponent(component); diff --git a/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs b/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs index 156ea71c16..775eeb4e38 100644 --- a/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs +++ b/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs @@ -10,6 +10,7 @@ namespace osu.Game.Rulesets.Taiko RimHit, DrumRollBody, DrumRollTick, - Swell + Swell, + HitTarget } } diff --git a/osu.Game.Rulesets.Taiko/UI/HitTarget.cs b/osu.Game.Rulesets.Taiko/UI/HitTarget.cs index 2bb208bd1d..88886508af 100644 --- a/osu.Game.Rulesets.Taiko/UI/HitTarget.cs +++ b/osu.Game.Rulesets.Taiko/UI/HitTarget.cs @@ -22,6 +22,8 @@ namespace osu.Game.Rulesets.Taiko.UI public HitTarget() { + RelativeSizeAxes = Axes.Both; + Children = new Drawable[] { new Box diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index bde9085c23..375d9995c0 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -17,6 +17,7 @@ using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Rulesets.Taiko.Objects.Drawables; using osu.Game.Rulesets.Taiko.Judgements; using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Skinning; using osuTK; using osuTK.Graphics; @@ -42,7 +43,7 @@ namespace osu.Game.Rulesets.Taiko.UI private readonly Container hitExplosionContainer; private readonly Container kiaiExplosionContainer; private readonly JudgementContainer judgementContainer; - internal readonly HitTarget HitTarget; + internal readonly Drawable HitTarget; private readonly ProxyContainer topLevelHitContainer; private readonly ProxyContainer barlineContainer; @@ -90,7 +91,7 @@ namespace osu.Game.Rulesets.Taiko.UI RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Left = HIT_TARGET_OFFSET }, Masking = true, - Children = new Drawable[] + Children = new[] { hitExplosionContainer = new Container { @@ -98,7 +99,7 @@ namespace osu.Game.Rulesets.Taiko.UI FillMode = FillMode.Fit, Blending = BlendingParameters.Additive, }, - HitTarget = new HitTarget + HitTarget = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.HitTarget), _ => new HitTarget()) { Anchor = Anchor.CentreLeft, Origin = Anchor.Centre, From ed9663985b439dba6b5c51281ba40cd6cb2e1c07 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Apr 2020 20:55:33 +0900 Subject: [PATCH 0345/1117] Rename panels --- .../Visual/Online/TestSceneDirectPanel.cs | 16 ++++++++-------- .../BeatmapListing/Panels/BeatmapPanel.cs | 2 +- .../{BeatmapPanelGrid.cs => GridBeatmapPanel.cs} | 4 ++-- .../{BeatmapPanelList.cs => ListBeatmapPanel.cs} | 4 ++-- osu.Game/Overlays/BeatmapListingOverlay.cs | 2 +- .../Beatmaps/PaginatedBeatmapContainer.cs | 2 +- osu.Game/Overlays/Rankings/SpotlightsLayout.cs | 2 +- 7 files changed, 16 insertions(+), 16 deletions(-) rename osu.Game/Overlays/BeatmapListing/Panels/{BeatmapPanelGrid.cs => GridBeatmapPanel.cs} (98%) rename osu.Game/Overlays/BeatmapListing/Panels/{BeatmapPanelList.cs => ListBeatmapPanel.cs} (99%) diff --git a/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs index 5809f93d90..d6ed654bac 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs @@ -20,8 +20,8 @@ namespace osu.Game.Tests.Visual.Online { public override IReadOnlyList RequiredTypes => new[] { - typeof(BeatmapPanelGrid), - typeof(BeatmapPanelList), + typeof(GridBeatmapPanel), + typeof(ListBeatmapPanel), typeof(IconPill) }; @@ -126,12 +126,12 @@ namespace osu.Game.Tests.Visual.Online Spacing = new Vector2(5, 20), Children = new Drawable[] { - new BeatmapPanelGrid(normal), - new BeatmapPanelGrid(undownloadable), - new BeatmapPanelGrid(manyDifficulties), - new BeatmapPanelList(normal), - new BeatmapPanelList(undownloadable), - new BeatmapPanelList(manyDifficulties), + new GridBeatmapPanel(normal), + new GridBeatmapPanel(undownloadable), + new GridBeatmapPanel(manyDifficulties), + new ListBeatmapPanel(normal), + new ListBeatmapPanel(undownloadable), + new ListBeatmapPanel(manyDifficulties), }, }, }; diff --git a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs index f260bf1573..88c15776cd 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs @@ -148,7 +148,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels if (SetInfo.Beatmaps.Count > maximum_difficulty_icons) { foreach (var ruleset in SetInfo.Beatmaps.Select(b => b.Ruleset).Distinct()) - icons.Add(new GroupedDifficultyIcon(SetInfo.Beatmaps.FindAll(b => b.Ruleset.Equals(ruleset)), ruleset, this is BeatmapPanelList ? Color4.White : colours.Gray5)); + icons.Add(new GroupedDifficultyIcon(SetInfo.Beatmaps.FindAll(b => b.Ruleset.Equals(ruleset)), ruleset, this is ListBeatmapPanel ? Color4.White : colours.Gray5)); } else { diff --git a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelGrid.cs b/osu.Game/Overlays/BeatmapListing/Panels/GridBeatmapPanel.cs similarity index 98% rename from osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelGrid.cs rename to osu.Game/Overlays/BeatmapListing/Panels/GridBeatmapPanel.cs index caa7eb6441..84d35da096 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelGrid.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/GridBeatmapPanel.cs @@ -19,7 +19,7 @@ using osuTK.Graphics; namespace osu.Game.Overlays.BeatmapListing.Panels { - public class BeatmapPanelGrid : BeatmapPanel + public class GridBeatmapPanel : BeatmapPanel { private const float horizontal_padding = 10; private const float vertical_padding = 5; @@ -31,7 +31,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels protected override PlayButton PlayButton => playButton; protected override Box PreviewBar => progressBar; - public BeatmapPanelGrid(BeatmapSetInfo beatmap) + public GridBeatmapPanel(BeatmapSetInfo beatmap) : base(beatmap) { Width = 380; diff --git a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelList.cs b/osu.Game/Overlays/BeatmapListing/Panels/ListBeatmapPanel.cs similarity index 99% rename from osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelList.cs rename to osu.Game/Overlays/BeatmapListing/Panels/ListBeatmapPanel.cs index 3245ddea99..433ea37f06 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelList.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/ListBeatmapPanel.cs @@ -19,7 +19,7 @@ using osuTK.Graphics; namespace osu.Game.Overlays.BeatmapListing.Panels { - public class BeatmapPanelList : BeatmapPanel + public class ListBeatmapPanel : BeatmapPanel { private const float transition_duration = 120; private const float horizontal_padding = 10; @@ -36,7 +36,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels protected override PlayButton PlayButton => playButton; protected override Box PreviewBar => progressBar; - public BeatmapPanelList(BeatmapSetInfo beatmap) + public ListBeatmapPanel(BeatmapSetInfo beatmap) : base(beatmap) { RelativeSizeAxes = Axes.X; diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index a024e2c74e..f680f7c67b 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -125,7 +125,7 @@ namespace osu.Game.Overlays Spacing = new Vector2(10), Alpha = 0, Margin = new MarginPadding { Vertical = 15 }, - ChildrenEnumerable = beatmaps.Select(b => new BeatmapPanelGrid(b) + ChildrenEnumerable = beatmaps.Select(b => new GridBeatmapPanel(b) { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, diff --git a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs index 5f70dc4d75..191f3c908a 100644 --- a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs @@ -33,7 +33,7 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps protected override Drawable CreateDrawableItem(APIBeatmapSet model) => !model.OnlineBeatmapSetID.HasValue ? null - : new BeatmapPanelGrid(model.ToBeatmapSet(Rulesets)) + : new GridBeatmapPanel(model.ToBeatmapSet(Rulesets)) { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, diff --git a/osu.Game/Overlays/Rankings/SpotlightsLayout.cs b/osu.Game/Overlays/Rankings/SpotlightsLayout.cs index 895fa94af5..917509e842 100644 --- a/osu.Game/Overlays/Rankings/SpotlightsLayout.cs +++ b/osu.Game/Overlays/Rankings/SpotlightsLayout.cs @@ -140,7 +140,7 @@ namespace osu.Game.Overlays.Rankings AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, Spacing = new Vector2(10), - Children = response.BeatmapSets.Select(b => new BeatmapPanelGrid(b.ToBeatmapSet(rulesets)) + Children = response.BeatmapSets.Select(b => new GridBeatmapPanel(b.ToBeatmapSet(rulesets)) { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, From 6193b80589790cc4f90a6141cd34fa6900b2723f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Apr 2020 21:20:57 +0900 Subject: [PATCH 0346/1117] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index d2bdbc8b61..25942863c5 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 35ee0864e1..9c17c453a6 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -23,7 +23,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 0200fca9a3..07ea4b9c2a 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -80,7 +80,7 @@ - + From e9a2e92adf3573c27096b796d99db094f2fd6f5d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Apr 2020 14:43:22 +0900 Subject: [PATCH 0347/1117] Fix incorrect beatmap comments --- osu.Game.Tests/Resources/hitobject-combo-offset.osu | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Resources/hitobject-combo-offset.osu b/osu.Game.Tests/Resources/hitobject-combo-offset.osu index c1f0dab8e9..d39a3e8548 100644 --- a/osu.Game.Tests/Resources/hitobject-combo-offset.osu +++ b/osu.Game.Tests/Resources/hitobject-combo-offset.osu @@ -5,27 +5,27 @@ osu file format v14 255,193,1000,49,0,0:0:0:0: // Combo index = 4 -// Slider with new combo followed by circle with no new combo +// 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 -// Slider without new combo followed by circle with no new combo +// 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 -// Slider without new combo followed by circle with new combo +// 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 -// Slider with new combo and offset (1) followed by circle with new combo and offset (3) +// 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 -// Slider with new combo and offset (2) followed by slider with no new combo followed by circle with no new combo +// 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: From 9713d903884469c247397ae3f7bd223fd0df21e9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Apr 2020 14:47:12 +0900 Subject: [PATCH 0348/1117] Always apply beatmap converter/processor --- .../Formats/LegacyBeatmapEncoderTest.cs | 61 +++++++++++++++++-- 1 file changed, 55 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs index f2b3a16f68..62cf2cec43 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs @@ -1,14 +1,21 @@ // 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.Linq; using NUnit.Framework; +using osu.Framework.Audio.Track; +using osu.Framework.Graphics.Textures; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Formats; using osu.Game.IO; using osu.Game.IO.Serialization; +using osu.Game.Rulesets.Catch; +using osu.Game.Rulesets.Mania; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Taiko; using osu.Game.Tests.Resources; namespace osu.Game.Tests.Beatmaps.Formats @@ -29,26 +36,68 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.That(encoded.Serialize(), Is.EqualTo(decoded.Serialize())); } - private Beatmap decode(string filename, out Beatmap encoded) + private IBeatmap decode(string filename, out IBeatmap encoded) { - using (var stream = TestResources.OpenResource(filename)) + using (var stream = TestResources.GetStore().GetStream(filename)) using (var sr = new LineBufferedReader(stream)) { - var legacyDecoded = new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(sr); + var legacyDecoded = convert(new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(sr)); using (var ms = new MemoryStream()) using (var sw = new StreamWriter(ms)) - using (var sr2 = new LineBufferedReader(ms)) + using (var sr2 = new LineBufferedReader(ms, true)) { new LegacyBeatmapEncoder(legacyDecoded).Encode(sw); - sw.Flush(); + sw.Flush(); ms.Position = 0; - encoded = new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(sr2); + encoded = convert(new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(sr2)); + return legacyDecoded; } } } + + private IBeatmap convert(IBeatmap beatmap) + { + switch (beatmap.BeatmapInfo.RulesetID) + { + case 0: + beatmap.BeatmapInfo.Ruleset = new OsuRuleset().RulesetInfo; + break; + + case 1: + beatmap.BeatmapInfo.Ruleset = new TaikoRuleset().RulesetInfo; + break; + + case 2: + beatmap.BeatmapInfo.Ruleset = new CatchRuleset().RulesetInfo; + break; + + case 3: + beatmap.BeatmapInfo.Ruleset = new ManiaRuleset().RulesetInfo; + break; + } + + return new TestWorkingBeatmap(beatmap).GetPlayableBeatmap(beatmap.BeatmapInfo.Ruleset); + } + + private class TestWorkingBeatmap : WorkingBeatmap + { + private readonly IBeatmap beatmap; + + public TestWorkingBeatmap(IBeatmap beatmap) + : base(beatmap.BeatmapInfo, null) + { + this.beatmap = beatmap; + } + + protected override IBeatmap GetBeatmap() => beatmap; + + protected override Texture GetBackground() => throw new NotImplementedException(); + + protected override Track GetTrack() => throw new NotImplementedException(); + } } } From 8ea76244a26699bc98a27926315292ee44a83897 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Apr 2020 14:48:32 +0900 Subject: [PATCH 0349/1117] Fix only single beatmap being tested --- osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs index 62cf2cec43..01edafcf31 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs @@ -23,14 +23,12 @@ namespace osu.Game.Tests.Beatmaps.Formats [TestFixture] public class LegacyBeatmapEncoderTest { - private const string normal = "Soleily - Renatus (Gamu) [Insane].osu"; - private static IEnumerable allBeatmaps => TestResources.GetStore().GetAvailableResources().Where(res => res.EndsWith(".osu")); [TestCaseSource(nameof(allBeatmaps))] - public void TestDecodeEncodedBeatmap(string name) + public void TestBeatmap(string name) { - var decoded = decode(normal, out var encoded); + var decoded = decode(name, out var encoded); Assert.That(decoded.HitObjects.Count, Is.EqualTo(encoded.HitObjects.Count)); Assert.That(encoded.Serialize(), Is.EqualTo(decoded.Serialize())); From 1e7e7417ed77dcec18944565b826c28919490a94 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Apr 2020 14:49:31 +0900 Subject: [PATCH 0350/1117] Fix testing relying on control point order --- .../Beatmaps/Formats/LegacyBeatmapEncoderTest.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs index 01edafcf31..bcc873b0b7 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; @@ -9,6 +10,7 @@ using NUnit.Framework; using osu.Framework.Audio.Track; using osu.Framework.Graphics.Textures; using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Formats; using osu.Game.IO; using osu.Game.IO.Serialization; @@ -30,10 +32,22 @@ namespace osu.Game.Tests.Beatmaps.Formats { var decoded = decode(name, out var encoded); - Assert.That(decoded.HitObjects.Count, Is.EqualTo(encoded.HitObjects.Count)); + sort(decoded); + sort(encoded); + Assert.That(encoded.Serialize(), Is.EqualTo(decoded.Serialize())); } + private void sort(IBeatmap beatmap) + { + // Sort control points to ensure a sane ordering, as they may be parsed in different orders. This works because each group contains only uniquely-typed control points. + foreach (var g in beatmap.ControlPointInfo.Groups) + { + ArrayList.Adapter((IList)g.ControlPoints).Sort( + Comparer.Create((c1, c2) => string.Compare(c1.GetType().ToString(), c2.GetType().ToString(), StringComparison.Ordinal))); + } + } + private IBeatmap decode(string filename, out IBeatmap encoded) { using (var stream = TestResources.GetStore().GetStream(filename)) From 21949ac499ab3ece38ddfb799a4f704a0a589c4b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Apr 2020 14:49:43 +0900 Subject: [PATCH 0351/1117] Add osu! test beatmap --- .../Resources/sample-beatmap-osu.osu | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 osu.Game.Tests/Resources/sample-beatmap-osu.osu diff --git a/osu.Game.Tests/Resources/sample-beatmap-osu.osu b/osu.Game.Tests/Resources/sample-beatmap-osu.osu new file mode 100644 index 0000000000..27c96077e6 --- /dev/null +++ b/osu.Game.Tests/Resources/sample-beatmap-osu.osu @@ -0,0 +1,32 @@ +osu file format v14 + +[General] +SampleSet: Normal +StackLeniency: 0.7 +Mode: 0 + +[Difficulty] +HPDrainRate:3 +CircleSize:5 +OverallDifficulty:8 +ApproachRate:8 +SliderMultiplier:3.59999990463257 +SliderTickRate:2 + +[TimingPoints] +24,352.941176470588,4,1,1,100,1,0 +6376,-50,4,1,1,100,0,0 + +[HitObjects] +98,69,24,1,0,0:0:0:0: +419,72,200,1,2,0:0:0:0: +81,314,376,1,6,0:0:0:0: +423,321,553,1,12,0:0:0:0: +86,192,729,2,0,P|459:193|460:193,1,359.999990463257 +86,192,1259,2,0,P|246:82|453:203,1,449.999988079071 +86,192,1876,2,0,B|256:30|257:313|464:177,1,359.999990463257 +86,55,2406,2,12,B|447:51|447:51|452:348|452:348|78:344,1,989.999973773957,14|2,0:0|0:0,0:0:0:0: +256,192,3553,12,0,4259,0:0:0:0: +67,57,4435,5,0,0:0:0:0: +440,52,4612,5,0,0:0:0:0: +86,181,4788,6,0,L|492:183,1,359.999990463257 \ No newline at end of file From a702a521f8c4af9fe4d645fcbd472466b6ba6ff5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Apr 2020 14:52:58 +0900 Subject: [PATCH 0352/1117] Fix not being able to serialise converted beatmaps --- .../Converters/TypedListConverter.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs index 6d244bff60..837650eb0a 100644 --- a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs +++ b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections; using System.Collections.Generic; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -43,13 +44,13 @@ namespace osu.Game.IO.Serialization.Converters var list = new List(); var obj = JObject.Load(reader); - var lookupTable = serializer.Deserialize>(obj["lookup_table"].CreateReader()); + var lookupTable = serializer.Deserialize>(obj["$lookup_table"].CreateReader()); - foreach (var tok in obj["items"]) + foreach (var tok in obj["$items"]) { var itemReader = tok.CreateReader(); - var typeName = lookupTable[(int)tok["type"]]; + var typeName = lookupTable[(int)tok["$type"]]; var instance = (T)Activator.CreateInstance(Type.GetType(typeName)); serializer.Populate(itemReader, instance); @@ -61,7 +62,7 @@ namespace osu.Game.IO.Serialization.Converters public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { - var list = (List)value; + var list = (IList)value; var lookupTable = new List(); var objects = new List(); @@ -84,16 +85,16 @@ namespace osu.Game.IO.Serialization.Converters } var itemObject = JObject.FromObject(item, serializer); - itemObject.AddFirst(new JProperty("type", typeId)); + itemObject.AddFirst(new JProperty("$type", typeId)); objects.Add(itemObject); } writer.WriteStartObject(); - writer.WritePropertyName("lookup_table"); + writer.WritePropertyName("$lookup_table"); serializer.Serialize(writer, lookupTable); - writer.WritePropertyName("items"); + writer.WritePropertyName("$items"); serializer.Serialize(writer, objects); writer.WriteEndObject(); From 3093c3e1857c8d5bc7fce5f5b8fa21b3bfeead05 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Apr 2020 14:55:17 +0900 Subject: [PATCH 0353/1117] Fix custom sample set not being written correctly --- .../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index fe63eec3f9..7721d50227 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.Globalization; using System.IO; using System.Linq; using System.Text; @@ -10,6 +11,7 @@ using osu.Game.Audio; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Legacy; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Legacy; using osu.Game.Rulesets.Objects.Types; namespace osu.Game.Beatmaps.Formats @@ -159,7 +161,7 @@ namespace osu.Game.Beatmaps.Formats beatLength = -100 / difficultyPoint.SpeedMultiplier; // Apply the control point to a hit sample to uncover legacy properties (e.g. suffix) - HitSampleInfo tempHitSample = samplePoint.ApplyTo(new HitSampleInfo()); + HitSampleInfo tempHitSample = samplePoint.ApplyTo(new ConvertHitObjectParser.LegacyHitSampleInfo()); // Convert effect flags to the legacy format LegacyEffectFlags effectFlags = LegacyEffectFlags.None; @@ -172,7 +174,7 @@ namespace osu.Game.Beatmaps.Formats writer.Write(FormattableString.Invariant($"{beatLength},")); writer.Write(FormattableString.Invariant($"{(int)beatmap.ControlPointInfo.TimingPointAt(group.Time).TimeSignature},")); writer.Write(FormattableString.Invariant($"{(int)toLegacySampleBank(tempHitSample.Bank)},")); - writer.Write(FormattableString.Invariant($"{toLegacyCustomSampleBank(tempHitSample.Suffix)},")); + writer.Write(FormattableString.Invariant($"{toLegacyCustomSampleBank(tempHitSample)},")); writer.Write(FormattableString.Invariant($"{tempHitSample.Volume},")); writer.Write(FormattableString.Invariant($"{(timingPoint != null ? '1' : '0')},")); writer.Write(FormattableString.Invariant($"{(int)effectFlags}")); @@ -326,7 +328,7 @@ namespace osu.Game.Beatmaps.Formats if (!banksOnly) { - string customSampleBank = toLegacyCustomSampleBank(samples.FirstOrDefault(s => !string.IsNullOrEmpty(s.Name))?.Suffix); + string customSampleBank = toLegacyCustomSampleBank(samples.FirstOrDefault(s => !string.IsNullOrEmpty(s.Name))); string sampleFilename = samples.FirstOrDefault(s => string.IsNullOrEmpty(s.Name))?.LookupNames.First() ?? string.Empty; int volume = samples.FirstOrDefault()?.Volume ?? 100; @@ -382,6 +384,15 @@ namespace osu.Game.Beatmaps.Formats } } - private string toLegacyCustomSampleBank(string sampleSuffix) => string.IsNullOrEmpty(sampleSuffix) ? "0" : sampleSuffix; + private string toLegacyCustomSampleBank(HitSampleInfo hitSampleInfo) + { + if (hitSampleInfo == null) + return "0"; + + if (hitSampleInfo is ConvertHitObjectParser.LegacyHitSampleInfo legacy) + return legacy.CustomSampleBank.ToString(CultureInfo.InvariantCulture); + + return "0"; + } } } From d8d85e5b08980c80a9f5f0a917dafe16a1ce71a1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Apr 2020 15:04:04 +0900 Subject: [PATCH 0354/1117] Don't output certain properties if they don't exist --- osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 7721d50227..3ba68c6086 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -60,7 +60,7 @@ namespace osu.Game.Beatmaps.Formats { writer.WriteLine("[General]"); - writer.WriteLine(FormattableString.Invariant($"AudioFilename: {Path.GetFileName(beatmap.Metadata.AudioFile)}")); + if (beatmap.Metadata.AudioFile != null) writer.WriteLine(FormattableString.Invariant($"AudioFilename: {Path.GetFileName(beatmap.Metadata.AudioFile)}")); writer.WriteLine(FormattableString.Invariant($"AudioLeadIn: {beatmap.BeatmapInfo.AudioLeadIn}")); writer.WriteLine(FormattableString.Invariant($"PreviewTime: {beatmap.Metadata.PreviewTime}")); // Todo: Not all countdown types are supported by lazer yet @@ -105,15 +105,15 @@ namespace osu.Game.Beatmaps.Formats writer.WriteLine("[Metadata]"); writer.WriteLine(FormattableString.Invariant($"Title: {beatmap.Metadata.Title}")); - writer.WriteLine(FormattableString.Invariant($"TitleUnicode: {beatmap.Metadata.TitleUnicode}")); + if (beatmap.Metadata.TitleUnicode != null) writer.WriteLine(FormattableString.Invariant($"TitleUnicode: {beatmap.Metadata.TitleUnicode}")); writer.WriteLine(FormattableString.Invariant($"Artist: {beatmap.Metadata.Artist}")); - writer.WriteLine(FormattableString.Invariant($"ArtistUnicode: {beatmap.Metadata.ArtistUnicode}")); + if (beatmap.Metadata.ArtistUnicode != null) writer.WriteLine(FormattableString.Invariant($"ArtistUnicode: {beatmap.Metadata.ArtistUnicode}")); writer.WriteLine(FormattableString.Invariant($"Creator: {beatmap.Metadata.AuthorString}")); writer.WriteLine(FormattableString.Invariant($"Version: {beatmap.BeatmapInfo.Version}")); - writer.WriteLine(FormattableString.Invariant($"Source: {beatmap.Metadata.Source}")); - writer.WriteLine(FormattableString.Invariant($"Tags: {beatmap.Metadata.Tags}")); - writer.WriteLine(FormattableString.Invariant($"BeatmapID: {beatmap.BeatmapInfo.OnlineBeatmapID ?? 0}")); - writer.WriteLine(FormattableString.Invariant($"BeatmapSetID: {beatmap.BeatmapInfo.BeatmapSet?.OnlineBeatmapSetID ?? -1}")); + if (beatmap.Metadata.Source != null) writer.WriteLine(FormattableString.Invariant($"Source: {beatmap.Metadata.Source}")); + if (beatmap.Metadata.Tags != null) writer.WriteLine(FormattableString.Invariant($"Tags: {beatmap.Metadata.Tags}")); + if (beatmap.BeatmapInfo.OnlineBeatmapID != null) writer.WriteLine(FormattableString.Invariant($"BeatmapID: {beatmap.BeatmapInfo.OnlineBeatmapID}")); + if (beatmap.BeatmapInfo.BeatmapSet?.OnlineBeatmapSetID != null) writer.WriteLine(FormattableString.Invariant($"BeatmapSetID: {beatmap.BeatmapInfo.BeatmapSet.OnlineBeatmapSetID}")); } private void handleDifficulty(TextWriter writer) From 1421e876b11479cdb4d7d313dc84124c9f3c254d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Apr 2020 15:04:43 +0900 Subject: [PATCH 0355/1117] Remove implicit new combo from spinners --- osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 3ba68c6086..f1f0a0a5de 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -242,7 +242,7 @@ namespace osu.Game.Beatmaps.Formats break; case IHasEndTime _: - type |= LegacyHitObjectType.Spinner | LegacyHitObjectType.NewCombo; + type |= LegacyHitObjectType.Spinner; break; default: From 516e6a4bb10586bfa3505bf776f7ab263d6a8ddd Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Apr 2020 15:05:24 +0900 Subject: [PATCH 0356/1117] Fix overlapping control points not written correctly --- .../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 40 +++++++++++-------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index f1f0a0a5de..44ccbb350d 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -50,7 +50,7 @@ namespace osu.Game.Beatmaps.Formats handleEvents(writer); writer.WriteLine(); - handleTimingPoints(writer); + handleControlPoints(writer); writer.WriteLine(); handleHitObjects(writer); @@ -139,7 +139,7 @@ namespace osu.Game.Beatmaps.Formats writer.WriteLine(FormattableString.Invariant($"{(int)LegacyEventType.Break},{b.StartTime},{b.EndTime}")); } - private void handleTimingPoints(TextWriter writer) + private void handleControlPoints(TextWriter writer) { if (beatmap.ControlPointInfo.Groups.Count == 0) return; @@ -148,17 +148,27 @@ namespace osu.Game.Beatmaps.Formats foreach (var group in beatmap.ControlPointInfo.Groups) { - var timingPoint = group.ControlPoints.OfType().FirstOrDefault(); - var difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(group.Time); - var samplePoint = beatmap.ControlPointInfo.SamplePointAt(group.Time); - var effectPoint = beatmap.ControlPointInfo.EffectPointAt(group.Time); + var groupTimingPoint = group.ControlPoints.OfType().FirstOrDefault(); - // Convert beat length the legacy format - double beatLength; - if (timingPoint != null) - beatLength = timingPoint.BeatLength; - else - beatLength = -100 / difficultyPoint.SpeedMultiplier; + // If the group contains a timing control point, it needs to be output separately. + if (groupTimingPoint != null) + { + writer.Write(FormattableString.Invariant($"{groupTimingPoint.Time},")); + writer.Write(FormattableString.Invariant($"{groupTimingPoint.BeatLength},")); + outputControlPointEffectsAt(groupTimingPoint.Time, true); + } + + // Output any remaining effects as secondary non-timing control point. + var difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(group.Time); + writer.Write(FormattableString.Invariant($"{group.Time},")); + writer.Write(FormattableString.Invariant($"{-100 / difficultyPoint.SpeedMultiplier},")); + outputControlPointEffectsAt(group.Time, false); + } + + void outputControlPointEffectsAt(double time, bool isTimingPoint) + { + var samplePoint = beatmap.ControlPointInfo.SamplePointAt(time); + var effectPoint = beatmap.ControlPointInfo.EffectPointAt(time); // Apply the control point to a hit sample to uncover legacy properties (e.g. suffix) HitSampleInfo tempHitSample = samplePoint.ApplyTo(new ConvertHitObjectParser.LegacyHitSampleInfo()); @@ -170,13 +180,11 @@ namespace osu.Game.Beatmaps.Formats if (effectPoint.OmitFirstBarLine) effectFlags |= LegacyEffectFlags.OmitFirstBarLine; - writer.Write(FormattableString.Invariant($"{group.Time},")); - writer.Write(FormattableString.Invariant($"{beatLength},")); - writer.Write(FormattableString.Invariant($"{(int)beatmap.ControlPointInfo.TimingPointAt(group.Time).TimeSignature},")); + writer.Write(FormattableString.Invariant($"{(int)beatmap.ControlPointInfo.TimingPointAt(time).TimeSignature},")); writer.Write(FormattableString.Invariant($"{(int)toLegacySampleBank(tempHitSample.Bank)},")); writer.Write(FormattableString.Invariant($"{toLegacyCustomSampleBank(tempHitSample)},")); writer.Write(FormattableString.Invariant($"{tempHitSample.Volume},")); - writer.Write(FormattableString.Invariant($"{(timingPoint != null ? '1' : '0')},")); + writer.Write(FormattableString.Invariant($"{(isTimingPoint ? '1' : '0')},")); writer.Write(FormattableString.Invariant($"{(int)effectFlags}")); writer.WriteLine(); } From d27ca725f946d7a85da2c37bbb9ee53bd5efb95e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 22 Apr 2020 08:51:21 +0900 Subject: [PATCH 0357/1117] Use IEnumerable instead --- osu.Game/IO/Serialization/Converters/TypedListConverter.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs index 837650eb0a..64f1ebeb1a 100644 --- a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs +++ b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections; using System.Collections.Generic; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -62,7 +61,7 @@ namespace osu.Game.IO.Serialization.Converters public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { - var list = (IList)value; + var list = (IEnumerable)value; var lookupTable = new List(); var objects = new List(); From ee278a2e1bd3e460a7092e2348c84f0d305f8cb3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Apr 2020 15:55:36 +0900 Subject: [PATCH 0358/1117] Add taiko/catch/mania sample beatmaps --- .../Resources/sample-beatmap-catch.osu | 30 +++++++++++++ .../Resources/sample-beatmap-mania.osu | 39 +++++++++++++++++ .../Resources/sample-beatmap-taiko.osu | 42 +++++++++++++++++++ 3 files changed, 111 insertions(+) create mode 100644 osu.Game.Tests/Resources/sample-beatmap-catch.osu create mode 100644 osu.Game.Tests/Resources/sample-beatmap-mania.osu create mode 100644 osu.Game.Tests/Resources/sample-beatmap-taiko.osu diff --git a/osu.Game.Tests/Resources/sample-beatmap-catch.osu b/osu.Game.Tests/Resources/sample-beatmap-catch.osu new file mode 100644 index 0000000000..09ef762e3e --- /dev/null +++ b/osu.Game.Tests/Resources/sample-beatmap-catch.osu @@ -0,0 +1,30 @@ +osu file format v14 + +[General] +SampleSet: Normal +StackLeniency: 0.7 +Mode: 2 + +[Difficulty] +HPDrainRate:3 +CircleSize:5 +OverallDifficulty:8 +ApproachRate:8 +SliderMultiplier:3.59999990463257 +SliderTickRate:2 + +[TimingPoints] +24,352.941176470588,4,1,1,100,1,0 +6376,-50,4,1,1,100,0,0 + +[HitObjects] +32,183,24,5,0,0:0:0:0: +106,123,200,1,10,0:0:0:0: +199,108,376,1,2,0:0:0:0: +305,105,553,5,4,0:0:0:0: +386,112,729,1,14,0:0:0:0: +486,197,906,5,12,0:0:0:0: +14,199,1082,2,0,L|473:198,1,449.999988079071 +14,199,1700,6,6,P|248:33|490:222,1,629.9999833107,0|8,0:0|0:0,0:0:0:0: +10,190,2494,2,8,B|252:29|254:335|468:167,1,449.999988079071,10|12,0:0|0:0,0:0:0:0: +256,192,3112,12,0,3906,0:0:0:0: \ No newline at end of file diff --git a/osu.Game.Tests/Resources/sample-beatmap-mania.osu b/osu.Game.Tests/Resources/sample-beatmap-mania.osu new file mode 100644 index 0000000000..04d6a31ab6 --- /dev/null +++ b/osu.Game.Tests/Resources/sample-beatmap-mania.osu @@ -0,0 +1,39 @@ +osu file format v14 + +[General] +SampleSet: Normal +StackLeniency: 0.7 +Mode: 3 + +[Difficulty] +HPDrainRate:3 +CircleSize:5 +OverallDifficulty:8 +ApproachRate:8 +SliderMultiplier:3.59999990463257 +SliderTickRate:2 + +[TimingPoints] +24,352.941176470588,4,1,1,100,1,0 +6376,-50,4,1,1,100,0,0 + +[HitObjects] +51,192,24,1,0,0:0:0:0: +153,192,200,1,0,0:0:0:0: +358,192,376,1,0,0:0:0:0: +460,192,553,1,0,0:0:0:0: +460,192,729,128,0,1435:0:0:0:0: +358,192,906,128,0,1612:0:0:0:0: +256,192,1082,128,0,1788:0:0:0:0: +153,192,1259,128,0,1965:0:0:0:0: +51,192,1435,128,0,2141:0:0:0:0: +51,192,2318,1,12,0:0:0:0: +153,192,2318,1,4,0:0:0:0: +256,192,2318,1,6,0:0:0:0: +358,192,2318,1,14,0:0:0:0: +460,192,2318,1,0,0:0:0:0: +51,192,2494,128,0,2582:0:0:0:0: +153,192,2494,128,14,2582:0:0:0:0: +256,192,2494,128,6,2582:0:0:0:0: +358,192,2494,128,4,2582:0:0:0:0: +460,192,2494,128,12,2582:0:0:0:0: \ No newline at end of file diff --git a/osu.Game.Tests/Resources/sample-beatmap-taiko.osu b/osu.Game.Tests/Resources/sample-beatmap-taiko.osu new file mode 100644 index 0000000000..94b4288336 --- /dev/null +++ b/osu.Game.Tests/Resources/sample-beatmap-taiko.osu @@ -0,0 +1,42 @@ +osu file format v14 + +[General] +SampleSet: Normal +StackLeniency: 0.7 +Mode: 1 + +[Difficulty] +HPDrainRate:3 +CircleSize:5 +OverallDifficulty:8 +ApproachRate:8 +SliderMultiplier:3.59999990463257 +SliderTickRate:2 + +[TimingPoints] +24,352.941176470588,4,1,1,100,1,0 +6376,-50,4,1,1,100,0,0 + +[HitObjects] +231,129,24,1,0,0:0:0:0: +231,129,200,1,0,0:0:0:0: +231,129,376,1,0,0:0:0:0: +231,129,553,1,0,0:0:0:0: +231,129,729,1,0,0:0:0:0: +373,132,906,1,4,0:0:0:0: +373,132,1082,1,4,0:0:0:0: +373,132,1259,1,4,0:0:0:0: +373,132,1435,1,4,0:0:0:0: +231,129,1788,1,8,0:0:0:0: +231,129,1964,1,8,0:0:0:0: +231,129,2140,1,8,0:0:0:0: +231,129,2317,1,8,0:0:0:0: +231,129,2493,1,8,0:0:0:0: +373,132,2670,1,12,0:0:0:0: +373,132,2846,1,12,0:0:0:0: +373,132,3023,1,12,0:0:0:0: +373,132,3199,1,12,0:0:0:0: +51,189,3553,2,0,L|150:188,1,89.9999976158143 +52,191,3906,2,0,L|512:189,1,449.999988079071 +26,196,4612,2,4,L|501:195,1,449.999988079071 +17,242,5318,2,10,P|250:69|495:243,1,629.9999833107,0|8,0:0|0:0,0:0:0:0: \ No newline at end of file From ea0ebc8527035e4884acebd77e5cad73de3200e3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Apr 2020 16:04:58 +0900 Subject: [PATCH 0359/1117] Implement beatmap encoding for all legacy rulesets --- .../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 53 ++++++++++++------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 44ccbb350d..af0adb65a5 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -13,6 +13,7 @@ using osu.Game.Beatmaps.Legacy; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Legacy; using osu.Game.Rulesets.Objects.Types; +using osuTK; namespace osu.Game.Beatmaps.Formats { @@ -197,32 +198,40 @@ namespace osu.Game.Beatmaps.Formats writer.WriteLine("[HitObjects]"); - // TODO: implement other legacy rulesets + foreach (var h in beatmap.HitObjects) + handleHitObject(writer, h); + } + + private void handleHitObject(TextWriter writer, HitObject hitObject) + { + Vector2 position = new Vector2(256, 192); + switch (beatmap.BeatmapInfo.RulesetID) { case 0: - foreach (var h in beatmap.HitObjects) - handleOsuHitObject(writer, h); + position = ((IHasPosition)hitObject).Position; + break; + + case 2: + position.X = ((IHasXPosition)hitObject).X * 512; + break; + + case 3: + int totalColumns = (int)Math.Max(1, beatmap.BeatmapInfo.BaseDifficulty.CircleSize); + position.X = (int)Math.Ceiling(((IHasXPosition)hitObject).X * (512f / totalColumns)); break; } - } - private void handleOsuHitObject(TextWriter writer, HitObject hitObject) - { - var positionData = (IHasPosition)hitObject; - - writer.Write(FormattableString.Invariant($"{positionData.X},")); - writer.Write(FormattableString.Invariant($"{positionData.Y},")); + writer.Write(FormattableString.Invariant($"{position.X},")); + writer.Write(FormattableString.Invariant($"{position.Y},")); writer.Write(FormattableString.Invariant($"{hitObject.StartTime},")); writer.Write(FormattableString.Invariant($"{(int)getObjectType(hitObject)},")); - writer.Write(hitObject is IHasCurve - ? FormattableString.Invariant($"0,") - : FormattableString.Invariant($"{(int)toLegacyHitSoundType(hitObject.Samples)},")); + writer.Write(FormattableString.Invariant($"{(int)toLegacyHitSoundType(hitObject.Samples)},")); if (hitObject is IHasCurve curveData) { - addCurveData(writer, curveData, positionData); + addCurveData(writer, curveData, position); writer.Write(getSampleBank(hitObject.Samples, zeroBanks: true)); } else @@ -237,11 +246,15 @@ namespace osu.Game.Beatmaps.Formats private static LegacyHitObjectType getObjectType(HitObject hitObject) { - var comboData = (IHasCombo)hitObject; + LegacyHitObjectType type = 0; - var type = (LegacyHitObjectType)(comboData.ComboOffset << 4); + if (hitObject is IHasCombo combo) + { + type = (LegacyHitObjectType)(combo.ComboOffset << 4); - if (comboData.NewCombo) type |= LegacyHitObjectType.NewCombo; + if (combo.NewCombo) + type |= LegacyHitObjectType.NewCombo; + } switch (hitObject) { @@ -261,7 +274,7 @@ namespace osu.Game.Beatmaps.Formats return type; } - private void addCurveData(TextWriter writer, IHasCurve curveData, IHasPosition positionData) + private void addCurveData(TextWriter writer, IHasCurve curveData, Vector2 position) { PathType? lastType = null; @@ -297,13 +310,13 @@ namespace osu.Game.Beatmaps.Formats else { // New segment with the same type - duplicate the control point - writer.Write(FormattableString.Invariant($"{positionData.X + point.Position.Value.X}:{positionData.Y + point.Position.Value.Y}|")); + writer.Write(FormattableString.Invariant($"{position.X + point.Position.Value.X}:{position.Y + point.Position.Value.Y}|")); } } if (i != 0) { - writer.Write(FormattableString.Invariant($"{positionData.X + point.Position.Value.X}:{positionData.Y + point.Position.Value.Y}")); + writer.Write(FormattableString.Invariant($"{position.X + point.Position.Value.X}:{position.Y + point.Position.Value.Y}")); writer.Write(i != curveData.Path.ControlPoints.Count - 1 ? "|" : ","); } } From d957614fc96fe142dccaeec1030de08e3f4c7453 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Apr 2020 16:33:19 +0900 Subject: [PATCH 0360/1117] Cleanup handling of mania samples --- .../Beatmaps/ManiaBeatmapConverter.cs | 21 +-------- .../Legacy/DistanceObjectPatternGenerator.cs | 10 ++-- .../Legacy/EndTimeObjectPatternGenerator.cs | 15 ++---- .../Objects/Drawables/DrawableHoldNote.cs | 5 ++ osu.Game.Rulesets.Mania/Objects/HoldNote.cs | 46 +++++++++++++++---- 5 files changed, 52 insertions(+), 45 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index 4187e39b43..b803caa1b7 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -238,9 +238,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps { StartTime = HitObject.StartTime, Duration = endTimeData.Duration, - Column = column, - Head = { Samples = sampleInfoListAt(HitObject.StartTime) }, - Tail = { Samples = sampleInfoListAt(endTimeData.EndTime) }, + Samples = HitObject.Samples, + NodeSamples = (HitObject as IHasRepeats)?.NodeSamples }); } else if (HitObject is IHasXPosition) @@ -255,22 +254,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps return pattern; } - - /// - /// Retrieves the sample info list at a point in time. - /// - /// The time to retrieve the sample info list from. - /// - private IList sampleInfoListAt(double time) - { - if (!(HitObject is IHasCurve curveData)) - return HitObject.Samples; - - double segmentTime = (curveData.EndTime - HitObject.StartTime) / curveData.SpanCount(); - - int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime); - return curveData.NodeSamples[index]; - } } } } diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index 315ef96e49..d8d5b67c0e 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -505,16 +505,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy } else { - var holdNote = new HoldNote + newObject = new HoldNote { StartTime = startTime, - Column = column, Duration = endTime - startTime, - Head = { Samples = sampleInfoListAt(startTime) }, - Tail = { Samples = sampleInfoListAt(endTime) } + Column = column, + Samples = HitObject.Samples, + NodeSamples = (HitObject as IHasRepeats)?.NodeSamples }; - - newObject = holdNote; } pattern.Add(newObject); diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs index b3be08e1f7..907bed0d65 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs @@ -64,21 +64,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy if (holdNote) { - var hold = new HoldNote + newObject = new HoldNote { StartTime = HitObject.StartTime, + Duration = endTime - HitObject.StartTime, Column = column, - Duration = endTime - HitObject.StartTime + Samples = HitObject.Samples, + NodeSamples = (HitObject as IHasRepeats)?.NodeSamples }; - - if (hold.Head.Samples == null) - hold.Head.Samples = new List(); - - hold.Head.Samples.Add(new HitSampleInfo { Name = HitSampleInfo.HIT_NORMAL }); - - hold.Tail.Samples = HitObject.Samples; - - newObject = hold; } else { diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index a9ef661aaa..bc3a136a6a 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs @@ -127,6 +127,11 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables bodyPiece.Anchor = bodyPiece.Origin = e.NewValue == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft; } + public override void PlaySamples() + { + // The hold note does not play samples itself. + } + protected override void Update() { base.Update(); diff --git a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs index 049bf55f90..04f8fd8c99 100644 --- a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/HoldNote.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.Collections.Generic; +using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Judgements; @@ -28,7 +30,9 @@ namespace osu.Game.Rulesets.Mania.Objects set { duration = value; - Tail.StartTime = EndTime; + + if (Tail != null) + Tail.StartTime = EndTime; } } @@ -38,8 +42,12 @@ namespace osu.Game.Rulesets.Mania.Objects set { base.StartTime = value; - Head.StartTime = value; - Tail.StartTime = EndTime; + + if (Head != null) + Head.StartTime = value; + + if (Tail != null) + Tail.StartTime = EndTime; } } @@ -49,20 +57,26 @@ namespace osu.Game.Rulesets.Mania.Objects set { base.Column = value; - Head.Column = value; - Tail.Column = value; + + if (Head != null) + Head.Column = value; + + if (Tail != null) + Tail.Column = value; } } + public List> NodeSamples { get; set; } + /// /// The head note of the hold. /// - public readonly Note Head = new Note(); + public Note Head { get; private set; } /// /// The tail note of the hold. /// - public readonly TailNote Tail = new TailNote(); + public TailNote Tail { get; private set; } /// /// The time between ticks of this hold. @@ -83,8 +97,19 @@ namespace osu.Game.Rulesets.Mania.Objects createTicks(); - AddNested(Head); - AddNested(Tail); + AddNested(Head = new Note + { + StartTime = StartTime, + Column = Column, + Samples = getNodeSamples(0), + }); + + AddNested(Tail = new TailNote + { + StartTime = EndTime, + Column = Column, + Samples = getNodeSamples(1), + }); } private void createTicks() @@ -105,5 +130,8 @@ namespace osu.Game.Rulesets.Mania.Objects public override Judgement CreateJudgement() => new IgnoreJudgement(); protected override HitWindows CreateHitWindows() => HitWindows.Empty; + + private IList getNodeSamples(int nodeIndex) => + nodeIndex < NodeSamples?.Count ? NodeSamples[nodeIndex] : Samples; } } From cc0c82aaebac82c2d3a7eb1844eac44dab47da31 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Apr 2020 16:44:26 +0900 Subject: [PATCH 0361/1117] Implement IHasXPosition on ManiaHitObject --- .../Beatmaps/ManiaBeatmapConverter.cs | 3 +-- osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs | 9 ++++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index b803caa1b7..cb583ac1a9 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -13,7 +13,6 @@ using osu.Game.Rulesets.Mania.Beatmaps.Patterns; using osu.Game.Rulesets.Mania.MathUtils; using osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy; using osuTK; -using osu.Game.Audio; namespace osu.Game.Rulesets.Mania.Beatmaps { @@ -67,7 +66,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps } } - public override bool CanConvert() => Beatmap.HitObjects.All(h => h is IHasXPosition || h is ManiaHitObject); + public override bool CanConvert() => Beatmap.HitObjects.All(h => h is IHasXPosition); protected override Beatmap ConvertBeatmap(IBeatmap original) { diff --git a/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs index 995e1516cb..27bf50493d 100644 --- a/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs +++ b/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs @@ -5,11 +5,12 @@ using osu.Framework.Bindables; using osu.Game.Rulesets.Mania.Objects.Types; using osu.Game.Rulesets.Mania.Scoring; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Mania.Objects { - public abstract class ManiaHitObject : HitObject, IHasColumn + public abstract class ManiaHitObject : HitObject, IHasColumn, IHasXPosition { public readonly Bindable ColumnBindable = new Bindable(); @@ -20,5 +21,11 @@ namespace osu.Game.Rulesets.Mania.Objects } protected override HitWindows CreateHitWindows() => new ManiaHitWindows(); + + #region LegacyBeatmapEncoder + + float IHasXPosition.X => Column; + + #endregion } } From d8fdd73e170e8117961a3da8d1ef31e85738c9c2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Apr 2020 16:45:01 +0900 Subject: [PATCH 0362/1117] Implement IHasCurve on DrumRoll --- .../Beatmaps/TaikoBeatmapConverter.cs | 6 ++-- osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs | 29 ++++++++++++++++++- .../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 8 +++-- 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index 695ada3a00..caf645d5a2 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps /// 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. /// - private const float legacy_velocity_multiplier = 1.4f; + public const float LEGACY_VELOCITY_MULTIPLIER = 1.4f; /// /// Because swells are easier in taiko than spinners are in osu!, @@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps // Rewrite the beatmap info to add the slider velocity multiplier original.BeatmapInfo = original.BeatmapInfo.Clone(); original.BeatmapInfo.BaseDifficulty = original.BeatmapInfo.BaseDifficulty.Clone(); - original.BeatmapInfo.BaseDifficulty.SliderMultiplier *= legacy_velocity_multiplier; + original.BeatmapInfo.BaseDifficulty.SliderMultiplier *= LEGACY_VELOCITY_MULTIPLIER; Beatmap converted = base.ConvertBeatmap(original); @@ -92,7 +92,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps double speedAdjustedBeatLength = timingPoint.BeatLength / speedAdjustment; // The true distance, accounting for any repeats. This ends up being the drum roll distance later - double distance = distanceData.Distance * spans * legacy_velocity_multiplier; + double distance = distanceData.Distance * spans * LEGACY_VELOCITY_MULTIPLIER; // The velocity of the taiko hit object - calculated as the velocity of a drum roll double taikoVelocity = taiko_base_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier / speedAdjustedBeatLength; diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs index aacd78f176..ad0cd2f2a8 100644 --- a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs @@ -3,15 +3,20 @@ using osu.Game.Rulesets.Objects.Types; using System; +using System.Collections.Generic; +using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.Taiko.Beatmaps; using osu.Game.Rulesets.Taiko.Judgements; +using osuTK; namespace osu.Game.Rulesets.Taiko.Objects { - public class DrumRoll : TaikoHitObject, IHasEndTime + public class DrumRoll : TaikoHitObject, IHasEndTime, IHasCurve { /// /// Drum roll distance that results in a duration of 1 speed-adjusted beat length. @@ -26,6 +31,11 @@ namespace osu.Game.Rulesets.Taiko.Objects public double Duration { get; set; } + /// + /// Velocity of this . + /// + public double Velocity { get; private set; } + /// /// Numer of ticks per beat length. /// @@ -54,6 +64,10 @@ namespace osu.Game.Rulesets.Taiko.Objects base.ApplyDefaultsToSelf(controlPointInfo, difficulty); TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime); + DifficultyControlPoint difficultyPoint = controlPointInfo.DifficultyPointAt(StartTime); + + double scoringDistance = base_distance * difficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier; + Velocity = scoringDistance / timingPoint.BeatLength; tickSpacing = timingPoint.BeatLength / TickRate; overallDifficulty = difficulty.OverallDifficulty; @@ -93,5 +107,18 @@ namespace osu.Game.Rulesets.Taiko.Objects public override Judgement CreateJudgement() => new TaikoDrumRollJudgement(); protected override HitWindows CreateHitWindows() => HitWindows.Empty; + + #region LegacyBeatmapEncoder + + double IHasDistance.Distance => Duration * Velocity; + + int IHasRepeats.RepeatCount { get => 0; set { } } + + List> IHasRepeats.NodeSamples => new List>(); + + SliderPath IHasCurve.Path + => new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(1) }, ((IHasDistance)this).Distance / TaikoBeatmapConverter.LEGACY_VELOCITY_MULTIPLIER); + + #endregion } } diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index af0adb65a5..4b760f1983 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -125,7 +125,12 @@ namespace osu.Game.Beatmaps.Formats writer.WriteLine(FormattableString.Invariant($"CircleSize: {beatmap.BeatmapInfo.BaseDifficulty.CircleSize}")); writer.WriteLine(FormattableString.Invariant($"OverallDifficulty: {beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty}")); writer.WriteLine(FormattableString.Invariant($"ApproachRate: {beatmap.BeatmapInfo.BaseDifficulty.ApproachRate}")); - writer.WriteLine(FormattableString.Invariant($"SliderMultiplier: {beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier}")); + + // Taiko adjusts the slider multiplier (see: TaikoBeatmapConverter.LEGACY_VELOCITY_MULTIPLIER) + writer.WriteLine(beatmap.BeatmapInfo.RulesetID == 1 + ? FormattableString.Invariant($"SliderMultiplier: {beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier / 1.4f}") + : FormattableString.Invariant($"SliderMultiplier: {beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier}")); + writer.WriteLine(FormattableString.Invariant($"SliderTickRate: {beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate}")); } @@ -226,7 +231,6 @@ namespace osu.Game.Beatmaps.Formats writer.Write(FormattableString.Invariant($"{position.Y},")); writer.Write(FormattableString.Invariant($"{hitObject.StartTime},")); writer.Write(FormattableString.Invariant($"{(int)getObjectType(hitObject)},")); - writer.Write(FormattableString.Invariant($"{(int)toLegacyHitSoundType(hitObject.Samples)},")); if (hitObject is IHasCurve curveData) From 1f962f5c563d80f40208aa8f5e8fa0d1b54389d9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Apr 2020 17:20:37 +0900 Subject: [PATCH 0363/1117] Reword comment --- osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index bc3a136a6a..770dd73e7a 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs @@ -129,7 +129,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables public override void PlaySamples() { - // The hold note does not play samples itself. + // Samples are played by the head/tail notes. } protected override void Update() From 6da0872ae55d9b07d2d1fe55a4db02455b18a9a6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Apr 2020 17:29:23 +0900 Subject: [PATCH 0364/1117] Use the last node sample for the tail note --- osu.Game.Rulesets.Mania/Objects/HoldNote.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs index 04f8fd8c99..eea2c31260 100644 --- a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs @@ -108,7 +108,7 @@ namespace osu.Game.Rulesets.Mania.Objects { StartTime = EndTime, Column = Column, - Samples = getNodeSamples(1), + Samples = getNodeSamples((NodeSamples?.Count - 1) ?? 1), }); } From ba12e23d9eed0b48effa40f7734c1485adfa91cd Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Apr 2020 17:31:49 +0900 Subject: [PATCH 0365/1117] Fix inspection --- osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs index ad0cd2f2a8..dc2f277e58 100644 --- a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs @@ -16,7 +16,7 @@ using osuTK; namespace osu.Game.Rulesets.Taiko.Objects { - public class DrumRoll : TaikoHitObject, IHasEndTime, IHasCurve + public class DrumRoll : TaikoHitObject, IHasCurve { /// /// Drum roll distance that results in a duration of 1 speed-adjusted beat length. From 0c74f1aaa91c70e7984bbe9c93b68509d6b09d38 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 Apr 2020 09:08:33 +0900 Subject: [PATCH 0366/1117] Fix now playing output showing empty brackets when no difficulty specified --- osu.Game/Beatmaps/BeatmapInfo.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 68d113ce40..90c100db05 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -149,7 +149,12 @@ namespace osu.Game.Beatmaps } } - public override string ToString() => $"{Metadata} [{Version}]".Trim(); + public override string ToString() + { + string version = string.IsNullOrEmpty(Version) ? string.Empty : $"[{Version}]"; + + return $"{Metadata} {version}".Trim(); + } public bool Equals(BeatmapInfo other) { From 360c9f8e387bffbb2d4d19d07465d412d0d94e75 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 Apr 2020 09:19:34 +0900 Subject: [PATCH 0367/1117] Add test coverage and handle null creator --- .../Beatmaps/ToStringFormattingTest.cs | 61 +++++++++++++++++++ osu.Game/Beatmaps/BeatmapMetadata.cs | 6 +- 2 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Tests/Beatmaps/ToStringFormattingTest.cs diff --git a/osu.Game.Tests/Beatmaps/ToStringFormattingTest.cs b/osu.Game.Tests/Beatmaps/ToStringFormattingTest.cs new file mode 100644 index 0000000000..c477bbd9cf --- /dev/null +++ b/osu.Game.Tests/Beatmaps/ToStringFormattingTest.cs @@ -0,0 +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 NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Users; + +namespace osu.Game.Tests.Beatmaps +{ + [TestFixture] + public class ToStringFormattingTest + { + [Test] + public void TestArtistTitle() + { + var beatmap = new BeatmapInfo + { + Metadata = new BeatmapMetadata + { + Artist = "artist", + Title = "title" + } + }; + + Assert.That(beatmap.ToString(), Is.EqualTo("artist - title")); + } + + [Test] + public void TestArtistTitleCreator() + { + var beatmap = new BeatmapInfo + { + Metadata = new BeatmapMetadata + { + Artist = "artist", + Title = "title", + Author = new User { Username = "creator" } + } + }; + + Assert.That(beatmap.ToString(), Is.EqualTo("artist - title (creator)")); + } + + [Test] + public void TestArtistTitleCreatorDifficulty() + { + var beatmap = new BeatmapInfo + { + Metadata = new BeatmapMetadata + { + Artist = "artist", + Title = "title", + Author = new User { Username = "creator" } + }, + Version = "difficulty" + }; + + Assert.That(beatmap.ToString(), Is.EqualTo("artist - title (creator) [difficulty]")); + } + } +} diff --git a/osu.Game/Beatmaps/BeatmapMetadata.cs b/osu.Game/Beatmaps/BeatmapMetadata.cs index 001f319307..775d78f1fb 100644 --- a/osu.Game/Beatmaps/BeatmapMetadata.cs +++ b/osu.Game/Beatmaps/BeatmapMetadata.cs @@ -53,7 +53,11 @@ namespace osu.Game.Beatmaps public string AudioFile { get; set; } public string BackgroundFile { get; set; } - public override string ToString() => $"{Artist} - {Title} ({Author})"; + public override string ToString() + { + string author = Author == null ? string.Empty : $"({Author})"; + return $"{Artist} - {Title} {author}".Trim(); + } [JsonIgnore] public string[] SearchableTerms => new[] From 95de2c6f7f9d82a6c8c72e94280d8b7257766ad7 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 22 Apr 2020 05:04:07 +0300 Subject: [PATCH 0368/1117] Mark Catcher.additiveTarget to never be null And provide empty containers instead. --- osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs | 3 ++- .../Difficulty/CatchDifficultyCalculator.cs | 3 ++- osu.Game.Rulesets.Catch/UI/Catcher.cs | 3 ++- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs index acc5f4e428..3a3e664690 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; namespace osu.Game.Rulesets.Catch.Tests { @@ -23,7 +24,7 @@ namespace osu.Game.Rulesets.Catch.Tests [BackgroundDependencyLoader] private void load() { - SetContents(() => new Catcher + SetContents(() => new Catcher(new Container()) { RelativePositionAxes = Axes.None, Anchor = Anchor.Centre, diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 4d9dbbbc5f..fbdf437b7b 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.Difficulty.Preprocessing; using osu.Game.Rulesets.Catch.Difficulty.Skills; @@ -71,7 +72,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty protected override Skill[] CreateSkills(IBeatmap beatmap) { - using (var catcher = new Catcher(beatmap.BeatmapInfo.BaseDifficulty)) + using (var catcher = new Catcher(new Container(), beatmap.BeatmapInfo.BaseDifficulty)) halfCatcherWidth = catcher.CatchWidth * 0.5f; return new Skill[] diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 7ecb245617..029de2cac0 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -3,6 +3,7 @@ using System; using System.Linq; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Animations; @@ -116,7 +117,7 @@ namespace osu.Game.Rulesets.Catch.UI private int hyperDashDirection; private float hyperDashTargetPosition; - public Catcher(BeatmapDifficulty difficulty = null, Container additiveTarget = null) + public Catcher([NotNull] Container additiveTarget, BeatmapDifficulty difficulty = null) { this.additiveTarget = additiveTarget; diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 1dd94afa9e..37d177b936 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Catch.UI { RelativeSizeAxes = Axes.X; Height = CATCHER_SIZE; - Child = MovableCatcher = new Catcher(difficulty, this); + Child = MovableCatcher = new Catcher(this, difficulty); } public static float GetCatcherSize(BeatmapDifficulty difficulty) From 9ab0f6d8bc5a160f9859305a4a70e3d889f644ec Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 22 Apr 2020 05:12:29 +0300 Subject: [PATCH 0369/1117] Separate trail-related logic to its own container --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 120 ++++----------- .../UI/CatcherTrailDisplay.cs | 137 ++++++++++++++++++ 2 files changed, 168 insertions(+), 89 deletions(-) create mode 100644 osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 029de2cac0..e30988d8f7 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -8,7 +8,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Animations; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; using osu.Framework.Utils; using osu.Game.Beatmaps; @@ -37,6 +36,11 @@ namespace osu.Game.Rulesets.Catch.UI /// public static readonly Color4 DEFAULT_CATCHER_HYPER_DASH_COLOUR = Color4.OrangeRed; + /// + /// The duration between transitioning to hyper-dash state. + /// + public const double HYPER_DASH_TRANSITION_DURATION = 180; + /// /// Whether we are hyper-dashing or not. /// @@ -49,10 +53,10 @@ namespace osu.Game.Rulesets.Catch.UI public Container ExplodingFruitTarget; - private readonly Container additiveTarget; - private Container dashTrails; - private Container hyperDashTrails; - private Container endGlowSprites; + [NotNull] + private readonly Container trailsTarget; + + private CatcherTrailDisplay trails; public CatcherAnimationState CurrentState { get; private set; } @@ -66,33 +70,23 @@ namespace osu.Game.Rulesets.Catch.UI /// internal float CatchWidth => CatcherArea.CATCHER_SIZE * Math.Abs(Scale.X) * allowed_catch_range; - protected bool Dashing + /// + /// The drawable catcher for . + /// + internal Drawable CurrentDrawableCatcher => currentCatcher.Drawable; + + private bool dashing; + + public bool Dashing { get => dashing; - set + protected set { if (value == dashing) return; dashing = value; - Trail |= dashing; - } - } - - /// - /// Activate or deactivate the trail. Will be automatically deactivated when conditions to keep the trail displayed are no longer met. - /// - protected bool Trail - { - get => trail; - set - { - if (value == trail || additiveTarget == null) return; - - trail = value; - - if (Trail) - beginTrail(); + trails.DisplayTrail |= dashing; } } @@ -109,17 +103,13 @@ namespace osu.Game.Rulesets.Catch.UI private int currentDirection; - private bool dashing; - - private bool trail; - private double hyperDashModifier = 1; private int hyperDashDirection; private float hyperDashTargetPosition; - public Catcher([NotNull] Container additiveTarget, BeatmapDifficulty difficulty = null) + public Catcher([NotNull] Container trailsTarget, BeatmapDifficulty difficulty = null) { - this.additiveTarget = additiveTarget; + this.trailsTarget = trailsTarget; RelativePositionAxes = Axes.X; X = 0.5f; @@ -158,12 +148,7 @@ namespace osu.Game.Rulesets.Catch.UI } }; - additiveTarget?.AddRange(new[] - { - dashTrails = new Container { RelativeSizeAxes = Axes.Both }, - hyperDashTrails = new Container { RelativeSizeAxes = Axes.Both, Colour = hyperDashColour }, - endGlowSprites = new Container { RelativeSizeAxes = Axes.Both, Colour = hyperDashEndGlowColour } - }); + trailsTarget.Add(trails = new CatcherTrailDisplay(this)); updateCatcher(); } @@ -257,7 +242,7 @@ namespace osu.Game.Rulesets.Catch.UI if (wasHyperDashing) { updateCatcherColour(false); - Trail &= Dashing; + trails.DisplayTrail &= Dashing; } } else @@ -269,21 +254,15 @@ namespace osu.Game.Rulesets.Catch.UI if (!wasHyperDashing) { updateCatcherColour(true); - Trail = true; - var hyperDashEndGlow = createAdditiveSprite(endGlowSprites); - hyperDashEndGlow.MoveToOffset(new Vector2(0, -10), 1200, Easing.In); - hyperDashEndGlow.ScaleTo(hyperDashEndGlow.Scale * 0.95f).ScaleTo(hyperDashEndGlow.Scale * 1.2f, 1200, Easing.In); - hyperDashEndGlow.FadeOut(1200); - hyperDashEndGlow.Expire(true); + trails.DisplayTrail = true; + trails.DisplayEndGlow(); } } } private void updateCatcherColour(bool hyperDashing) { - const float hyper_dash_transition_length = 180; - if (hyperDashing) { // special behaviour for catcher colour if no skin overrides. @@ -292,17 +271,17 @@ namespace osu.Game.Rulesets.Catch.UI ? DEFAULT_CATCHER_HYPER_DASH_COLOUR : hyperDashColour; - this.FadeColour(catcherColour, hyper_dash_transition_length, Easing.OutQuint); - this.FadeTo(0.2f, hyper_dash_transition_length, Easing.OutQuint); + this.FadeColour(catcherColour, HYPER_DASH_TRANSITION_DURATION, Easing.OutQuint); + this.FadeTo(0.2f, HYPER_DASH_TRANSITION_DURATION, Easing.OutQuint); } else { - this.FadeColour(Color4.White, hyper_dash_transition_length, Easing.OutQuint); - this.FadeTo(1f, hyper_dash_transition_length, Easing.OutQuint); + this.FadeColour(Color4.White, HYPER_DASH_TRANSITION_DURATION, Easing.OutQuint); + this.FadeTo(1f, HYPER_DASH_TRANSITION_DURATION, Easing.OutQuint); } - hyperDashTrails?.FadeColour(hyperDashColour, hyper_dash_transition_length, Easing.OutQuint); - endGlowSprites?.FadeColour(hyperDashEndGlowColour, hyper_dash_transition_length, Easing.OutQuint); + trails.HyperDashTrailsColour = hyperDashColour; + trails.EndGlowSpritesColour = hyperDashEndGlowColour; } public bool OnPressed(CatchAction action) @@ -453,22 +432,6 @@ namespace osu.Game.Rulesets.Catch.UI (currentCatcher.Drawable as IFramedAnimation)?.GotoFrame(0); } - private void beginTrail() - { - if (!dashing && !HyperDashing) - { - Trail = false; - return; - } - - var additive = createAdditiveSprite(HyperDashing ? hyperDashTrails : dashTrails); - - additive.FadeTo(0.4f).FadeOut(800, Easing.OutQuint); - additive.Expire(true); - - Scheduler.AddDelayed(beginTrail, HyperDashing ? 25 : 50); - } - private void updateState(CatcherAnimationState state) { if (CurrentState == state) @@ -478,27 +441,6 @@ namespace osu.Game.Rulesets.Catch.UI updateCatcher(); } - private CatcherTrailSprite createAdditiveSprite(Container target) - { - if (target == null) - return null; - - var tex = (currentCatcher.Drawable as TextureAnimation)?.CurrentFrame ?? ((Sprite)currentCatcher.Drawable).Texture; - - var sprite = new CatcherTrailSprite(tex) - { - Anchor = Anchor, - Scale = Scale, - Blending = BlendingParameters.Additive, - RelativePositionAxes = RelativePositionAxes, - Position = Position - }; - - target.Add(sprite); - - return sprite; - } - private void removeFromPlateWithTransform(DrawableHitObject fruit, Action action) { if (ExplodingFruitTarget != null) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs new file mode 100644 index 0000000000..afbfac9a51 --- /dev/null +++ b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs @@ -0,0 +1,137 @@ +// 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; +using osu.Framework.Graphics.Animations; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Catch.UI +{ + /// + /// Represents a component responsible for displaying + /// the appropriate catcher trails when requested to. + /// + public class CatcherTrailDisplay : CompositeDrawable + { + private readonly Catcher catcher; + + private readonly Container dashTrails; + private readonly Container hyperDashTrails; + private readonly Container endGlowSprites; + + private Color4 hyperDashTrailsColour; + + public Color4 HyperDashTrailsColour + { + get => hyperDashTrailsColour; + set + { + if (hyperDashTrailsColour == value) + return; + + hyperDashTrailsColour = value; + hyperDashTrails.FadeColour(hyperDashTrailsColour, Catcher.HYPER_DASH_TRANSITION_DURATION, Easing.OutQuint); + } + } + + private Color4 endGlowSpritesColour; + + public Color4 EndGlowSpritesColour + { + get => endGlowSpritesColour; + set + { + if (endGlowSpritesColour == value) + return; + + endGlowSpritesColour = value; + endGlowSprites.FadeColour(endGlowSpritesColour, Catcher.HYPER_DASH_TRANSITION_DURATION, Easing.OutQuint); + } + } + + private bool trail; + + /// + /// Whether to start displaying trails following the catcher. + /// + public bool DisplayTrail + { + get => trail; + set + { + if (trail == value) + return; + + trail = value; + + if (trail) + displayTrail(); + } + } + + public CatcherTrailDisplay(Catcher catcher) + { + this.catcher = catcher ?? throw new ArgumentNullException(nameof(catcher)); + + RelativeSizeAxes = Axes.Both; + + InternalChildren = new[] + { + dashTrails = new Container { RelativeSizeAxes = Axes.Both }, + hyperDashTrails = new Container { RelativeSizeAxes = Axes.Both, Colour = Catcher.DEFAULT_HYPER_DASH_COLOUR }, + endGlowSprites = new Container { RelativeSizeAxes = Axes.Both, Colour = Catcher.DEFAULT_HYPER_DASH_COLOUR }, + }; + } + + /// + /// Displays a single end-glow catcher sprite. + /// + public void DisplayEndGlow() + { + var endGlow = createTrailSprite(endGlowSprites); + + endGlow.MoveToOffset(new Vector2(0, -10), 1200, Easing.In); + endGlow.ScaleTo(endGlow.Scale * 0.95f).ScaleTo(endGlow.Scale * 1.2f, 1200, Easing.In); + endGlow.FadeOut(1200); + endGlow.Expire(true); + } + + private void displayTrail() + { + if (!catcher.Dashing && !catcher.HyperDashing) + { + DisplayTrail = false; + return; + } + + var sprite = createTrailSprite(catcher.HyperDashing ? hyperDashTrails : dashTrails); + + sprite.FadeTo(0.4f).FadeOut(800, Easing.OutQuint); + sprite.Expire(true); + + Scheduler.AddDelayed(displayTrail, catcher.HyperDashing ? 25 : 50); + } + + private CatcherTrailSprite createTrailSprite(Container target) + { + var texture = (catcher.CurrentDrawableCatcher as TextureAnimation)?.CurrentFrame ?? ((Sprite)catcher.CurrentDrawableCatcher).Texture; + + var sprite = new CatcherTrailSprite(texture) + { + Anchor = catcher.Anchor, + Scale = catcher.Scale, + Blending = BlendingParameters.Additive, + RelativePositionAxes = catcher.RelativePositionAxes, + Position = catcher.Position + }; + + target.Add(sprite); + + return sprite; + } + } +} From 5d3475a5ed32b45c6b3fca5fdd823fab0e2141f9 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 22 Apr 2020 05:12:51 +0300 Subject: [PATCH 0370/1117] Retrieve CatcherTrailDisplay for asserting colours set correctly --- .../TestSceneHyperDashColouring.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs index 347b71f3ff..0c8ade9018 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs @@ -113,6 +113,7 @@ namespace osu.Game.Rulesets.Catch.Tests private void checkHyperDashCatcherColour(ISkin skin, Color4 expectedCatcherColour, Color4? expectedEndGlowColour = null) { CatcherArea catcherArea = null; + CatcherTrailDisplay trails = null; AddStep("create hyper-dashing catcher", () => { @@ -123,6 +124,7 @@ namespace osu.Game.Rulesets.Catch.Tests Scale = new Vector2(4f), }, skin); + trails = catcherArea.OfType().Single(); catcherArea.MovableCatcher.SetHyperDashState(2); catcherArea.MovableCatcher.FinishTransforms(); }); @@ -132,8 +134,8 @@ namespace osu.Game.Rulesets.Catch.Tests ? catcherArea.MovableCatcher.Colour == Catcher.DEFAULT_CATCHER_HYPER_DASH_COLOUR : catcherArea.MovableCatcher.Colour == expectedCatcherColour); - AddAssert("catcher trails colours are correct", () => catcherArea.OfType>().Any(c => c.Colour == expectedCatcherColour)); - AddAssert("catcher end-glow colours are correct", () => catcherArea.OfType>().Any(c => c.Colour == (expectedEndGlowColour ?? expectedCatcherColour))); + AddAssert("catcher trails colours are correct", () => trails.HyperDashTrailsColour == expectedCatcherColour); + AddAssert("catcher end-glow colours are correct", () => trails.EndGlowSpritesColour == (expectedEndGlowColour ?? expectedCatcherColour)); AddStep("finish hyper-dashing", () => { From 2d4077e71313c8fdb2c03bddd65db9b0a9555ae5 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 22 Apr 2020 05:25:40 +0300 Subject: [PATCH 0371/1117] Reword special default hyper-dash colour constant a bit --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index e30988d8f7..08e438db56 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -29,7 +29,8 @@ namespace osu.Game.Rulesets.Catch.UI public static readonly Color4 DEFAULT_HYPER_DASH_COLOUR = Color4.Red; /// - /// The default colour used directly for this 's . + /// The default hyper-dash colour used directly for this + /// 's . /// /// /// This colour is only used when no skin overrides . From f841eb7e0607b19feb84b0cc896b027390128663 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 22 Apr 2020 07:27:15 +0300 Subject: [PATCH 0372/1117] Replace constructing a whole Catcher with static calculation methods --- .../Difficulty/CatchDifficultyCalculator.cs | 3 +- osu.Game.Rulesets.Catch/UI/Catcher.cs | 36 +++++++++++++++---- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 4d9dbbbc5f..d99325ff87 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -71,8 +71,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty protected override Skill[] CreateSkills(IBeatmap beatmap) { - using (var catcher = new Catcher(beatmap.BeatmapInfo.BaseDifficulty)) - halfCatcherWidth = catcher.CatchWidth * 0.5f; + halfCatcherWidth = Catcher.CalculateCatchWidth(beatmap.BeatmapInfo.BaseDifficulty) * 0.5f; return new Skill[] { diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 920d804e72..ee806b7b99 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -44,11 +44,6 @@ namespace osu.Game.Rulesets.Catch.UI /// private const float allowed_catch_range = 0.8f; - /// - /// Width of the area that can be used to attempt catches during gameplay. - /// - internal float CatchWidth => CatcherArea.CATCHER_SIZE * Math.Abs(Scale.X) * allowed_catch_range; - protected bool Dashing { get => dashing; @@ -79,6 +74,11 @@ namespace osu.Game.Rulesets.Catch.UI } } + /// + /// Width of the area that can be used to attempt catches during gameplay. + /// + private readonly float catchWidth; + private Container caughtFruit; private CatcherSprite catcherIdle; @@ -106,7 +106,9 @@ namespace osu.Game.Rulesets.Catch.UI Size = new Vector2(CatcherArea.CATCHER_SIZE); if (difficulty != null) - Scale = new Vector2(1.0f - 0.7f * (difficulty.CircleSize - 5) / 5); + Scale = CalculateScale(difficulty); + + catchWidth = CalculateCatchWidth(Scale); } [BackgroundDependencyLoader] @@ -139,6 +141,26 @@ namespace osu.Game.Rulesets.Catch.UI updateCatcher(); } + /// + /// Calculates the scale of the catcher based off the provided beatmap difficulty. + /// + internal static Vector2 CalculateScale(BeatmapDifficulty difficulty) + => new Vector2(1.0f - 0.7f * (difficulty.CircleSize - 5) / 5); + + /// + /// Calculates the width of the area used for attempting catches in gameplay. + /// + internal static float CalculateCatchWidth(Vector2 scale) + => CatcherArea.CATCHER_SIZE * Math.Abs(scale.X) * allowed_catch_range; + + /// + /// Calculates the width of the area used for attempting catches in gameplay. + /// + /// + /// + internal static float CalculateCatchWidth(BeatmapDifficulty difficulty) + => CalculateCatchWidth(CalculateScale(difficulty)); + /// /// Add a caught fruit to the catcher's stack. /// @@ -177,7 +199,7 @@ namespace osu.Game.Rulesets.Catch.UI /// Whether the catch is possible. public bool AttemptCatch(CatchHitObject fruit) { - var halfCatchWidth = CatchWidth * 0.5f; + var halfCatchWidth = catchWidth * 0.5f; // this stuff wil disappear once we move fruit to non-relative coordinate space in the future. var catchObjectPosition = fruit.X * CatchPlayfield.BASE_WIDTH; From fccb30e031a40e08533e5ce2f862cbe81d6fc504 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 22 Apr 2020 07:36:59 +0300 Subject: [PATCH 0373/1117] Adjust documents *whoops* --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index ee806b7b99..4dace76008 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -150,14 +150,14 @@ namespace osu.Game.Rulesets.Catch.UI /// /// Calculates the width of the area used for attempting catches in gameplay. /// + /// The scale of the catcher. internal static float CalculateCatchWidth(Vector2 scale) => CatcherArea.CATCHER_SIZE * Math.Abs(scale.X) * allowed_catch_range; /// /// Calculates the width of the area used for attempting catches in gameplay. /// - /// - /// + /// The beatmap difficulty. internal static float CalculateCatchWidth(BeatmapDifficulty difficulty) => CalculateCatchWidth(CalculateScale(difficulty)); From 883788dd5aec1b228328812fb4ea14043c299118 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 22 Apr 2020 07:37:49 +0300 Subject: [PATCH 0374/1117] Privatize externally-unused methods --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 4dace76008..daf9456919 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -106,7 +106,7 @@ namespace osu.Game.Rulesets.Catch.UI Size = new Vector2(CatcherArea.CATCHER_SIZE); if (difficulty != null) - Scale = CalculateScale(difficulty); + Scale = calculateScale(difficulty); catchWidth = CalculateCatchWidth(Scale); } @@ -144,7 +144,7 @@ namespace osu.Game.Rulesets.Catch.UI /// /// Calculates the scale of the catcher based off the provided beatmap difficulty. /// - internal static Vector2 CalculateScale(BeatmapDifficulty difficulty) + private static Vector2 calculateScale(BeatmapDifficulty difficulty) => new Vector2(1.0f - 0.7f * (difficulty.CircleSize - 5) / 5); /// @@ -159,7 +159,7 @@ namespace osu.Game.Rulesets.Catch.UI /// /// The beatmap difficulty. internal static float CalculateCatchWidth(BeatmapDifficulty difficulty) - => CalculateCatchWidth(CalculateScale(difficulty)); + => CalculateCatchWidth(calculateScale(difficulty)); /// /// Add a caught fruit to the catcher's stack. From 58af75ad576396258285b2722dfb07d1176f7889 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 22 Apr 2020 13:45:12 +0900 Subject: [PATCH 0375/1117] Add back missing line --- osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index cb583ac1a9..1c8116754f 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -237,6 +237,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps { StartTime = HitObject.StartTime, Duration = endTimeData.Duration, + Column = column, Samples = HitObject.Samples, NodeSamples = (HitObject as IHasRepeats)?.NodeSamples }); From 7cdc9a599c6ffb39c7635e63cbf8fa57bbcb2684 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 22 Apr 2020 16:27:07 +0900 Subject: [PATCH 0376/1117] Fix mania holds written as spinners --- osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 4b760f1983..fc853a7a68 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -248,7 +248,7 @@ namespace osu.Game.Beatmaps.Formats writer.WriteLine(); } - private static LegacyHitObjectType getObjectType(HitObject hitObject) + private LegacyHitObjectType getObjectType(HitObject hitObject) { LegacyHitObjectType type = 0; @@ -267,7 +267,10 @@ namespace osu.Game.Beatmaps.Formats break; case IHasEndTime _: - type |= LegacyHitObjectType.Spinner; + if (beatmap.BeatmapInfo.RulesetID == 3) + type |= LegacyHitObjectType.Hold; + else + type |= LegacyHitObjectType.Spinner; break; default: From 3b805daa0b1fedac05e46e26fa6e77ba9cc1b4dc Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 22 Apr 2020 16:40:07 +0900 Subject: [PATCH 0377/1117] Fix hold note end time being written incorrectly --- .../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index fc853a7a68..b5a8f1604c 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -240,8 +240,9 @@ namespace osu.Game.Beatmaps.Formats } else { - if (hitObject is IHasEndTime endTimeData) - writer.Write(FormattableString.Invariant($"{endTimeData.EndTime},")); + if (hitObject is IHasEndTime _) + addEndTimeData(writer, hitObject); + writer.Write(getSampleBank(hitObject.Samples)); } @@ -344,6 +345,20 @@ namespace osu.Game.Beatmaps.Formats } } + private void addEndTimeData(TextWriter writer, HitObject hitObject) + { + var endTimeData = (IHasEndTime)hitObject; + var type = getObjectType(hitObject); + + char suffix = ','; + + // Holds write the end time as if it's part of sample data. + if (type == LegacyHitObjectType.Hold) + suffix = ':'; + + writer.Write(FormattableString.Invariant($"{endTimeData.EndTime}{suffix}")); + } + private string getSampleBank(IList samples, bool banksOnly = false, bool zeroBanks = false) { LegacySampleBank normalBank = toLegacySampleBank(samples.SingleOrDefault(s => s.Name == HitSampleInfo.HIT_NORMAL)?.Bank); From 730b5ea1a986a6772520d8babd185ede3543c2ab Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 22 Apr 2020 10:40:21 +0300 Subject: [PATCH 0378/1117] Make the Catcher.Colour assertion read better --- .../TestSceneHyperDashColouring.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs index 0c8ade9018..1e1746efb3 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs @@ -130,9 +130,16 @@ namespace osu.Game.Rulesets.Catch.Tests }); AddAssert("catcher colour is correct", () => - expectedCatcherColour == Catcher.DEFAULT_HYPER_DASH_COLOUR - ? catcherArea.MovableCatcher.Colour == Catcher.DEFAULT_CATCHER_HYPER_DASH_COLOUR - : catcherArea.MovableCatcher.Colour == expectedCatcherColour); + { + var expected = expectedCatcherColour; + + if (expected == Catcher.DEFAULT_HYPER_DASH_COLOUR) + // The expected colour for Catcher.Colour is another colour + // for the default skin, assert with that instead. + expected = Catcher.DEFAULT_CATCHER_HYPER_DASH_COLOUR; + + return catcherArea.MovableCatcher.Colour == expected; + }); AddAssert("catcher trails colours are correct", () => trails.HyperDashTrailsColour == expectedCatcherColour); AddAssert("catcher end-glow colours are correct", () => trails.EndGlowSpritesColour == (expectedEndGlowColour ?? expectedCatcherColour)); From bffe6742e0cde53ce1ce4c6f76546e79eca41abd Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 22 Apr 2020 10:43:49 +0300 Subject: [PATCH 0379/1117] Replace finishing catcher transforms with until-true step --- osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs index 1e1746efb3..589bafe400 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs @@ -126,10 +126,9 @@ namespace osu.Game.Rulesets.Catch.Tests trails = catcherArea.OfType().Single(); catcherArea.MovableCatcher.SetHyperDashState(2); - catcherArea.MovableCatcher.FinishTransforms(); }); - AddAssert("catcher colour is correct", () => + AddUntilStep("catcher colour is correct", () => { var expected = expectedCatcherColour; From 44405d4771b76649e64fd7bd81e13cef17977b71 Mon Sep 17 00:00:00 2001 From: Tim Oliver Date: Wed, 22 Apr 2020 15:50:23 +0800 Subject: [PATCH 0380/1117] Moved result to load complete for flying hits --- .../Objects/Drawables/DrawableFlyingCentreHit.cs | 3 ++- .../Objects/Drawables/DrawableFlyingRimHit.cs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingCentreHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingCentreHit.cs index 826a4467f8..f70d940bd2 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingCentreHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingCentreHit.cs @@ -9,8 +9,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { public class DrawableFlyingCentreHit : DrawableCentreHit { - protected override void CheckForResult(bool userTriggered, double timeOffset) + protected override void LoadComplete() { + base.LoadComplete(); ApplyResult(r => r.Type = HitResult.Good); } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingRimHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingRimHit.cs index 4a6fed8302..9005dac653 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingRimHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingRimHit.cs @@ -9,8 +9,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { public class DrawableFlyingRimHit : DrawableRimHit { - protected override void CheckForResult(bool userTriggered, double timeOffset) + protected override void LoadComplete() { + base.LoadComplete(); ApplyResult(r => r.Type = HitResult.Good); } From 9c22d2f1dd167cd6b28ec2bdd7a3949219773209 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 22 Apr 2020 17:41:24 +0900 Subject: [PATCH 0381/1117] Use platform bindings for editor actions --- osu.Game/Screens/Edit/Editor.cs | 49 +++++++++++++++++---------------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 9a1f450dc6..5665f4b25d 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -22,6 +22,7 @@ using osu.Game.Screens.Edit.Design; using osuTK.Input; using System.Collections.Generic; using osu.Framework; +using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Logging; using osu.Game.Beatmaps; @@ -37,7 +38,7 @@ using osu.Game.Users; namespace osu.Game.Screens.Edit { [Cached(typeof(IBeatSnapProvider))] - public class Editor : ScreenWithBeatmapBackground, IKeyBindingHandler, IBeatSnapProvider + public class Editor : ScreenWithBeatmapBackground, IKeyBindingHandler, IKeyBindingHandler, IBeatSnapProvider { public override float BackgroundParallaxAmount => 0.1f; @@ -230,6 +231,30 @@ namespace osu.Game.Screens.Edit clock.ProcessFrame(); } + public bool OnPressed(PlatformAction action) + { + switch (action.ActionType) + { + case PlatformActionType.Undo: + undo(); + return true; + + case PlatformActionType.Redo: + redo(); + return true; + + case PlatformActionType.Save: + saveBeatmap(); + return true; + } + + return false; + } + + public void OnReleased(PlatformAction action) + { + } + protected override bool OnKeyDown(KeyDownEvent e) { switch (e.Key) @@ -241,28 +266,6 @@ namespace osu.Game.Screens.Edit case Key.Right: seek(e, 1); return true; - - case Key.S: - if (e.ControlPressed) - { - saveBeatmap(); - return true; - } - - break; - - case Key.Z: - if (e.ControlPressed) - { - if (e.ShiftPressed) - redo(); - else - undo(); - - return true; - } - - break; } return base.OnKeyDown(e); From 8b0274fedd51ff2e897930786b4c04187f541daa Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 22 Apr 2020 17:55:50 +0900 Subject: [PATCH 0382/1117] Remove obsolete methods --- .../Objects/Drawables/DrawableHitObject.cs | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index e847dcec40..1316ac1156 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Reflection; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -180,11 +179,6 @@ namespace osu.Game.Rulesets.Objects.Drawables private void apply(HitObject hitObject) { -#pragma warning disable 618 // can be removed 20200417 - if (GetType().GetMethod(nameof(AddNested), BindingFlags.NonPublic | BindingFlags.Instance)?.DeclaringType != typeof(DrawableHitObject)) - return; -#pragma warning restore 618 - if (nestedHitObjects.IsValueCreated) { nestedHitObjects.Value.Clear(); @@ -194,8 +188,6 @@ namespace osu.Game.Rulesets.Objects.Drawables foreach (var h in hitObject.NestedHitObjects) { var drawableNested = CreateNestedHitObject(h) ?? throw new InvalidOperationException($"{nameof(CreateNestedHitObject)} returned null for {h.GetType().ReadableName()}."); - - addNested(drawableNested); AddNestedHitObject(drawableNested); } } @@ -208,13 +200,6 @@ namespace osu.Game.Rulesets.Objects.Drawables { } - /// - /// Adds a nested . This should not be used except for legacy nested usages. - /// - /// - [Obsolete("Use AddNestedHitObject() / ClearNestedHitObjects() / CreateNestedHitObject() instead.")] // can be removed 20200417 - protected virtual void AddNested(DrawableHitObject h) => addNested(h); - /// /// Invoked by the base to remove all previously-added nested s. /// @@ -229,17 +214,6 @@ namespace osu.Game.Rulesets.Objects.Drawables /// The drawable representation for . protected virtual DrawableHitObject CreateNestedHitObject(HitObject hitObject) => null; - private void addNested(DrawableHitObject hitObject) - { - // Todo: Exists for legacy purposes, can be removed 20200417 - - hitObject.OnNewResult += (d, r) => OnNewResult?.Invoke(d, r); - hitObject.OnRevertResult += (d, r) => OnRevertResult?.Invoke(d, r); - hitObject.ApplyCustomUpdateState += (d, j) => ApplyCustomUpdateState?.Invoke(d, j); - - nestedHitObjects.Value.Add(hitObject); - } - #region State / Transform Management /// From e1142b424d2f140dbb61521920c33336d5f0992e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 22 Apr 2020 18:14:21 +0900 Subject: [PATCH 0383/1117] Fix test failures --- .../Editor/TestSceneEditorChangeStates.cs | 41 ++++--------------- osu.Game/Screens/Edit/Editor.cs | 12 +++--- 2 files changed, 15 insertions(+), 38 deletions(-) diff --git a/osu.Game.Tests/Visual/Editor/TestSceneEditorChangeStates.cs b/osu.Game.Tests/Visual/Editor/TestSceneEditorChangeStates.cs index dd1b6cf6aa..efc2a6f552 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneEditorChangeStates.cs +++ b/osu.Game.Tests/Visual/Editor/TestSceneEditorChangeStates.cs @@ -10,24 +10,22 @@ using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Compose.Components.Timeline; -using osuTK.Input; namespace osu.Game.Tests.Visual.Editor { public class TestSceneEditorChangeStates : ScreenTestScene { private EditorBeatmap editorBeatmap; + private TestEditor editor; public override void SetUpSteps() { base.SetUpSteps(); - Screens.Edit.Editor editor = null; - AddStep("load editor", () => { Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); - LoadScreen(editor = new Screens.Edit.Editor()); + LoadScreen(editor = new TestEditor()); }); AddUntilStep("wait for editor to load", () => editor.ChildrenOfType().FirstOrDefault()?.IsLoaded == true @@ -160,36 +158,15 @@ namespace osu.Game.Tests.Visual.Editor AddAssert("no hitobject added", () => addedObject == null); } - private void addUndoSteps() + private void addUndoSteps() => AddStep("undo", () => editor.Undo()); + + private void addRedoSteps() => AddStep("redo", () => editor.Redo()); + + private class TestEditor : Screens.Edit.Editor { - AddStep("press undo", () => - { - InputManager.PressKey(Key.LControl); - InputManager.PressKey(Key.Z); - }); + public new void Undo() => base.Undo(); - AddStep("release keys", () => - { - InputManager.ReleaseKey(Key.LControl); - InputManager.ReleaseKey(Key.Z); - }); - } - - private void addRedoSteps() - { - AddStep("press redo", () => - { - InputManager.PressKey(Key.LControl); - InputManager.PressKey(Key.LShift); - InputManager.PressKey(Key.Z); - }); - - AddStep("release keys", () => - { - InputManager.ReleaseKey(Key.LControl); - InputManager.ReleaseKey(Key.LShift); - InputManager.ReleaseKey(Key.Z); - }); + public new void Redo() => base.Redo(); } } } diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 5665f4b25d..54e4af94a4 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -158,8 +158,8 @@ namespace osu.Game.Screens.Edit { Items = new[] { - undoMenuItem = new EditorMenuItem("Undo", MenuItemType.Standard, undo), - redoMenuItem = new EditorMenuItem("Redo", MenuItemType.Standard, redo) + undoMenuItem = new EditorMenuItem("Undo", MenuItemType.Standard, Undo), + redoMenuItem = new EditorMenuItem("Redo", MenuItemType.Standard, Redo) } } } @@ -236,11 +236,11 @@ namespace osu.Game.Screens.Edit switch (action.ActionType) { case PlatformActionType.Undo: - undo(); + Undo(); return true; case PlatformActionType.Redo: - redo(); + Redo(); return true; case PlatformActionType.Save: @@ -329,9 +329,9 @@ namespace osu.Game.Screens.Edit return base.OnExiting(next); } - private void undo() => changeHandler.RestoreState(-1); + protected void Undo() => changeHandler.RestoreState(-1); - private void redo() => changeHandler.RestoreState(1); + protected void Redo() => changeHandler.RestoreState(1); private void resetTrack(bool seekToStart = false) { From 93151f761215c135ae625241532fa6b899e73747 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 22 Apr 2020 18:32:59 +0900 Subject: [PATCH 0384/1117] Add back necessary events + addition to list --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 1316ac1156..0047142cbd 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -188,6 +188,12 @@ namespace osu.Game.Rulesets.Objects.Drawables foreach (var h in hitObject.NestedHitObjects) { var drawableNested = CreateNestedHitObject(h) ?? throw new InvalidOperationException($"{nameof(CreateNestedHitObject)} returned null for {h.GetType().ReadableName()}."); + + drawableNested.OnNewResult += (d, r) => OnNewResult?.Invoke(d, r); + drawableNested.OnRevertResult += (d, r) => OnRevertResult?.Invoke(d, r); + drawableNested.ApplyCustomUpdateState += (d, j) => ApplyCustomUpdateState?.Invoke(d, j); + + nestedHitObjects.Value.Add(drawableNested); AddNestedHitObject(drawableNested); } } From 0a34fddcc3cfda42bc40e393374b7c0decd824d8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 22 Apr 2020 19:38:27 +0900 Subject: [PATCH 0385/1117] Fix TestBeatmap not setting appropriate ruleset ID --- osu.Game/Tests/Beatmaps/TestBeatmap.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Tests/Beatmaps/TestBeatmap.cs b/osu.Game/Tests/Beatmaps/TestBeatmap.cs index 96e3c037a3..a7c84bf692 100644 --- a/osu.Game/Tests/Beatmaps/TestBeatmap.cs +++ b/osu.Game/Tests/Beatmaps/TestBeatmap.cs @@ -23,6 +23,7 @@ namespace osu.Game.Tests.Beatmaps HitObjects = baseBeatmap.HitObjects; BeatmapInfo.Ruleset = ruleset; + BeatmapInfo.RulesetID = ruleset.ID ?? 0; BeatmapInfo.BeatmapSet.Metadata = BeatmapInfo.Metadata; BeatmapInfo.BeatmapSet.Beatmaps = new List { BeatmapInfo }; BeatmapInfo.BeatmapSet.OnlineInfo = new BeatmapSetOnlineInfo From 08982e0e00da8fe144793a8b7fa49713fdf98145 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 22 Apr 2020 19:49:21 +0900 Subject: [PATCH 0386/1117] Ensure editor tests wait for load to complete --- .../Editor/TestSceneEditorChangeStates.cs | 21 +++++++------------ osu.Game/Tests/Visual/EditorTestScene.cs | 10 ++++++++- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/osu.Game.Tests/Visual/Editor/TestSceneEditorChangeStates.cs b/osu.Game.Tests/Visual/Editor/TestSceneEditorChangeStates.cs index dd1b6cf6aa..c68015d1a2 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneEditorChangeStates.cs +++ b/osu.Game.Tests/Visual/Editor/TestSceneEditorChangeStates.cs @@ -4,35 +4,28 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Testing; -using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Screens.Edit; -using osu.Game.Screens.Edit.Compose.Components.Timeline; using osuTK.Input; namespace osu.Game.Tests.Visual.Editor { - public class TestSceneEditorChangeStates : ScreenTestScene + public class TestSceneEditorChangeStates : EditorTestScene { + public TestSceneEditorChangeStates() + : base(new OsuRuleset()) + { + } + private EditorBeatmap editorBeatmap; public override void SetUpSteps() { base.SetUpSteps(); - Screens.Edit.Editor editor = null; - - AddStep("load editor", () => - { - Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); - LoadScreen(editor = new Screens.Edit.Editor()); - }); - - AddUntilStep("wait for editor to load", () => editor.ChildrenOfType().FirstOrDefault()?.IsLoaded == true - && editor.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); - AddStep("get beatmap", () => editorBeatmap = editor.ChildrenOfType().Single()); + AddStep("get beatmap", () => editorBeatmap = Editor.ChildrenOfType().Single()); } [Test] diff --git a/osu.Game/Tests/Visual/EditorTestScene.cs b/osu.Game/Tests/Visual/EditorTestScene.cs index 80bc3bdb87..88e50d4858 100644 --- a/osu.Game/Tests/Visual/EditorTestScene.cs +++ b/osu.Game/Tests/Visual/EditorTestScene.cs @@ -3,9 +3,13 @@ using System; using System.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Testing; using osu.Game.Rulesets; +using osu.Game.Rulesets.Edit; using osu.Game.Screens.Edit; +using osu.Game.Screens.Edit.Compose.Components.Timeline; namespace osu.Game.Tests.Visual { @@ -13,6 +17,8 @@ namespace osu.Game.Tests.Visual { public override IReadOnlyList RequiredTypes => new[] { typeof(Editor), typeof(EditorScreen) }; + protected Editor Editor { get; private set; } + private readonly Ruleset ruleset; protected EditorTestScene(Ruleset ruleset) @@ -30,7 +36,9 @@ namespace osu.Game.Tests.Visual { base.SetUpSteps(); - AddStep("Load editor", () => LoadScreen(new Editor())); + AddStep("load editor", () => LoadScreen(Editor = new Editor())); + AddUntilStep("wait for editor to load", () => Editor.ChildrenOfType().FirstOrDefault()?.IsLoaded == true + && Editor.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); } } } From 617d27ace9620128db73f1da44278d2111d801a0 Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Wed, 22 Apr 2020 15:19:29 +0200 Subject: [PATCH 0387/1117] Restart branch --- .../Skinning/TaikoLegacySkinTransformer.cs | 12 ++- .../TaikoSkinComponents.cs | 1 + .../UI/DefaultTaikoDonTextureAnimation.cs | 60 ++++++++++++++ .../UI/DrawableTaikoCharacter.cs | 80 +++++++++++++++++++ .../UI/TaikoDonAnimationState.cs | 13 +++ osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 39 +++++++-- 6 files changed, 196 insertions(+), 9 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko/UI/DefaultTaikoDonTextureAnimation.cs create mode 100644 osu.Game.Rulesets.Taiko/UI/DrawableTaikoCharacter.cs create mode 100644 osu.Game.Rulesets.Taiko/UI/TaikoDonAnimationState.cs diff --git a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs index 78eec94590..722dbf2671 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs @@ -7,6 +7,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; using osu.Game.Audio; +using osu.Game.Rulesets.Taiko.UI; using osu.Game.Skinning; namespace osu.Game.Rulesets.Taiko.Skinning @@ -32,9 +33,16 @@ namespace osu.Game.Rulesets.Taiko.Skinning return new LegacyInputDrum(); return null; - } - return source.GetDrawableComponent(component); + case TaikoSkinComponents.TaikoDon: + if (GetTexture("pippidonclear0") != null) + return new DrawableTaikoCharacter(); + + return null; + + default: + return source.GetDrawableComponent(component); + } } public Texture GetTexture(string componentName) => source.GetTexture(componentName); diff --git a/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs b/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs index 6d4581db80..a68cc54efa 100644 --- a/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs +++ b/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs @@ -6,5 +6,6 @@ namespace osu.Game.Rulesets.Taiko public enum TaikoSkinComponents { InputDrum, + TaikoDon } } diff --git a/osu.Game.Rulesets.Taiko/UI/DefaultTaikoDonTextureAnimation.cs b/osu.Game.Rulesets.Taiko/UI/DefaultTaikoDonTextureAnimation.cs new file mode 100644 index 0000000000..1fefed953d --- /dev/null +++ b/osu.Game.Rulesets.Taiko/UI/DefaultTaikoDonTextureAnimation.cs @@ -0,0 +1,60 @@ +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Animations; +using osu.Framework.Graphics.Textures; +using osu.Game.Skinning; + +namespace osu.Game.Rulesets.Taiko.UI +{ + public sealed class DefaultTaikoDonTextureAnimation : TextureAnimation + { + private readonly TaikoDonAnimationState _state; + private int currentFrame; + + public DefaultTaikoDonTextureAnimation(TaikoDonAnimationState state) : base(false) + { + _state = state; + this.Stop(); + + Origin = Anchor.BottomLeft; + Anchor = Anchor.BottomLeft; + } + + [BackgroundDependencyLoader] + private void load(ISkinSource skin) + { + for (int i = 0;; i++) + { + var textureName = $"pippidon{_getStateString(_state)}{i}"; + Texture texture = skin.GetTexture(textureName); + + if (texture == null) + break; + + AddFrame(texture); + } + } + + /// + /// Advances the current frame by one. + /// + public void Move() + { + if (FrameCount <= currentFrame) + currentFrame = 0; + + GotoFrame(currentFrame); + + currentFrame++; + } + + private string _getStateString(TaikoDonAnimationState state) => state switch + { + TaikoDonAnimationState.Clear => "clear", + TaikoDonAnimationState.Fail => "fail", + TaikoDonAnimationState.Idle => "idle", + TaikoDonAnimationState.Kiai => "kiai", + _ => null + }; + } +} diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoCharacter.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoCharacter.cs new file mode 100644 index 0000000000..897670d049 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoCharacter.cs @@ -0,0 +1,80 @@ +using osu.Framework.Allocation; +using osu.Framework.Audio.Track; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Textures; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Graphics.Containers; +using osuTK; + +namespace osu.Game.Rulesets.Taiko.UI +{ + public sealed class DrawableTaikoCharacter : BeatSyncedContainer + { + private static DefaultTaikoDonTextureAnimation idleDrawable, clearDrawable, kiaiDrawable, failDrawable; + + private TaikoDonAnimationState state; + + public DrawableTaikoCharacter() + { + RelativeSizeAxes = Axes.Both; + //Size = new Vector2(1f, 2.5f); + //Origin = Anchor.BottomLeft; + var xd = new Vector2(1); + } + + private DefaultTaikoDonTextureAnimation getStateDrawable() => State switch + { + TaikoDonAnimationState.Idle => idleDrawable, + TaikoDonAnimationState.Clear => clearDrawable, + TaikoDonAnimationState.Kiai => kiaiDrawable, + TaikoDonAnimationState.Fail => failDrawable, + _ => null + }; + + public TaikoDonAnimationState State + { + get => state; + set + { + state = value; + + foreach (var child in InternalChildren) + child.Hide(); + + getStateDrawable().Show(); + } + } + + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + InternalChildren = new[] + { + idleDrawable = new DefaultTaikoDonTextureAnimation(TaikoDonAnimationState.Idle), + clearDrawable = new DefaultTaikoDonTextureAnimation(TaikoDonAnimationState.Clear), + kiaiDrawable = new DefaultTaikoDonTextureAnimation(TaikoDonAnimationState.Kiai), + failDrawable = new DefaultTaikoDonTextureAnimation(TaikoDonAnimationState.Fail), + }; + + // sets the state, to make sure we have the correct sprite loaded and set. + State = TaikoDonAnimationState.Idle; + } + + protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) + { + base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); + + getStateDrawable().Move(); + + //var signature = timingPoint.TimeSignature == TimeSignatures.SimpleQuadruple ? 4 : 3; + //var length = timingPoint.BeatLength; + //var rate = 1000d / length; + //adjustableClock.Rate = rate; + // + //// Start animating on the first beat. + //if (beatIndex < 1) + // adjustableClock.Start(); + // Logger.GetLogger(LoggingTarget.Information).Add($"Length = {length}ms | Rate = {rate}x | BPM = {timingPoint.BPM} / {signature}"); + } + } +} diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoDonAnimationState.cs b/osu.Game.Rulesets.Taiko/UI/TaikoDonAnimationState.cs new file mode 100644 index 0000000000..773710ee7e --- /dev/null +++ b/osu.Game.Rulesets.Taiko/UI/TaikoDonAnimationState.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.Rulesets.Taiko.UI +{ + public enum TaikoDonAnimationState + { + Idle, + Clear, + Kiai, + Fail + } +} diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index bde9085c23..d5cabb8662 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -19,6 +19,9 @@ using osu.Game.Rulesets.Taiko.Judgements; using osu.Game.Rulesets.Taiko.Objects; using osuTK; using osuTK.Graphics; +using osu.Game.Rulesets.Scoring; +using osu.Framework.Bindables; +using osu.Game.Skinning; namespace osu.Game.Rulesets.Taiko.UI { @@ -53,6 +56,10 @@ namespace osu.Game.Rulesets.Taiko.UI private readonly Box overlayBackground; private readonly Box background; + private readonly SkinnableDrawable characterDrawable; + + private Bindable frameTime = new Bindable(100); + public TaikoPlayfield(ControlPointInfo controlPoints) { InternalChildren = new Drawable[] @@ -186,6 +193,12 @@ namespace osu.Game.Rulesets.Taiko.UI { Name = "Top level hit objects", RelativeSizeAxes = Axes.Both, + }, + characterDrawable = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.TaikoDon), _ => new Container(), confineMode: ConfineMode.ScaleToFit) + { + Origin = Anchor.BottomLeft, + Anchor = Anchor.TopLeft, + RelativePositionAxes = Axes.None } }; } @@ -254,16 +267,28 @@ namespace osu.Game.Rulesets.Taiko.UI break; } - } - private class ProxyContainer : LifetimeManagementContainer - { - public new MarginPadding Padding + if (characterDrawable.Drawable is DrawableTaikoCharacter character) { - set => base.Padding = value; + if (result.Type == HitResult.Miss && result.Judgement.AffectsCombo) + { + character.State = TaikoDonAnimationState.Fail; + } + else + { + character.State = judgedObject.HitObject.Kiai ? TaikoDonAnimationState.Kiai : TaikoDonAnimationState.Idle; + } } - - public void Add(Drawable proxy) => AddInternal(proxy); } } + + class ProxyContainer : LifetimeManagementContainer + { + public new MarginPadding Padding + { + set => base.Padding = value; + } + + public void Add(Drawable proxy) => AddInternal(proxy); + } } From 26779a57b4b211ab86e9bdd41d194c1d7271e5e5 Mon Sep 17 00:00:00 2001 From: Tim Oliver Date: Wed, 22 Apr 2020 22:49:30 +0800 Subject: [PATCH 0388/1117] Exposed public ability to unproxy content --- .../Objects/Drawables/DrawableTaikoHitObject.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs index 5f892dd2fa..b2f9086184 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs @@ -76,6 +76,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables /// public Drawable CreateProxiedContent() => proxiedContent.CreateProxy(); + public void RemoveProxiedContent() => UnproxyContent(); + public abstract bool OnPressed(TaikoAction action); public virtual void OnReleased(TaikoAction action) From 2600518b1bfdfc39809dc5221a7b2e783e7fd0bb Mon Sep 17 00:00:00 2001 From: Tim Oliver Date: Wed, 22 Apr 2020 22:50:00 +0800 Subject: [PATCH 0389/1117] Moved drumroll container and removed rewound notes --- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 31 ++++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index e947795fe5..70839ce806 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -119,7 +119,14 @@ namespace osu.Game.Rulesets.Taiko.UI RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Left = HIT_TARGET_OFFSET }, Masking = true, - Child = HitObjectContainer + Children = new Drawable[] + { + HitObjectContainer, + drumRollHitContainer = new ScrollingHitObjectContainer + { + Name = "Drumroll hit" + } + } }, kiaiExplosionContainer = new Container { @@ -135,14 +142,6 @@ namespace osu.Game.Rulesets.Taiko.UI RelativeSizeAxes = Axes.Y, Margin = new MarginPadding { Left = HIT_TARGET_OFFSET }, Blending = BlendingParameters.Additive - }, - drumRollHitContainer = new ScrollingHitObjectContainer - { - Name = "Drumroll hit", - RelativeSizeAxes = Axes.Both, - FillMode = FillMode.Stretch, - Margin = new MarginPadding { Left = HIT_TARGET_OFFSET }, - Width = 1.0f } } }, @@ -282,6 +281,20 @@ namespace osu.Game.Rulesets.Taiko.UI } } + protected override void Update() + { + base.Update(); + + if (Time.Elapsed < 0) + { + foreach (DrawableHit taikoHit in drumRollHitContainer.Objects) + { + taikoHit.RemoveProxiedContent(); + drumRollHitContainer.Remove(taikoHit); + } + } + } + private class ProxyContainer : LifetimeManagementContainer { public new MarginPadding Padding From 40f11ed15c944dd0007a5d637a1215c2ac33c902 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 23 Apr 2020 10:37:05 +0900 Subject: [PATCH 0390/1117] Resolve broken test scene --- .../Visual/Editor/TestSceneEditorChangeStates.cs | 7 ++++--- osu.Game/Tests/Visual/EditorTestScene.cs | 4 +++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Editor/TestSceneEditorChangeStates.cs b/osu.Game.Tests/Visual/Editor/TestSceneEditorChangeStates.cs index 7b4747592a..efdcc6f78b 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneEditorChangeStates.cs +++ b/osu.Game.Tests/Visual/Editor/TestSceneEditorChangeStates.cs @@ -19,7 +19,6 @@ namespace osu.Game.Tests.Visual.Editor } private EditorBeatmap editorBeatmap; - private TestEditor editor; public override void SetUpSteps() { @@ -153,9 +152,11 @@ namespace osu.Game.Tests.Visual.Editor AddAssert("no hitobject added", () => addedObject == null); } - private void addUndoSteps() => AddStep("undo", () => editor.Undo()); + private void addUndoSteps() => AddStep("undo", () => ((TestEditor)Editor).Undo()); - private void addRedoSteps() => AddStep("redo", () => editor.Redo()); + private void addRedoSteps() => AddStep("redo", () => ((TestEditor)Editor).Redo()); + + protected override Screens.Edit.Editor CreateEditor() => new TestEditor(); private class TestEditor : Screens.Edit.Editor { diff --git a/osu.Game/Tests/Visual/EditorTestScene.cs b/osu.Game/Tests/Visual/EditorTestScene.cs index 88e50d4858..caf2bc0ff1 100644 --- a/osu.Game/Tests/Visual/EditorTestScene.cs +++ b/osu.Game/Tests/Visual/EditorTestScene.cs @@ -36,9 +36,11 @@ namespace osu.Game.Tests.Visual { base.SetUpSteps(); - AddStep("load editor", () => LoadScreen(Editor = new Editor())); + AddStep("load editor", () => LoadScreen(Editor = CreateEditor())); AddUntilStep("wait for editor to load", () => Editor.ChildrenOfType().FirstOrDefault()?.IsLoaded == true && Editor.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); } + + protected virtual Editor CreateEditor() => new Editor(); } } From 86ef73aa27934b377c5a931c0e66b2f96cc0fc9a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 23 Apr 2020 11:16:59 +0900 Subject: [PATCH 0391/1117] Implement HitObjectContainer.Clear() --- osu.Game/Rulesets/UI/HitObjectContainer.cs | 9 +++++++++ .../Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs | 8 ++++++++ 2 files changed, 17 insertions(+) diff --git a/osu.Game/Rulesets/UI/HitObjectContainer.cs b/osu.Game/Rulesets/UI/HitObjectContainer.cs index dea981c3ad..f4f66f1272 100644 --- a/osu.Game/Rulesets/UI/HitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/HitObjectContainer.cs @@ -43,6 +43,15 @@ namespace osu.Game.Rulesets.UI return true; } + public virtual void Clear(bool disposeChildren = true) + { + ClearInternal(disposeChildren); + + foreach (var kvp in startTimeMap) + kvp.Value.bindable.UnbindAll(); + startTimeMap.Clear(); + } + public int IndexOf(DrawableHitObject hitObject) => IndexOfInternal(hitObject); private void onStartTimeChanged(DrawableHitObject hitObject) diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index 108f98d5fc..57f58be55a 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -58,6 +58,14 @@ namespace osu.Game.Rulesets.UI.Scrolling return result; } + public override void Clear(bool disposeChildren = true) + { + base.Clear(disposeChildren); + + initialStateCache.Invalidate(); + hitObjectInitialStateCache.Clear(); + } + private float scrollLength; protected override void Update() From 6df45164face4fb8b31e3cb7a9cfaf9d97003ec7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 23 Apr 2020 11:17:16 +0900 Subject: [PATCH 0392/1117] Expose direction from scrolling test container --- osu.Game/Tests/Visual/ScrollingTestContainer.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Tests/Visual/ScrollingTestContainer.cs b/osu.Game/Tests/Visual/ScrollingTestContainer.cs index 3b741fcf1d..994f23577d 100644 --- a/osu.Game/Tests/Visual/ScrollingTestContainer.cs +++ b/osu.Game/Tests/Visual/ScrollingTestContainer.cs @@ -30,6 +30,11 @@ namespace osu.Game.Tests.Visual set => scrollingInfo.TimeRange.Value = value; } + public ScrollingDirection Direction + { + set => scrollingInfo.Direction.Value = value; + } + public IScrollingInfo ScrollingInfo => scrollingInfo; [Cached(Type = typeof(IScrollingInfo))] From 6376128bde406d3340feb7f9dd946d8bdae77225 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 23 Apr 2020 05:33:28 +0300 Subject: [PATCH 0393/1117] Remove unnecessary using directive --- osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 57930a21fa..d99325ff87 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; -using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.Difficulty.Preprocessing; using osu.Game.Rulesets.Catch.Difficulty.Skills; From ca56e6c0d2a1fb09749bd8f4070cfeaed947048c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 Apr 2020 12:10:26 +0900 Subject: [PATCH 0394/1117] Rename taiko HitTarget classes to avoid conflict with mania --- .../Skinning/TestSceneTaikoPlayfield.cs | 4 ++-- .../{LegacyHitTarget.cs => TaikoLegacyHitTarget.cs} | 8 +++++--- .../Skinning/TaikoLegacySkinTransformer.cs | 2 +- .../UI/{HitTarget.cs => TaikoHitTarget.cs} | 6 ++---- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) rename osu.Game.Rulesets.Taiko/Skinning/{LegacyHitTarget.cs => TaikoLegacyHitTarget.cs} (80%) rename osu.Game.Rulesets.Taiko/UI/{HitTarget.cs => TaikoHitTarget.cs} (95%) diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs index 730eed0e0f..cdd6f9ab19 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs @@ -18,8 +18,8 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning { public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] { - typeof(HitTarget), - typeof(LegacyHitTarget), + typeof(TaikoHitTarget), + typeof(TaikoLegacyHitTarget), }).ToList(); [Cached(typeof(IScrollingInfo))] diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyHitTarget.cs b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacyHitTarget.cs similarity index 80% rename from osu.Game.Rulesets.Taiko/Skinning/LegacyHitTarget.cs rename to osu.Game.Rulesets.Taiko/Skinning/TaikoLegacyHitTarget.cs index 51aea9b9ab..b80f273d24 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/LegacyHitTarget.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacyHitTarget.cs @@ -10,7 +10,7 @@ using osuTK; namespace osu.Game.Rulesets.Taiko.Skinning { - public class LegacyHitTarget : CompositeDrawable + public class TaikoLegacyHitTarget : CompositeDrawable { [BackgroundDependencyLoader] private void load(ISkinSource skin) @@ -22,7 +22,8 @@ namespace osu.Game.Rulesets.Taiko.Skinning new Sprite { Texture = skin.GetTexture("approachcircle"), - Scale = new Vector2(0.73f), + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.73f) * 0.625f, Alpha = 0.7f, Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -30,7 +31,8 @@ namespace osu.Game.Rulesets.Taiko.Skinning new Sprite { Texture = skin.GetTexture("taikobigcircle"), - Scale = new Vector2(0.7f), + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.7f) * 0.625f, Alpha = 0.5f, Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs index 6b59718173..eaa69283e4 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs @@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning case TaikoSkinComponents.HitTarget: if (GetTexture("taikobigcircle") != null) - return new LegacyHitTarget(); + return new TaikoLegacyHitTarget(); return null; } diff --git a/osu.Game.Rulesets.Taiko/UI/HitTarget.cs b/osu.Game.Rulesets.Taiko/UI/TaikoHitTarget.cs similarity index 95% rename from osu.Game.Rulesets.Taiko/UI/HitTarget.cs rename to osu.Game.Rulesets.Taiko/UI/TaikoHitTarget.cs index 88886508af..7de1593ab6 100644 --- a/osu.Game.Rulesets.Taiko/UI/HitTarget.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoHitTarget.cs @@ -13,14 +13,14 @@ namespace osu.Game.Rulesets.Taiko.UI /// /// A component that is displayed at the hit position in the taiko playfield. /// - internal class HitTarget : Container + internal class TaikoHitTarget : Container { /// /// Thickness of all drawn line pieces. /// private const float border_thickness = 2.5f; - public HitTarget() + public TaikoHitTarget() { RelativeSizeAxes = Axes.Both; @@ -41,7 +41,6 @@ namespace osu.Game.Rulesets.Taiko.UI Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, - FillMode = FillMode.Fit, Scale = new Vector2(TaikoHitObject.DEFAULT_STRONG_SIZE), Masking = true, BorderColour = Color4.White, @@ -63,7 +62,6 @@ namespace osu.Game.Rulesets.Taiko.UI Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, - FillMode = FillMode.Fit, Scale = new Vector2(TaikoHitObject.DEFAULT_SIZE), Masking = true, BorderColour = Color4.White, diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 375d9995c0..69c9003434 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -99,7 +99,7 @@ namespace osu.Game.Rulesets.Taiko.UI FillMode = FillMode.Fit, Blending = BlendingParameters.Additive, }, - HitTarget = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.HitTarget), _ => new HitTarget()) + HitTarget = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.HitTarget), _ => new TaikoHitTarget()) { Anchor = Anchor.CentreLeft, Origin = Anchor.Centre, From 8d5732aabd87662ec038358e63b630e23153605b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 23 Apr 2020 12:17:11 +0900 Subject: [PATCH 0395/1117] Make placements happen on mouse down --- .../TestSceneSliderPlacementBlueprint.cs | 17 ++++++++++++++++ .../HitCircles/HitCirclePlacementBlueprint.cs | 20 +++++++++++++------ .../Components/PathControlPointPiece.cs | 4 +++- .../Sliders/SliderPlacementBlueprint.cs | 10 +++++----- 4 files changed, 39 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderPlacementBlueprint.cs index 9fc479953e..fe9973f4d8 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderPlacementBlueprint.cs @@ -259,6 +259,23 @@ namespace osu.Game.Rulesets.Osu.Tests assertControlPointType(2, PathType.PerfectCurve); } + [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); + } + private void addMovementStep(Vector2 position) => AddStep($"move mouse to {position}", () => InputManager.MoveMouseTo(InputManager.ToScreenSpace(position))); private void addClickStep(MouseButton button) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/HitCirclePlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/HitCirclePlacementBlueprint.cs index 407f5f540e..2f400160b8 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/HitCirclePlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/HitCirclePlacementBlueprint.cs @@ -6,6 +6,7 @@ using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components; using osu.Game.Rulesets.Osu.Objects; using osuTK; +using osuTK.Input; namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles { @@ -28,16 +29,23 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles circlePiece.UpdateFrom(HitObject); } - protected override bool OnClick(ClickEvent e) + protected override bool OnMouseDown(MouseDownEvent e) { - EndPlacement(true); - return true; + if (e.Button == MouseButton.Left) + { + BeginPlacement(); + return true; + } + + return base.OnMouseDown(e); } - public override void UpdatePosition(Vector2 screenSpacePosition) + protected override void OnMouseUp(MouseUpEvent e) { - BeginPlacement(); - HitObject.Position = ToLocalSpace(screenSpacePosition); + if (e.Button == MouseButton.Left) + EndPlacement(true); } + + public override void UpdatePosition(Vector2 screenSpacePosition) => HitObject.Position = ToLocalSpace(screenSpacePosition); } } 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 fed149b5c5..d0c1eb5317 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -28,7 +28,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components public Action RequestSelection; public readonly BindableBool IsSelected = new BindableBool(); - public readonly PathControlPoint ControlPoint; private readonly Slider slider; @@ -146,6 +145,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components protected override bool OnDragStart(DragStartEvent e) { + if (RequestSelection == null) + return false; + if (e.Button == MouseButton.Left) { changeHandler?.BeginChange(); diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 9af972dbce..ac30f5a762 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -82,8 +82,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders } } - protected override bool OnClick(ClickEvent e) + protected override bool OnMouseDown(MouseDownEvent e) { + if (e.Button != MouseButton.Left) + return base.OnMouseDown(e); + switch (state) { case PlacementState.Initial: @@ -91,9 +94,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders break; case PlacementState.Body: - if (e.Button != MouseButton.Left) - break; - if (canPlaceNewControlPoint(out var lastPoint)) { // Place a new point by detatching the current cursor. @@ -111,7 +111,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders currentSegmentLength = 1; } - return true; + break; } return true; From 58bf288595a2fc16f3491c614480bd1d997d0afe Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 Apr 2020 12:17:48 +0900 Subject: [PATCH 0396/1117] Remove DrawableHit's custom sizing logic Turns out this was unnecessary and never actually being used. --- osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index 9333e5f144..fe9a89f2be 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -92,8 +92,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables // The input manager processes all input prior to us updating, so this is the perfect time // for us to remove the extra press blocking, before input is handled in the next frame pressHandledThisFrame = false; - - Size = BaseSize * Parent.RelativeChildSize; } protected override void UpdateStateTransforms(ArmedState state) From c59096a9419c3087d35137350bc480a90972d3c6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 23 Apr 2020 12:05:34 +0900 Subject: [PATCH 0397/1117] Fix note placement --- .../ManiaPlacementBlueprintTestScene.cs | 2 - .../TestSceneNotePlacementBlueprint.cs | 42 +++++++++++++++++++ .../Blueprints/ManiaPlacementBlueprint.cs | 2 +- .../Edit/Blueprints/NotePlacementBlueprint.cs | 9 ++++ 4 files changed, 52 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestScene.cs b/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestScene.cs index afde1c9521..aac77c9c1c 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestScene.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestScene.cs @@ -41,8 +41,6 @@ namespace osu.Game.Rulesets.Mania.Tests AccentColour = Color4.OrangeRed, Clock = new FramedClock(new StopwatchClock()), // No scroll }); - - AddStep("change direction", () => ((ScrollingTestContainer)HitObjectContainer).Flip()); } protected override Container CreateHitObjectContainer() => new ScrollingTestContainer(ScrollingDirection.Down) { RelativeSizeAxes = Axes.Both }; diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneNotePlacementBlueprint.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneNotePlacementBlueprint.cs index d7b539a2a0..2d97e61aa5 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneNotePlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneNotePlacementBlueprint.cs @@ -1,17 +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.Linq; +using NUnit.Framework; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Testing; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Mania.Edit.Blueprints; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects.Drawables; +using osu.Game.Rulesets.Mania.UI; 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.Tests.Visual; +using osuTK; +using osuTK.Input; namespace osu.Game.Rulesets.Mania.Tests { public class TestSceneNotePlacementBlueprint : ManiaPlacementBlueprintTestScene { + [SetUp] + public void Setup() => Schedule(() => + { + this.ChildrenOfType().ForEach(c => c.Clear()); + + ResetPlacement(); + + ((ScrollingTestContainer)HitObjectContainer).Direction = ScrollingDirection.Down; + }); + + [Test] + public void TestPlaceBeforeCurrentTimeDownwards() + { + AddStep("move mouse before current time", () => InputManager.MoveMouseTo(this.ChildrenOfType().Single().ScreenSpaceDrawQuad.BottomLeft - new Vector2(0, 10))); + + AddStep("click", () => InputManager.Click(MouseButton.Left)); + + AddAssert("note start time < 0", () => getNote().StartTime < 0); + } + + [Test] + public void TestPlaceAfterCurrentTimeDownwards() + { + AddStep("move mouse after current time", () => InputManager.MoveMouseTo(this.ChildrenOfType().Single())); + + AddStep("click", () => InputManager.Click(MouseButton.Left)); + + AddAssert("note start time > 0", () => getNote().StartTime > 0); + } + + private Note getNote() => this.ChildrenOfType().FirstOrDefault()?.HitObject; + protected override DrawableHitObject CreateHitObject(HitObject hitObject) => new DrawableNote((Note)hitObject); protected override PlacementBlueprint CreateBlueprint() => new NotePlacementBlueprint(); } diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs index 6ddf212266..f228daa5e3 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs @@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints return base.OnMouseDown(e); HitObject.Column = Column.Index; - BeginPlacement(TimeAt(e.ScreenSpaceMousePosition)); + BeginPlacement(TimeAt(e.ScreenSpaceMousePosition), true); return true; } diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/NotePlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/NotePlacementBlueprint.cs index 32c6a6fd07..fd8ef52cef 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/NotePlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/NotePlacementBlueprint.cs @@ -4,6 +4,7 @@ using osu.Framework.Graphics; using osu.Game.Rulesets.Mania.Edit.Blueprints.Components; using osu.Game.Rulesets.Mania.Objects; +using osuTK; namespace osu.Game.Rulesets.Mania.Edit.Blueprints { @@ -26,5 +27,13 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints Width = SnappedWidth; Position = SnappedMousePosition; } + + public override void UpdatePosition(Vector2 screenSpacePosition) + { + base.UpdatePosition(screenSpacePosition); + + // Continue updating the position until placement is finished on mouse up. + BeginPlacement(TimeAt(screenSpacePosition), true); + } } } From 37f7e0a7349967077f701cf8c3e263573f2f31e5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 Apr 2020 12:33:34 +0900 Subject: [PATCH 0398/1117] Restructure TaikoPlayfield for better skin support --- .../Skinning/TestSceneTaikoPlayfield.cs | 6 +- .../Skinning/TaikoLegacySkinTransformer.cs | 18 ++ .../TaikoSkinComponents.cs | 4 +- osu.Game.Rulesets.Taiko/UI/HitExplosion.cs | 2 +- .../UI/PlayfieldBackground.cs | 61 ++++++ osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 187 ++++++++---------- 6 files changed, 165 insertions(+), 113 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko/UI/PlayfieldBackground.cs diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs index cdd6f9ab19..3c7360a6bd 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs @@ -5,7 +5,9 @@ using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; +using osu.Framework.Testing; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Taiko.Skinning; using osu.Game.Rulesets.Taiko.UI; @@ -20,6 +22,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning { typeof(TaikoHitTarget), typeof(TaikoLegacyHitTarget), + typeof(PlayfieldBackground), }).ToList(); [Cached(typeof(IScrollingInfo))] @@ -33,10 +36,11 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning { AddStep("Load playfield", () => SetContents(() => new TaikoPlayfield(new ControlPointInfo()) { - Height = 0.4f, Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, })); + + AddRepeatStep("change height", () => this.ChildrenOfType().ForEach(p => p.Height = Math.Max(0.2f, (p.Height + 0.2f) % 1f)), 50); } } } diff --git a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs index eaa69283e4..919788f08a 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; using osu.Game.Audio; using osu.Game.Skinning; +using osuTK; namespace osu.Game.Rulesets.Taiko.Skinning { @@ -55,6 +56,23 @@ namespace osu.Game.Rulesets.Taiko.Skinning return new TaikoLegacyHitTarget(); return null; + + case TaikoSkinComponents.PlayfieldBackgroundRight: + if (GetTexture("taiko-bar-right") != null) + return this.GetAnimation("taiko-bar-right", false, false).With(d => + { + d.RelativeSizeAxes = Axes.Both; + d.Size = Vector2.One; + }); + + return null; + + case TaikoSkinComponents.PlayfieldBackgroundLeft: + // This is displayed inside LegacyInputDrum. It is required to be there for layout purposes (can be seen on legacy skins). + if (GetTexture("taiko-bar-right") != null) + return Drawable.Empty(); + + return null; } return source.GetDrawableComponent(component); diff --git a/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs b/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs index 775eeb4e38..60a2be7f99 100644 --- a/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs +++ b/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs @@ -11,6 +11,8 @@ namespace osu.Game.Rulesets.Taiko DrumRollBody, DrumRollTick, Swell, - HitTarget + HitTarget, + PlayfieldBackgroundLeft, + PlayfieldBackgroundRight } } diff --git a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs index 404960c26f..d4118d38b6 100644 --- a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Taiko.UI JudgedObject = judgedObject; - Anchor = Anchor.CentreLeft; + Anchor = Anchor.Centre; Origin = Anchor.Centre; RelativeSizeAxes = Axes.Both; diff --git a/osu.Game.Rulesets.Taiko/UI/PlayfieldBackground.cs b/osu.Game.Rulesets.Taiko/UI/PlayfieldBackground.cs new file mode 100644 index 0000000000..39fb6c0476 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/UI/PlayfieldBackground.cs @@ -0,0 +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.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.Graphics; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Taiko.UI +{ + public class PlayfieldBackground : CompositeDrawable + { + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Name = "Transparent playfield background"; + RelativeSizeAxes = Axes.Both; + Masking = true; + BorderColour = colours.Gray1; + + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(0.2f), + Radius = 5, + }; + + InternalChildren = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colours.Gray0, + Alpha = 0.6f + }, + new Container + { + Name = "Border", + RelativeSizeAxes = Axes.Both, + Masking = true, + MaskingSmoothness = 0, + BorderThickness = 2, + AlwaysPresent = true, + Children = new[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true + } + } + } + }; + } + } +} diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 69c9003434..4587e896ba 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -6,7 +6,6 @@ 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.ControlPoints; using osu.Game.Graphics; @@ -25,162 +24,132 @@ namespace osu.Game.Rulesets.Taiko.UI { public class TaikoPlayfield : ScrollingPlayfield { + private readonly ControlPointInfo controlPoints; + /// /// Default height of a when inside a . /// public const float DEFAULT_HEIGHT = 178; - /// - /// The offset from which the center of the hit target lies at. - /// - public const float HIT_TARGET_OFFSET = 100; - /// /// The size of the left area of the playfield. This area contains the input drum. /// - private const float left_area_size = 240; + private const float left_area_size = 180; - private readonly Container hitExplosionContainer; - private readonly Container kiaiExplosionContainer; - private readonly JudgementContainer judgementContainer; - internal readonly Drawable HitTarget; + private Container hitExplosionContainer; + private Container kiaiExplosionContainer; + private JudgementContainer judgementContainer; + internal Drawable HitTarget; - private readonly ProxyContainer topLevelHitContainer; - private readonly ProxyContainer barlineContainer; + private ProxyContainer topLevelHitContainer; + private ProxyContainer barlineContainer; + private Container rightArea; + private Container leftArea; - private readonly Container overlayBackgroundContainer; - private readonly Container backgroundContainer; - - private readonly Box overlayBackground; - private readonly Box background; + private Container hitTargetOffsetContent; public TaikoPlayfield(ControlPointInfo controlPoints) + { + this.controlPoints = controlPoints; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) { InternalChildren = new Drawable[] { - backgroundContainer = new Container - { - Name = "Transparent playfield background", - RelativeSizeAxes = Axes.Both, - Masking = true, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Colour = Color4.Black.Opacity(0.2f), - Radius = 5, - }, - Children = new Drawable[] - { - background = new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0.6f - }, - } - }, - new Container + new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.PlayfieldBackgroundRight), _ => new PlayfieldBackground()), + rightArea = new Container { Name = "Right area", RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Left = left_area_size }, + RelativePositionAxes = Axes.Both, + Masking = true, Children = new Drawable[] { new Container { Name = "Masked elements before hit objects", RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Left = HIT_TARGET_OFFSET }, - Masking = true, + FillMode = FillMode.Fit, Children = new[] { hitExplosionContainer = new Container { RelativeSizeAxes = Axes.Both, - FillMode = FillMode.Fit, Blending = BlendingParameters.Additive, }, HitTarget = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.HitTarget), _ => new TaikoHitTarget()) { - Anchor = Anchor.CentreLeft, - Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, - FillMode = FillMode.Fit } } }, - barlineContainer = new ProxyContainer + hitTargetOffsetContent = new Container { RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Left = HIT_TARGET_OFFSET } - }, - new Container - { - Name = "Hit objects", - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Left = HIT_TARGET_OFFSET }, - Masking = true, - Child = HitObjectContainer - }, - kiaiExplosionContainer = new Container - { - Name = "Kiai hit explosions", - RelativeSizeAxes = Axes.Both, - FillMode = FillMode.Fit, - Margin = new MarginPadding { Left = HIT_TARGET_OFFSET }, - Blending = BlendingParameters.Additive - }, - judgementContainer = new JudgementContainer - { - Name = "Judgements", - RelativeSizeAxes = Axes.Y, - Margin = new MarginPadding { Left = HIT_TARGET_OFFSET }, - Blending = BlendingParameters.Additive + Children = new Drawable[] + { + barlineContainer = new ProxyContainer + { + RelativeSizeAxes = Axes.Both, + }, + new Container + { + Name = "Hit objects", + RelativeSizeAxes = Axes.Both, + Child = HitObjectContainer + }, + kiaiExplosionContainer = new Container + { + Name = "Kiai hit explosions", + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit, + Blending = BlendingParameters.Additive + }, + judgementContainer = new JudgementContainer + { + Name = "Judgements", + RelativeSizeAxes = Axes.Y, + Blending = BlendingParameters.Additive + }, + } }, } }, - overlayBackgroundContainer = new Container + leftArea = new Container { Name = "Left overlay", - RelativeSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit, + BorderColour = colours.Gray0, Size = new Vector2(left_area_size, 1), Children = new Drawable[] { - overlayBackground = new Box + new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.PlayfieldBackgroundLeft), _ => new Container { RelativeSizeAxes = Axes.Both, - }, + Children = new Drawable[] + { + new Box + { + Colour = colours.Gray1, + RelativeSizeAxes = Axes.Both, + }, + new Box + { + Anchor = Anchor.TopRight, + RelativeSizeAxes = Axes.Y, + Width = 10, + Colour = Framework.Graphics.Colour.ColourInfo.GradientHorizontal(Color4.Black.Opacity(0.6f), Color4.Black.Opacity(0)), + }, + } + }), new InputDrum(controlPoints) { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Scale = new Vector2(0.9f), - Margin = new MarginPadding { Right = 20 } + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, }, - new Box - { - Anchor = Anchor.TopRight, - RelativeSizeAxes = Axes.Y, - Width = 10, - Colour = Framework.Graphics.Colour.ColourInfo.GradientHorizontal(Color4.Black.Opacity(0.6f), Color4.Black.Opacity(0)), - }, - } - }, - new Container - { - Name = "Border", - RelativeSizeAxes = Axes.Both, - Masking = true, - MaskingSmoothness = 0, - BorderThickness = 2, - AlwaysPresent = true, - Children = new[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - AlwaysPresent = true - } } }, topLevelHitContainer = new ProxyContainer @@ -191,14 +160,12 @@ namespace osu.Game.Rulesets.Taiko.UI }; } - [BackgroundDependencyLoader] - private void load(OsuColour colours) + protected override void Update() { - overlayBackgroundContainer.BorderColour = colours.Gray0; - overlayBackground.Colour = colours.Gray1; + base.Update(); - backgroundContainer.BorderColour = colours.Gray1; - background.Colour = colours.Gray0; + rightArea.Padding = new MarginPadding { Left = leftArea.DrawWidth }; + hitTargetOffsetContent.Padding = new MarginPadding { Left = HitTarget.DrawWidth / 2 }; } public override void Add(DrawableHitObject h) From 49568a3d562884e8960b8ac5d728f476de872d37 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 Apr 2020 12:19:30 +0900 Subject: [PATCH 0399/1117] Adjust input drum to work with new playfield changes --- osu.Game.Rulesets.Taiko/Skinning/LegacyInputDrum.cs | 9 +++++++++ osu.Game.Rulesets.Taiko/UI/InputDrum.cs | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyInputDrum.cs b/osu.Game.Rulesets.Taiko/Skinning/LegacyInputDrum.cs index c61e35692b..276a9d76a8 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/LegacyInputDrum.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/LegacyInputDrum.cs @@ -78,6 +78,15 @@ namespace osu.Game.Rulesets.Taiko.Skinning } } + protected override void Update() + { + base.Update(); + + // Relying on RelativeSizeAxes.Both + FillMode.Fit doesn't work due to the precise pixel layout requirements. + // This is a bit ugly but makes the non-legacy implementations a lot cleaner to implement. + Scale = new Vector2(Parent.DrawHeight / Size.Y); + } + /// /// A half-drum. Contains one centre and one rim hit. /// diff --git a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs index 422ea2f929..38026517d9 100644 --- a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs +++ b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs @@ -31,7 +31,6 @@ namespace osu.Game.Rulesets.Taiko.UI sampleMapping = new DrumSampleMapping(controlPoints); RelativeSizeAxes = Axes.Both; - FillMode = FillMode.Fit; } [BackgroundDependencyLoader] @@ -40,6 +39,8 @@ namespace osu.Game.Rulesets.Taiko.UI Child = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.InputDrum), _ => new Container { RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit, + Scale = new Vector2(0.9f), Children = new Drawable[] { new TaikoHalfDrum(false) From 2e022fbcb5e47519e57a3c973cbb58295e636788 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 Apr 2020 12:47:57 +0900 Subject: [PATCH 0400/1117] Add comment about padding update computation --- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 4587e896ba..4e0ef64ce1 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -164,6 +164,8 @@ namespace osu.Game.Rulesets.Taiko.UI { base.Update(); + // Padding is required to be updated for elements which are based on "absolute" X sized elements. + // This is basically allowing for correct alignment as relative pieces move around them. rightArea.Padding = new MarginPadding { Left = leftArea.DrawWidth }; hitTargetOffsetContent.Padding = new MarginPadding { Left = HitTarget.DrawWidth / 2 }; } From 22d2607ff58eb51bfa38d04e08a097011caabf8e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 23 Apr 2020 12:53:09 +0900 Subject: [PATCH 0401/1117] Only commit if placement is active --- .../Edit/Blueprints/NotePlacementBlueprint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/NotePlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/NotePlacementBlueprint.cs index fd8ef52cef..888ce695c2 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/NotePlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/NotePlacementBlueprint.cs @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints base.UpdatePosition(screenSpacePosition); // Continue updating the position until placement is finished on mouse up. - BeginPlacement(TimeAt(screenSpacePosition), true); + BeginPlacement(TimeAt(screenSpacePosition), PlacementActive); } } } From 4f0b5a34d3a1325b77d9901cfd43d6af55a0e29c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 23 Apr 2020 12:53:23 +0900 Subject: [PATCH 0402/1117] Fix hold note placement body sized incorrectly --- .../Objects/Drawables/DrawableHoldNote.cs | 5 ++++- .../Objects/Drawables/Pieces/DefaultBodyPiece.cs | 1 - 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index a9ef661aaa..d63c0326a7 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs @@ -51,7 +51,10 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables AddRangeInternal(new[] { - bodyPiece = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HoldNoteBody, hitObject.Column), _ => new DefaultBodyPiece()) + bodyPiece = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HoldNoteBody, hitObject.Column), _ => new DefaultBodyPiece + { + RelativeSizeAxes = Axes.Both + }) { RelativeSizeAxes = Axes.X }, diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/DefaultBodyPiece.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/DefaultBodyPiece.cs index 0ee0a14df3..bc4a095395 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/DefaultBodyPiece.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/DefaultBodyPiece.cs @@ -34,7 +34,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces public DefaultBodyPiece() { - RelativeSizeAxes = Axes.Both; Blending = BlendingParameters.Additive; AddLayout(subtractionCache); From 61d2580e1c847cde16cfb4bb023dcc978730e636 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 Apr 2020 13:07:30 +0900 Subject: [PATCH 0403/1117] Fix gap to left of InputDrum on legacy skins --- .../Skinning/LegacyInputDrum.cs | 47 ++++++++++--------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyInputDrum.cs b/osu.Game.Rulesets.Taiko/Skinning/LegacyInputDrum.cs index 276a9d76a8..1e8cade01d 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/LegacyInputDrum.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/LegacyInputDrum.cs @@ -20,36 +20,41 @@ namespace osu.Game.Rulesets.Taiko.Skinning { private LegacyHalfDrum left; private LegacyHalfDrum right; + private Container content; public LegacyInputDrum() { - Size = new Vector2(180, 200); + RelativeSizeAxes = Axes.Both; } [BackgroundDependencyLoader] private void load(ISkinSource skin) { - Children = new Drawable[] + Child = content = new Container { - new Sprite + Size = new Vector2(180, 200), + Children = new Drawable[] { - Texture = skin.GetTexture("taiko-bar-left") - }, - left = new LegacyHalfDrum(false) - { - Name = "Left Half", - RelativeSizeAxes = Axes.Both, - RimAction = TaikoAction.LeftRim, - CentreAction = TaikoAction.LeftCentre - }, - right = new LegacyHalfDrum(true) - { - Name = "Right Half", - RelativeSizeAxes = Axes.Both, - Origin = Anchor.TopRight, - Scale = new Vector2(-1, 1), - RimAction = TaikoAction.RightRim, - CentreAction = TaikoAction.RightCentre + new Sprite + { + Texture = skin.GetTexture("taiko-bar-left") + }, + left = new LegacyHalfDrum(false) + { + Name = "Left Half", + RelativeSizeAxes = Axes.Both, + RimAction = TaikoAction.LeftRim, + CentreAction = TaikoAction.LeftCentre + }, + right = new LegacyHalfDrum(true) + { + Name = "Right Half", + RelativeSizeAxes = Axes.Both, + Origin = Anchor.TopRight, + Scale = new Vector2(-1, 1), + RimAction = TaikoAction.RightRim, + CentreAction = TaikoAction.RightCentre + } } }; @@ -84,7 +89,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning // Relying on RelativeSizeAxes.Both + FillMode.Fit doesn't work due to the precise pixel layout requirements. // This is a bit ugly but makes the non-legacy implementations a lot cleaner to implement. - Scale = new Vector2(Parent.DrawHeight / Size.Y); + content.Scale = new Vector2(DrawHeight / content.Size.Y); } /// From 4032d669596a2031eca223c37bc2cddb9a102725 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 Apr 2020 13:17:46 +0900 Subject: [PATCH 0404/1117] Apply same legacy scale adjust logic to TaikoLegacyHitTarget --- .../Skinning/TaikoLegacyHitTarget.cs | 52 ++++++++++++------- 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacyHitTarget.cs b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacyHitTarget.cs index b80f273d24..7c1e65f569 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacyHitTarget.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacyHitTarget.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; +using osu.Game.Rulesets.Taiko.UI; using osu.Game.Skinning; using osuTK; @@ -12,32 +13,47 @@ namespace osu.Game.Rulesets.Taiko.Skinning { public class TaikoLegacyHitTarget : CompositeDrawable { + private Container content; + [BackgroundDependencyLoader] private void load(ISkinSource skin) { RelativeSizeAxes = Axes.Both; - InternalChildren = new Drawable[] + InternalChild = content = new Container { - new Sprite + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Children = new Drawable[] { - Texture = skin.GetTexture("approachcircle"), - RelativeSizeAxes = Axes.Both, - Size = new Vector2(0.73f) * 0.625f, - Alpha = 0.7f, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }, - new Sprite - { - Texture = skin.GetTexture("taikobigcircle"), - RelativeSizeAxes = Axes.Both, - Size = new Vector2(0.7f) * 0.625f, - Alpha = 0.5f, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }, + new Sprite + { + Texture = skin.GetTexture("approachcircle"), + Scale = new Vector2(0.73f), + Alpha = 0.7f, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + new Sprite + { + Texture = skin.GetTexture("taikobigcircle"), + Scale = new Vector2(0.7f), + Alpha = 0.5f, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + } }; } + + protected override void Update() + { + base.Update(); + + // Relying on RelativeSizeAxes.Both + FillMode.Fit doesn't work due to the precise pixel layout requirements. + // This is a bit ugly but makes the non-legacy implementations a lot cleaner to implement. + content.Scale = new Vector2(DrawHeight / TaikoPlayfield.DEFAULT_HEIGHT); + } } } From 559487b20583fa087760f0410da44d8d35927892 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 Apr 2020 13:23:49 +0900 Subject: [PATCH 0405/1117] Move playfield background implementation to its own file --- .../Skinning/TestSceneTaikoPlayfield.cs | 2 +- .../Skinning/TaikoLegacySkinTransformer.cs | 2 + .../UI/PlayfieldBackgroundLeft.cs | 37 +++++++++++++++++++ ...kground.cs => PlayfieldBackgroundRight.cs} | 2 +- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 25 +------------ 5 files changed, 43 insertions(+), 25 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko/UI/PlayfieldBackgroundLeft.cs rename osu.Game.Rulesets.Taiko/UI/{PlayfieldBackground.cs => PlayfieldBackgroundRight.cs} (96%) diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs index 3c7360a6bd..16b3c036a3 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning { typeof(TaikoHitTarget), typeof(TaikoLegacyHitTarget), - typeof(PlayfieldBackground), + typeof(PlayfieldBackgroundRight), }).ToList(); [Cached(typeof(IScrollingInfo))] diff --git a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs index 919788f08a..15bbd32eb1 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs @@ -59,11 +59,13 @@ namespace osu.Game.Rulesets.Taiko.Skinning case TaikoSkinComponents.PlayfieldBackgroundRight: if (GetTexture("taiko-bar-right") != null) + { return this.GetAnimation("taiko-bar-right", false, false).With(d => { d.RelativeSizeAxes = Axes.Both; d.Size = Vector2.One; }); + } return null; diff --git a/osu.Game.Rulesets.Taiko/UI/PlayfieldBackgroundLeft.cs b/osu.Game.Rulesets.Taiko/UI/PlayfieldBackgroundLeft.cs new file mode 100644 index 0000000000..2a8890a95d --- /dev/null +++ b/osu.Game.Rulesets.Taiko/UI/PlayfieldBackgroundLeft.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 osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Taiko.UI +{ + internal class PlayfieldBackgroundLeft : CompositeDrawable + { + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + RelativeSizeAxes = Axes.Both; + InternalChildren = new Drawable[] + { + new Box + { + Colour = colours.Gray1, + RelativeSizeAxes = Axes.Both, + }, + new Box + { + Anchor = Anchor.TopRight, + RelativeSizeAxes = Axes.Y, + Width = 10, + Colour = Framework.Graphics.Colour.ColourInfo.GradientHorizontal(Color4.Black.Opacity(0.6f), Color4.Black.Opacity(0)), + }, + }; + } + } +} diff --git a/osu.Game.Rulesets.Taiko/UI/PlayfieldBackground.cs b/osu.Game.Rulesets.Taiko/UI/PlayfieldBackgroundRight.cs similarity index 96% rename from osu.Game.Rulesets.Taiko/UI/PlayfieldBackground.cs rename to osu.Game.Rulesets.Taiko/UI/PlayfieldBackgroundRight.cs index 39fb6c0476..44bfdacf37 100644 --- a/osu.Game.Rulesets.Taiko/UI/PlayfieldBackground.cs +++ b/osu.Game.Rulesets.Taiko/UI/PlayfieldBackgroundRight.cs @@ -12,7 +12,7 @@ using osuTK.Graphics; namespace osu.Game.Rulesets.Taiko.UI { - public class PlayfieldBackground : CompositeDrawable + public class PlayfieldBackgroundRight : CompositeDrawable { [BackgroundDependencyLoader] private void load(OsuColour colours) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 4e0ef64ce1..1e8d37e3e5 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -3,10 +3,8 @@ 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.Shapes; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; using osu.Game.Rulesets.Objects.Drawables; @@ -18,7 +16,6 @@ using osu.Game.Rulesets.Taiko.Judgements; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Skinning; using osuTK; -using osuTK.Graphics; namespace osu.Game.Rulesets.Taiko.UI { @@ -58,7 +55,7 @@ namespace osu.Game.Rulesets.Taiko.UI { InternalChildren = new Drawable[] { - new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.PlayfieldBackgroundRight), _ => new PlayfieldBackground()), + new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.PlayfieldBackgroundRight), _ => new PlayfieldBackgroundRight()), rightArea = new Container { Name = "Right area", @@ -126,25 +123,7 @@ namespace osu.Game.Rulesets.Taiko.UI Size = new Vector2(left_area_size, 1), Children = new Drawable[] { - new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.PlayfieldBackgroundLeft), _ => new Container - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - new Box - { - Colour = colours.Gray1, - RelativeSizeAxes = Axes.Both, - }, - new Box - { - Anchor = Anchor.TopRight, - RelativeSizeAxes = Axes.Y, - Width = 10, - Colour = Framework.Graphics.Colour.ColourInfo.GradientHorizontal(Color4.Black.Opacity(0.6f), Color4.Black.Opacity(0)), - }, - } - }), + new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.PlayfieldBackgroundLeft), _ => new PlayfieldBackgroundLeft()), new InputDrum(controlPoints) { Anchor = Anchor.CentreLeft, From 12c235027df835b189669ade2313f8128b4d31ea Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 23 Apr 2020 13:28:27 +0900 Subject: [PATCH 0406/1117] Remove stale file --- .../Edit/Masks/ManiaSelectionBlueprint.cs | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 osu.Game.Rulesets.Mania/Edit/Masks/ManiaSelectionBlueprint.cs diff --git a/osu.Game.Rulesets.Mania/Edit/Masks/ManiaSelectionBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Masks/ManiaSelectionBlueprint.cs deleted file mode 100644 index 433db79ae0..0000000000 --- a/osu.Game.Rulesets.Mania/Edit/Masks/ManiaSelectionBlueprint.cs +++ /dev/null @@ -1,18 +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.Game.Rulesets.Edit; -using osu.Game.Rulesets.Objects.Drawables; - -namespace osu.Game.Rulesets.Mania.Edit.Masks -{ - public abstract class ManiaSelectionBlueprint : OverlaySelectionBlueprint - { - protected ManiaSelectionBlueprint(DrawableHitObject drawableObject) - : base(drawableObject) - { - RelativeSizeAxes = Axes.None; - } - } -} From f804be25d13e785dd7a0152f7b9b7c79d93d3138 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 Apr 2020 13:36:33 +0900 Subject: [PATCH 0407/1117] Remove incorrect area sizing (now using fillmode / relative instead) --- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 1e8d37e3e5..8402ebb4c4 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -15,7 +15,6 @@ using osu.Game.Rulesets.Taiko.Objects.Drawables; using osu.Game.Rulesets.Taiko.Judgements; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Skinning; -using osuTK; namespace osu.Game.Rulesets.Taiko.UI { @@ -28,11 +27,6 @@ namespace osu.Game.Rulesets.Taiko.UI /// public const float DEFAULT_HEIGHT = 178; - /// - /// The size of the left area of the playfield. This area contains the input drum. - /// - private const float left_area_size = 180; - private Container hitExplosionContainer; private Container kiaiExplosionContainer; private JudgementContainer judgementContainer; @@ -120,7 +114,6 @@ namespace osu.Game.Rulesets.Taiko.UI RelativeSizeAxes = Axes.Both, FillMode = FillMode.Fit, BorderColour = colours.Gray0, - Size = new Vector2(left_area_size, 1), Children = new Drawable[] { new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.PlayfieldBackgroundLeft), _ => new PlayfieldBackgroundLeft()), From b4e1ad81d07d89f4ea9b62dc684c1562629cb472 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 Apr 2020 13:48:08 +0900 Subject: [PATCH 0408/1117] Fix alignment of right half of legacy input drum --- osu.Game.Rulesets.Taiko/Skinning/LegacyInputDrum.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyInputDrum.cs b/osu.Game.Rulesets.Taiko/Skinning/LegacyInputDrum.cs index 1e8cade01d..81d645e294 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/LegacyInputDrum.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/LegacyInputDrum.cs @@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning const float ratio = 1.6f; // because the right half is flipped, we need to position using width - position to get the true "topleft" origin position - float negativeScaleAdjust = Width / ratio; + float negativeScaleAdjust = content.Width / ratio; if (skin.GetConfig(LegacySkinConfiguration.LegacySetting.Version)?.Value >= 2.1m) { From 2a1cc35541f46c8bc36d8447fddce0d7c37589de Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 Apr 2020 14:01:50 +0900 Subject: [PATCH 0409/1117] Add test scene --- .../Skinning/TestSceneDrawableBarLine.cs | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableBarLine.cs diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableBarLine.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableBarLine.cs new file mode 100644 index 0000000000..a1a5c8c836 --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableBarLine.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. + +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Taiko.Objects.Drawables; +using osu.Game.Rulesets.UI.Scrolling; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Taiko.Tests.Skinning +{ + [TestFixture] + public class TestSceneDrawableBarLine : TaikoSkinnableTestScene + { + public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] + { + typeof(DrawableBarLine), + }).ToList(); + + [Cached(typeof(IScrollingInfo))] + private ScrollingTestContainer.TestScrollingInfo info = new ScrollingTestContainer.TestScrollingInfo + { + Direction = { Value = ScrollingDirection.Left }, + TimeRange = { Value = 5000 }, + }; + + [BackgroundDependencyLoader] + private void load() + { + AddStep("Bar line ", () => SetContents(() => + { + var hoc = new ScrollingHitObjectContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Height = 0.8f + }; + + hoc.Add(new DrawableBarLine(createBarLineAtCurrentTime()) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }); + + return hoc; + })); + + AddStep("Bar line (major)", () => SetContents(() => + { + var hoc = new ScrollingHitObjectContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Height = 0.8f + }; + + hoc.Add(new DrawableBarLineMajor(createBarLineAtCurrentTime(true)) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }); + + return hoc; + })); + } + + private BarLine createBarLineAtCurrentTime(bool major = false) + { + var drumroll = new BarLine + { + Major = major, + StartTime = Time.Current + 1000, + }; + + var cpi = new ControlPointInfo(); + cpi.Add(0, new TimingControlPoint { BeatLength = 500 }); + + drumroll.ApplyDefaults(cpi, new BeatmapDifficulty()); + + return drumroll; + } + } +} From 12f156dcecdb34228c7cb4209b09fbfc00d79821 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 Apr 2020 14:32:48 +0900 Subject: [PATCH 0410/1117] Add taiko barline skinning support --- .../Objects/Drawables/DrawableBarLine.cs | 13 +++++---- .../Objects/Drawables/DrawableBarLineMajor.cs | 2 +- .../Skinning/LegacyBarLine.cs | 27 +++++++++++++++++++ .../Skinning/TaikoLegacySkinTransformer.cs | 6 +++++ .../TaikoSkinComponents.cs | 3 ++- 5 files changed, 44 insertions(+), 7 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko/Skinning/LegacyBarLine.cs diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs index e9caabbcc8..1e08e921a6 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs @@ -6,6 +6,7 @@ using osu.Framework.Graphics.Shapes; using osu.Game.Rulesets.Objects; using osuTK; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Skinning; namespace osu.Game.Rulesets.Taiko.Objects.Drawables { @@ -27,7 +28,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables /// /// The visual line tracker. /// - protected Box Tracker; + protected SkinnableDrawable Line; /// /// The bar line. @@ -45,13 +46,15 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables RelativeSizeAxes = Axes.Y; Width = tracker_width; - AddInternal(Tracker = new Box + AddInternal(Line = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.BarLine), _ => new Box + { + RelativeSizeAxes = Axes.Both, + EdgeSmoothness = new Vector2(0.5f, 0), + }) { Anchor = Anchor.Centre, Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - EdgeSmoothness = new Vector2(0.5f, 0), - Alpha = 0.75f + Alpha = 0.75f, }); } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLineMajor.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLineMajor.cs index 4d3a1a3f8a..62aab3524b 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLineMajor.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLineMajor.cs @@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables } }); - Tracker.Alpha = 1f; + Line.Alpha = 1f; } protected override void LoadComplete() diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyBarLine.cs b/osu.Game.Rulesets.Taiko/Skinning/LegacyBarLine.cs new file mode 100644 index 0000000000..7d08a21ab1 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Skinning/LegacyBarLine.cs @@ -0,0 +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 osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Game.Skinning; +using osuTK; + +namespace osu.Game.Rulesets.Taiko.Skinning +{ + public class LegacyBarLine : Sprite + { + [BackgroundDependencyLoader] + private void load(ISkinSource skin) + { + Texture = skin.GetTexture("taiko-barline"); + + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + RelativeSizeAxes = Axes.Both; + Size = new Vector2(1, 0.88f); + FillMode = FillMode.Fill; + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs index 15bbd32eb1..447d6ae455 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs @@ -75,6 +75,12 @@ namespace osu.Game.Rulesets.Taiko.Skinning return Drawable.Empty(); return null; + + case TaikoSkinComponents.BarLine: + if (GetTexture("taiko-barline") != null) + return new LegacyBarLine(); + + return null; } return source.GetDrawableComponent(component); diff --git a/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs b/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs index 60a2be7f99..a90ce608b2 100644 --- a/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs +++ b/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs @@ -13,6 +13,7 @@ namespace osu.Game.Rulesets.Taiko Swell, HitTarget, PlayfieldBackgroundLeft, - PlayfieldBackgroundRight + PlayfieldBackgroundRight, + BarLine } } From 8f31846defad9ea469b4aedd5970355177c20d69 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 Apr 2020 14:39:34 +0900 Subject: [PATCH 0411/1117] Add playfield background to test scene to better understand bounds --- .../Skinning/TestSceneDrawableBarLine.cs | 32 +++++++++++++++---- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableBarLine.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableBarLine.cs index a1a5c8c836..e72f42a0b4 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableBarLine.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableBarLine.cs @@ -7,10 +7,12 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Objects.Drawables; +using osu.Game.Rulesets.Taiko.UI; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Tests.Visual; @@ -36,11 +38,19 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning { AddStep("Bar line ", () => SetContents(() => { - var hoc = new ScrollingHitObjectContainer + ScrollingHitObjectContainer hoc; + + var cont = new Container { + RelativeSizeAxes = Axes.Both, + Height = 0.8f, Anchor = Anchor.Centre, Origin = Anchor.Centre, - Height = 0.8f + Children = new Drawable[] + { + new TaikoPlayfield(new ControlPointInfo()), + hoc = new ScrollingHitObjectContainer() + } }; hoc.Add(new DrawableBarLine(createBarLineAtCurrentTime()) @@ -49,16 +59,24 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning Origin = Anchor.Centre, }); - return hoc; + return cont; })); AddStep("Bar line (major)", () => SetContents(() => { - var hoc = new ScrollingHitObjectContainer + ScrollingHitObjectContainer hoc; + + var cont = new Container { + RelativeSizeAxes = Axes.Both, + Height = 0.8f, Anchor = Anchor.Centre, Origin = Anchor.Centre, - Height = 0.8f + Children = new Drawable[] + { + new TaikoPlayfield(new ControlPointInfo()), + hoc = new ScrollingHitObjectContainer() + } }; hoc.Add(new DrawableBarLineMajor(createBarLineAtCurrentTime(true)) @@ -67,7 +85,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning Origin = Anchor.Centre, }); - return hoc; + return cont; })); } @@ -76,7 +94,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning var drumroll = new BarLine { Major = major, - StartTime = Time.Current + 1000, + StartTime = Time.Current + 2000, }; var cpi = new ControlPointInfo(); From ab93c819b54f930211c507f28ea23b3ae9ca6cfa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 Apr 2020 15:26:03 +0900 Subject: [PATCH 0412/1117] Add metric right background element --- .../metrics-skin/taiko-bar-right@2x.png | Bin 0 -> 7830 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/metrics-skin/taiko-bar-right@2x.png diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/metrics-skin/taiko-bar-right@2x.png b/osu.Game.Rulesets.Taiko.Tests/Resources/metrics-skin/taiko-bar-right@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..5ca8a40d88a13e447d26cd8c0e777173d8e60445 GIT binary patch literal 7830 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 Of{?|_1M@zM+V&ruzN7m9 literal 0 HcmV?d00001 From 712331a2fe524fb6a3c7a8d77a8095d50d7c83d2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 Apr 2020 15:26:20 +0900 Subject: [PATCH 0413/1117] Add metric barline element --- .../Resources/metrics-skin/taiko-barline@2x.png | Bin 0 -> 1551 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/metrics-skin/taiko-barline@2x.png diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/metrics-skin/taiko-barline@2x.png b/osu.Game.Rulesets.Taiko.Tests/Resources/metrics-skin/taiko-barline@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..3e44f3309585d83ce100c59f08e880582b4feb70 GIT binary patch literal 1551 zcmbVMU5MON6wZ{kw7awwY(eVFC539K`JE)YxozB?o!NE*W;@Idbc)#aZgOWP&LlTY z?sjIkAY}zbL=i1mw)!H1|5kh|XzBV=MQ9PhC&357H&+pT5E1cae!{NQzg`HrKi~b% z`OZD(JX4uFzH4m%7{_tD>~g8f{yR7>cY4QmHoJE9^jGY+Gb*1-IBxvj^viLpuiVFR z+xGjlI;}g8K$ip}^2h=fdqKp|9Cvi87a_NeDOkWwKeYHCKf1vK-?R9Wx+6PL5x4yE za*XGf=W6b9+ciCY>S1uS2N^+tDFVHq6DF`{@qJ#%=IOS?gFb||ExwQ%1a+qZiX_HB z7Y)Hxv`H{wii)AhdE+6V%8Dt;lai7bWCh9w)F;4j@XT85HDI-LY-o$EEWSl)1SP54 z?TX#JNaCiXm?q;;B~=v|LP(ZEih4qr92hZ_aN@>(M12y1lo2hEMQZWP(}9E_%4ox6 z7$z1lsfQv-5#>};ALzLmE?SH`edC@h;SLUPNE3!tGHlc$lq4_npld$CJ(Ye!XoLAm>Fi?4u+JO@3uOj&em@wpRrbW#V!?Ut#8&}JTi7s-n;eFd&ciS@k0B%Kc0J_?4qB( z*TISbbBCY4^~B8d&Np-Cetmq}Ufa6&_8Z*%mSbms;PUc8@%f9RvG&gN?(Np^H=k6l ze|tfB^=o~-@X}2>{%rM=*J_o*wteBD`X%?&B}MrB@+uv7etTPg%f9f%>j%$#ywcvY s=a&z1@BMr=_vr4Y_SNCqmUoYQcxCnFFCUw_oVILxcCPgP%-NMc0h$5s%m4rY literal 0 HcmV?d00001 From e3a3144236fe6e2790dafb5198248392b9227145 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 23 Apr 2020 11:07:55 +0300 Subject: [PATCH 0414/1117] Rename editor tests namespace from "Editor" to "Editing" --- osu.Game.Tests/{Editor => Editing}/EditorChangeHandlerTest.cs | 2 +- .../{Editor => Editing}/LegacyEditorBeatmapPatcherTest.cs | 2 +- .../TestSceneHitObjectComposerDistanceSnapping.cs | 2 +- .../Visual/{Editor => Editing}/TestSceneBeatDivisorControl.cs | 2 +- .../Visual/{Editor => Editing}/TestSceneComposeScreen.cs | 2 +- .../Visual/{Editor => Editing}/TestSceneDistanceSnapGrid.cs | 2 +- .../Visual/{Editor => Editing}/TestSceneEditorChangeStates.cs | 4 ++-- .../{Editor => Editing}/TestSceneEditorComposeRadioButtons.cs | 2 +- .../Visual/{Editor => Editing}/TestSceneEditorMenuBar.cs | 2 +- .../Visual/{Editor => Editing}/TestSceneEditorSeekSnapping.cs | 2 +- .../{Editor => Editing}/TestSceneEditorSummaryTimeline.cs | 2 +- .../Visual/{Editor => Editing}/TestSceneHitObjectComposer.cs | 2 +- .../Visual/{Editor => Editing}/TestScenePlaybackControl.cs | 2 +- .../TestSceneTimelineBlueprintContainer.cs | 2 +- .../{Editor => Editing}/TestSceneTimelineTickDisplay.cs | 2 +- .../Visual/{Editor => Editing}/TestSceneTimingScreen.cs | 2 +- .../Visual/{Editor => Editing}/TestSceneWaveform.cs | 2 +- .../{Editor => Editing}/TestSceneZoomableScrollContainer.cs | 2 +- .../Visual/{Editor => Editing}/TimelineTestScene.cs | 2 +- 19 files changed, 20 insertions(+), 20 deletions(-) rename osu.Game.Tests/{Editor => Editing}/EditorChangeHandlerTest.cs (98%) rename osu.Game.Tests/{Editor => Editing}/LegacyEditorBeatmapPatcherTest.cs (99%) rename osu.Game.Tests/{Editor => Editing}/TestSceneHitObjectComposerDistanceSnapping.cs (99%) rename osu.Game.Tests/Visual/{Editor => Editing}/TestSceneBeatDivisorControl.cs (98%) rename osu.Game.Tests/Visual/{Editor => Editing}/TestSceneComposeScreen.cs (96%) rename osu.Game.Tests/Visual/{Editor => Editing}/TestSceneDistanceSnapGrid.cs (99%) rename osu.Game.Tests/Visual/{Editor => Editing}/TestSceneEditorChangeStates.cs (98%) rename osu.Game.Tests/Visual/{Editor => Editing}/TestSceneEditorComposeRadioButtons.cs (97%) rename osu.Game.Tests/Visual/{Editor => Editing}/TestSceneEditorMenuBar.cs (99%) rename osu.Game.Tests/Visual/{Editor => Editing}/TestSceneEditorSeekSnapping.cs (99%) rename osu.Game.Tests/Visual/{Editor => Editing}/TestSceneEditorSummaryTimeline.cs (95%) rename osu.Game.Tests/Visual/{Editor => Editing}/TestSceneHitObjectComposer.cs (98%) rename osu.Game.Tests/Visual/{Editor => Editing}/TestScenePlaybackControl.cs (96%) rename osu.Game.Tests/Visual/{Editor => Editing}/TestSceneTimelineBlueprintContainer.cs (95%) rename osu.Game.Tests/Visual/{Editor => Editing}/TestSceneTimelineTickDisplay.cs (95%) rename osu.Game.Tests/Visual/{Editor => Editing}/TestSceneTimingScreen.cs (96%) rename osu.Game.Tests/Visual/{Editor => Editing}/TestSceneWaveform.cs (98%) rename osu.Game.Tests/Visual/{Editor => Editing}/TestSceneZoomableScrollContainer.cs (99%) rename osu.Game.Tests/Visual/{Editor => Editing}/TimelineTestScene.cs (99%) diff --git a/osu.Game.Tests/Editor/EditorChangeHandlerTest.cs b/osu.Game.Tests/Editing/EditorChangeHandlerTest.cs similarity index 98% rename from osu.Game.Tests/Editor/EditorChangeHandlerTest.cs rename to osu.Game.Tests/Editing/EditorChangeHandlerTest.cs index 9613f250c4..feda1ae0e9 100644 --- a/osu.Game.Tests/Editor/EditorChangeHandlerTest.cs +++ b/osu.Game.Tests/Editing/EditorChangeHandlerTest.cs @@ -5,7 +5,7 @@ using NUnit.Framework; using osu.Game.Beatmaps; using osu.Game.Screens.Edit; -namespace osu.Game.Tests.Editor +namespace osu.Game.Tests.Editing { [TestFixture] public class EditorChangeHandlerTest diff --git a/osu.Game.Tests/Editor/LegacyEditorBeatmapPatcherTest.cs b/osu.Game.Tests/Editing/LegacyEditorBeatmapPatcherTest.cs similarity index 99% rename from osu.Game.Tests/Editor/LegacyEditorBeatmapPatcherTest.cs rename to osu.Game.Tests/Editing/LegacyEditorBeatmapPatcherTest.cs index c24418d688..a3ab677d96 100644 --- a/osu.Game.Tests/Editor/LegacyEditorBeatmapPatcherTest.cs +++ b/osu.Game.Tests/Editing/LegacyEditorBeatmapPatcherTest.cs @@ -17,7 +17,7 @@ using osu.Game.Screens.Edit; using osuTK; using Decoder = osu.Game.Beatmaps.Formats.Decoder; -namespace osu.Game.Tests.Editor +namespace osu.Game.Tests.Editing { [TestFixture] public class LegacyEditorBeatmapPatcherTest diff --git a/osu.Game.Tests/Editor/TestSceneHitObjectComposerDistanceSnapping.cs b/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs similarity index 99% rename from osu.Game.Tests/Editor/TestSceneHitObjectComposerDistanceSnapping.cs rename to osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs index 3cb5909ba9..168ec0f09d 100644 --- a/osu.Game.Tests/Editor/TestSceneHitObjectComposerDistanceSnapping.cs +++ b/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs @@ -14,7 +14,7 @@ using osu.Game.Rulesets.Osu.Edit; using osu.Game.Screens.Edit; using osu.Game.Tests.Visual; -namespace osu.Game.Tests.Editor +namespace osu.Game.Tests.Editing { [HeadlessTest] public class TestSceneHitObjectComposerDistanceSnapping : EditorClockTestScene diff --git a/osu.Game.Tests/Visual/Editor/TestSceneBeatDivisorControl.cs b/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs similarity index 98% rename from osu.Game.Tests/Visual/Editor/TestSceneBeatDivisorControl.cs rename to osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs index fd7a5980f3..f6e69fd8bf 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneBeatDivisorControl.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs @@ -14,7 +14,7 @@ using osu.Game.Screens.Edit.Compose.Components; using osuTK; using osuTK.Input; -namespace osu.Game.Tests.Visual.Editor +namespace osu.Game.Tests.Visual.Editing { public class TestSceneBeatDivisorControl : OsuManualInputManagerTestScene { diff --git a/osu.Game.Tests/Visual/Editor/TestSceneComposeScreen.cs b/osu.Game.Tests/Visual/Editing/TestSceneComposeScreen.cs similarity index 96% rename from osu.Game.Tests/Visual/Editor/TestSceneComposeScreen.cs rename to osu.Game.Tests/Visual/Editing/TestSceneComposeScreen.cs index a8830824c0..6f5655006e 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneComposeScreen.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneComposeScreen.cs @@ -9,7 +9,7 @@ using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Compose; -namespace osu.Game.Tests.Visual.Editor +namespace osu.Game.Tests.Visual.Editing { [TestFixture] public class TestSceneComposeScreen : EditorClockTestScene diff --git a/osu.Game.Tests/Visual/Editor/TestSceneDistanceSnapGrid.cs b/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs similarity index 99% rename from osu.Game.Tests/Visual/Editor/TestSceneDistanceSnapGrid.cs rename to osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs index f49256a633..417d16fdb0 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneDistanceSnapGrid.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs @@ -13,7 +13,7 @@ using osu.Game.Screens.Edit.Compose.Components; using osuTK; using osuTK.Graphics; -namespace osu.Game.Tests.Visual.Editor +namespace osu.Game.Tests.Visual.Editing { public class TestSceneDistanceSnapGrid : EditorClockTestScene { diff --git a/osu.Game.Tests/Visual/Editor/TestSceneEditorChangeStates.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorChangeStates.cs similarity index 98% rename from osu.Game.Tests/Visual/Editor/TestSceneEditorChangeStates.cs rename to osu.Game.Tests/Visual/Editing/TestSceneEditorChangeStates.cs index efc2a6f552..d85bf15d52 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneEditorChangeStates.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorChangeStates.cs @@ -11,7 +11,7 @@ using osu.Game.Rulesets.Osu.Objects; using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Compose.Components.Timeline; -namespace osu.Game.Tests.Visual.Editor +namespace osu.Game.Tests.Visual.Editing { public class TestSceneEditorChangeStates : ScreenTestScene { @@ -162,7 +162,7 @@ namespace osu.Game.Tests.Visual.Editor private void addRedoSteps() => AddStep("redo", () => editor.Redo()); - private class TestEditor : Screens.Edit.Editor + private class TestEditor : Editor { public new void Undo() => base.Undo(); diff --git a/osu.Game.Tests/Visual/Editor/TestSceneEditorComposeRadioButtons.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorComposeRadioButtons.cs similarity index 97% rename from osu.Game.Tests/Visual/Editor/TestSceneEditorComposeRadioButtons.cs rename to osu.Game.Tests/Visual/Editing/TestSceneEditorComposeRadioButtons.cs index 1709067d5d..2deeaef1f6 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneEditorComposeRadioButtons.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorComposeRadioButtons.cs @@ -7,7 +7,7 @@ using NUnit.Framework; using osu.Framework.Graphics; using osu.Game.Screens.Edit.Components.RadioButtons; -namespace osu.Game.Tests.Visual.Editor +namespace osu.Game.Tests.Visual.Editing { [TestFixture] public class TestSceneEditorComposeRadioButtons : OsuTestScene diff --git a/osu.Game.Tests/Visual/Editor/TestSceneEditorMenuBar.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorMenuBar.cs similarity index 99% rename from osu.Game.Tests/Visual/Editor/TestSceneEditorMenuBar.cs rename to osu.Game.Tests/Visual/Editing/TestSceneEditorMenuBar.cs index 53c2d62067..2cbdacb61c 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneEditorMenuBar.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorMenuBar.cs @@ -10,7 +10,7 @@ using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics.UserInterface; using osu.Game.Screens.Edit.Components.Menus; -namespace osu.Game.Tests.Visual.Editor +namespace osu.Game.Tests.Visual.Editing { [TestFixture] public class TestSceneEditorMenuBar : OsuTestScene diff --git a/osu.Game.Tests/Visual/Editor/TestSceneEditorSeekSnapping.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorSeekSnapping.cs similarity index 99% rename from osu.Game.Tests/Visual/Editor/TestSceneEditorSeekSnapping.cs rename to osu.Game.Tests/Visual/Editing/TestSceneEditorSeekSnapping.cs index 3118e0cabe..41d1459103 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneEditorSeekSnapping.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorSeekSnapping.cs @@ -13,7 +13,7 @@ using osu.Game.Rulesets.Osu.Objects; using osuTK; using osuTK.Graphics; -namespace osu.Game.Tests.Visual.Editor +namespace osu.Game.Tests.Visual.Editing { [TestFixture] public class TestSceneEditorSeekSnapping : EditorClockTestScene diff --git a/osu.Game.Tests/Visual/Editor/TestSceneEditorSummaryTimeline.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorSummaryTimeline.cs similarity index 95% rename from osu.Game.Tests/Visual/Editor/TestSceneEditorSummaryTimeline.cs rename to osu.Game.Tests/Visual/Editing/TestSceneEditorSummaryTimeline.cs index 2e04eb50ca..c92423545d 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneEditorSummaryTimeline.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorSummaryTimeline.cs @@ -10,7 +10,7 @@ using osu.Game.Rulesets.Osu; using osu.Game.Screens.Edit.Components.Timelines.Summary; using osuTK; -namespace osu.Game.Tests.Visual.Editor +namespace osu.Game.Tests.Visual.Editing { [TestFixture] public class TestSceneEditorSummaryTimeline : EditorClockTestScene diff --git a/osu.Game.Tests/Visual/Editor/TestSceneHitObjectComposer.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs similarity index 98% rename from osu.Game.Tests/Visual/Editor/TestSceneHitObjectComposer.cs rename to osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs index e41c2427fb..ddaca26220 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneHitObjectComposer.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs @@ -20,7 +20,7 @@ using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Compose.Components; using osuTK; -namespace osu.Game.Tests.Visual.Editor +namespace osu.Game.Tests.Visual.Editing { [TestFixture] public class TestSceneHitObjectComposer : EditorClockTestScene diff --git a/osu.Game.Tests/Visual/Editor/TestScenePlaybackControl.cs b/osu.Game.Tests/Visual/Editing/TestScenePlaybackControl.cs similarity index 96% rename from osu.Game.Tests/Visual/Editor/TestScenePlaybackControl.cs rename to osu.Game.Tests/Visual/Editing/TestScenePlaybackControl.cs index 0d4fe4366d..3af976cae0 100644 --- a/osu.Game.Tests/Visual/Editor/TestScenePlaybackControl.cs +++ b/osu.Game.Tests/Visual/Editing/TestScenePlaybackControl.cs @@ -9,7 +9,7 @@ using osu.Game.Beatmaps; using osu.Game.Screens.Edit.Components; using osuTK; -namespace osu.Game.Tests.Visual.Editor +namespace osu.Game.Tests.Visual.Editing { [TestFixture] public class TestScenePlaybackControl : OsuTestScene diff --git a/osu.Game.Tests/Visual/Editor/TestSceneTimelineBlueprintContainer.cs b/osu.Game.Tests/Visual/Editing/TestSceneTimelineBlueprintContainer.cs similarity index 95% rename from osu.Game.Tests/Visual/Editor/TestSceneTimelineBlueprintContainer.cs rename to osu.Game.Tests/Visual/Editing/TestSceneTimelineBlueprintContainer.cs index 4d8f877575..5ab2f49b4a 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneTimelineBlueprintContainer.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneTimelineBlueprintContainer.cs @@ -7,7 +7,7 @@ using NUnit.Framework; using osu.Framework.Graphics; using osu.Game.Screens.Edit.Compose.Components.Timeline; -namespace osu.Game.Tests.Visual.Editor +namespace osu.Game.Tests.Visual.Editing { [TestFixture] public class TestSceneTimelineBlueprintContainer : TimelineTestScene diff --git a/osu.Game.Tests/Visual/Editor/TestSceneTimelineTickDisplay.cs b/osu.Game.Tests/Visual/Editing/TestSceneTimelineTickDisplay.cs similarity index 95% rename from osu.Game.Tests/Visual/Editor/TestSceneTimelineTickDisplay.cs rename to osu.Game.Tests/Visual/Editing/TestSceneTimelineTickDisplay.cs index 43a3cd6122..e33040acdc 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneTimelineTickDisplay.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneTimelineTickDisplay.cs @@ -8,7 +8,7 @@ using osu.Game.Screens.Edit.Compose.Components; using osu.Game.Screens.Edit.Compose.Components.Timeline; using osuTK; -namespace osu.Game.Tests.Visual.Editor +namespace osu.Game.Tests.Visual.Editing { [TestFixture] public class TestSceneTimelineTickDisplay : TimelineTestScene diff --git a/osu.Game.Tests/Visual/Editor/TestSceneTimingScreen.cs b/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs similarity index 96% rename from osu.Game.Tests/Visual/Editor/TestSceneTimingScreen.cs rename to osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs index ae09a7fa47..a6dbe9571e 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneTimingScreen.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs @@ -9,7 +9,7 @@ using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Timing; -namespace osu.Game.Tests.Visual.Editor +namespace osu.Game.Tests.Visual.Editing { [TestFixture] public class TestSceneTimingScreen : EditorClockTestScene diff --git a/osu.Game.Tests/Visual/Editor/TestSceneWaveform.cs b/osu.Game.Tests/Visual/Editing/TestSceneWaveform.cs similarity index 98% rename from osu.Game.Tests/Visual/Editor/TestSceneWaveform.cs rename to osu.Game.Tests/Visual/Editing/TestSceneWaveform.cs index e2762f3d5f..0c1296b82c 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneWaveform.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneWaveform.cs @@ -14,7 +14,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Osu; using osuTK.Graphics; -namespace osu.Game.Tests.Visual.Editor +namespace osu.Game.Tests.Visual.Editing { [TestFixture] public class TestSceneWaveform : OsuTestScene diff --git a/osu.Game.Tests/Visual/Editor/TestSceneZoomableScrollContainer.cs b/osu.Game.Tests/Visual/Editing/TestSceneZoomableScrollContainer.cs similarity index 99% rename from osu.Game.Tests/Visual/Editor/TestSceneZoomableScrollContainer.cs rename to osu.Game.Tests/Visual/Editing/TestSceneZoomableScrollContainer.cs index 19d19c2759..082268d824 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneZoomableScrollContainer.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneZoomableScrollContainer.cs @@ -15,7 +15,7 @@ using osu.Game.Screens.Edit.Compose.Components.Timeline; using osuTK; using osuTK.Graphics; -namespace osu.Game.Tests.Visual.Editor +namespace osu.Game.Tests.Visual.Editing { public class TestSceneZoomableScrollContainer : OsuManualInputManagerTestScene { diff --git a/osu.Game.Tests/Visual/Editor/TimelineTestScene.cs b/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs similarity index 99% rename from osu.Game.Tests/Visual/Editor/TimelineTestScene.cs rename to osu.Game.Tests/Visual/Editing/TimelineTestScene.cs index 7081eb3af5..56b2860e96 100644 --- a/osu.Game.Tests/Visual/Editor/TimelineTestScene.cs +++ b/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs @@ -18,7 +18,7 @@ using osu.Game.Screens.Edit.Compose.Components.Timeline; using osuTK; using osuTK.Graphics; -namespace osu.Game.Tests.Visual.Editor +namespace osu.Game.Tests.Visual.Editing { public abstract class TimelineTestScene : EditorClockTestScene { From 0a840a26133b76a1bbd80b9d77782843460e28d9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 23 Apr 2020 17:41:33 +0900 Subject: [PATCH 0415/1117] Fix mania not getting its own selection handler --- osu.Game.Rulesets.Mania/Edit/ManiaBlueprintContainer.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaBlueprintContainer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaBlueprintContainer.cs index d744036b4c..cea27498c3 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaBlueprintContainer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaBlueprintContainer.cs @@ -30,5 +30,7 @@ namespace osu.Game.Rulesets.Mania.Edit return base.CreateBlueprintFor(hitObject); } + + protected override SelectionHandler CreateSelectionHandler() => new ManiaSelectionHandler(); } } From 4ebb28d3e7fdc0a412643f5e4a3effce353e9ebe Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 23 Apr 2020 17:52:54 +0900 Subject: [PATCH 0416/1117] wip --- .../Edit/Blueprints/HoldNoteSelectionBlueprint.cs | 2 ++ osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs | 4 ++-- .../Screens/Edit/Compose/Components/BlueprintContainer.cs | 7 +++++-- osu.Game/Tests/Beatmaps/TestBeatmap.cs | 1 + 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNoteSelectionBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNoteSelectionBlueprint.cs index d569d68b59..43d43ef252 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNoteSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNoteSelectionBlueprint.cs @@ -76,5 +76,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints } public override Quad SelectionQuad => ScreenSpaceDrawQuad; + + public override Vector2 SelectionPoint => DrawableObject.Head.ScreenSpaceDrawQuad.Centre; } } diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs index 9069a636a8..78f159b733 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs @@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Mania.Edit var maniaBlueprint = (ManiaSelectionBlueprint)moveEvent.Blueprint; int lastColumn = maniaBlueprint.DrawableObject.HitObject.Column; - adjustOrigins(maniaBlueprint); + // adjustOrigins(maniaBlueprint); performDragMovement(moveEvent); performColumnMovement(lastColumn, moveEvent); @@ -71,7 +71,7 @@ namespace osu.Game.Rulesets.Mania.Edit // When scrolling downwards the anchor position is at the bottom of the screen, however the movement event assumes the anchor is at the top of the screen. // This causes the delta to assume a positive hitobject position, and which can be corrected for by subtracting the parent height. if (scrollingInfo.Direction.Value == ScrollingDirection.Down) - delta -= moveEvent.Blueprint.Parent.DrawHeight; // todo: probably wrong + delta -= moveEvent.Blueprint.Parent.DrawHeight; // todo: definitely wrong foreach (var selectionBlueprint in SelectedBlueprints) { diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index ad16e22e5e..0823be01f8 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -401,15 +401,18 @@ namespace osu.Game.Screens.Edit.Compose.Components HitObject draggedObject = movementBlueprint.HitObject; - // The final movement position, relative to screenSpaceMovementStartPosition + // The final movement position, relative to movementBlueprintOriginalPosition Vector2 movePosition = movementBlueprintOriginalPosition.Value + e.ScreenSpaceMousePosition - e.ScreenSpaceMouseDownPosition; - (Vector2 snappedPosition, double snappedTime) = snapProvider.GetSnappedPosition(ToLocalSpace(movePosition), draggedObject.StartTime); + (Vector2 snappedPosition, _) = snapProvider.GetSnappedPosition(ToLocalSpace(movePosition), draggedObject.StartTime); // Move the hitobjects if (!selectionHandler.HandleMovement(new MoveSelectionEvent(movementBlueprint, ToScreenSpace(snappedPosition)))) return true; + // Todo: Temp + (_, double snappedTime) = snapProvider.GetSnappedPosition(ToLocalSpace(snappedPosition), draggedObject.StartTime); + // Apply the start time at the newly snapped-to position double offset = snappedTime - draggedObject.StartTime; foreach (HitObject obj in selectionHandler.SelectedHitObjects) diff --git a/osu.Game/Tests/Beatmaps/TestBeatmap.cs b/osu.Game/Tests/Beatmaps/TestBeatmap.cs index 96e3c037a3..a7c84bf692 100644 --- a/osu.Game/Tests/Beatmaps/TestBeatmap.cs +++ b/osu.Game/Tests/Beatmaps/TestBeatmap.cs @@ -23,6 +23,7 @@ namespace osu.Game.Tests.Beatmaps HitObjects = baseBeatmap.HitObjects; BeatmapInfo.Ruleset = ruleset; + BeatmapInfo.RulesetID = ruleset.ID ?? 0; BeatmapInfo.BeatmapSet.Metadata = BeatmapInfo.Metadata; BeatmapInfo.BeatmapSet.Beatmaps = new List { BeatmapInfo }; BeatmapInfo.BeatmapSet.OnlineInfo = new BeatmapSetOnlineInfo From b471a240cc9f1510a945352937ddd6b3834fb8f7 Mon Sep 17 00:00:00 2001 From: Tim Oliver Date: Thu, 23 Apr 2020 16:59:56 +0800 Subject: [PATCH 0417/1117] Fixed merge typo in playfield members --- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index a13a818f68..d44de496f4 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Taiko.UI private readonly Container kiaiExplosionContainer; private readonly JudgementContainer judgementContainer; private readonly ScrollingHitObjectContainer drumRollHitContainer; - internal readonly HitTarget HitTarget; + internal readonly Drawable HitTarget; private readonly ProxyContainer topLevelHitContainer; private readonly ProxyContainer barlineContainer; From a9897ba627311977186f50a9db0b97326e8351b4 Mon Sep 17 00:00:00 2001 From: Tim Oliver Date: Thu, 23 Apr 2020 18:15:12 +0800 Subject: [PATCH 0418/1117] Moved proxy behaviour to drumroll container --- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index d44de496f4..07f3fe4e01 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -195,7 +195,8 @@ namespace osu.Game.Rulesets.Taiko.UI { Name = "Top level hit objects", RelativeSizeAxes = Axes.Both, - } + }, + drumRollHitContainer.CreateProxy() }; } @@ -241,7 +242,7 @@ namespace osu.Game.Rulesets.Taiko.UI drawableHit = new DrawableFlyingCentreHit(Time.Current, isStrong); drumRollHitContainer.Add(drawableHit); - topLevelHitContainer.Add(drawableHit.CreateProxiedContent()); + } internal void OnNewResult(DrawableHitObject judgedObject, JudgementResult result) From f1ae8af5818958f67b057151ed3c58726fccfa44 Mon Sep 17 00:00:00 2001 From: Tim Oliver Date: Thu, 23 Apr 2020 18:16:05 +0800 Subject: [PATCH 0419/1117] Removed un-needed using directives --- osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs index d4d073ee94..f767403c65 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs @@ -1,11 +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 osu.Framework.Allocation; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Graphics; -using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; using osu.Game.Skinning; From dded4f8176265a4bc5571856424edd9eef883943 Mon Sep 17 00:00:00 2001 From: Tim Oliver Date: Thu, 23 Apr 2020 18:17:31 +0800 Subject: [PATCH 0420/1117] Fixed syntax warnings in Taiko playfield --- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 07f3fe4e01..f4982fa7e6 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -242,7 +242,6 @@ namespace osu.Game.Rulesets.Taiko.UI drawableHit = new DrawableFlyingCentreHit(Time.Current, isStrong); drumRollHitContainer.Add(drawableHit); - } internal void OnNewResult(DrawableHitObject judgedObject, JudgementResult result) @@ -289,8 +288,9 @@ namespace osu.Game.Rulesets.Taiko.UI if (Time.Elapsed < 0) { - foreach (DrawableHit taikoHit in drumRollHitContainer.Objects) + foreach (var o in drumRollHitContainer.Objects) { + var taikoHit = (DrawableHit)o; taikoHit.RemoveProxiedContent(); drumRollHitContainer.Remove(taikoHit); } From 0a0ea39431ebf6432c0aa7eb071591931074a7fc Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 23 Apr 2020 13:24:18 +0300 Subject: [PATCH 0421/1117] Mark the top ruleset creation method as can-be-null --- osu.Game/Tests/Visual/OsuTestScene.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs index 25ac768272..83db86c0a0 100644 --- a/osu.Game/Tests/Visual/OsuTestScene.cs +++ b/osu.Game/Tests/Visual/OsuTestScene.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Track; @@ -145,6 +146,7 @@ namespace osu.Game.Tests.Visual /// /// When testing against ruleset-specific components, this method must be overriden to their ruleset. /// + [CanBeNull] protected virtual Ruleset CreateRuleset() => null; protected virtual IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset); From c059588a09a761d6c92304d71b77218c836c915f Mon Sep 17 00:00:00 2001 From: Tim Oliver Date: Thu, 23 Apr 2020 18:26:40 +0800 Subject: [PATCH 0422/1117] Removed un-needed unproxy method --- .../Objects/Drawables/DrawableTaikoHitObject.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs index e0ff236297..1be04f1760 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs @@ -76,8 +76,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables /// public Drawable CreateProxiedContent() => proxiedContent.CreateProxy(); - public void RemoveProxiedContent() => UnproxyContent(); - public abstract bool OnPressed(TaikoAction action); public virtual void OnReleased(TaikoAction action) From 1fa3764a1db26d70a124437d3c6ae6e70c780674 Mon Sep 17 00:00:00 2001 From: Tim Oliver Date: Thu, 23 Apr 2020 18:26:53 +0800 Subject: [PATCH 0423/1117] Cleaned up Update method in Taiko Playfield --- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index f4982fa7e6..3d371d5260 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -286,14 +286,12 @@ namespace osu.Game.Rulesets.Taiko.UI { base.Update(); + // When rewinding, make sure to remove any auxilliary hit notes that were + // spawned and played during a drumroll. if (Time.Elapsed < 0) { foreach (var o in drumRollHitContainer.Objects) - { - var taikoHit = (DrawableHit)o; - taikoHit.RemoveProxiedContent(); - drumRollHitContainer.Remove(taikoHit); - } + drumRollHitContainer.Remove(o); } } From 2fa47992dc58f0c8293be22eba14673391181933 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 23 Apr 2020 13:25:06 +0300 Subject: [PATCH 0424/1117] Seal the ruleset creation methods and let abstract method take their place Also makes `CreatePlayerRuleset()` and `CreateRulesetForSkinProvider()` not-null to avoid unwanted behaviour with their derivers --- .../CatchSkinnableTestScene.cs | 2 +- .../Mods/TestSceneCatchModPerfect.cs | 4 +-- .../TestSceneCatchPlayer.cs | 2 +- .../Mods/TestSceneManiaModPerfect.cs | 4 +-- .../Skinning/ManiaSkinnableTestScene.cs | 4 +-- .../TestSceneManiaPlayer.cs | 2 +- .../Mods/TestSceneOsuModDifficultyAdjust.cs | 4 +-- .../Mods/TestSceneOsuModDoubleTime.cs | 4 +-- .../Mods/TestSceneOsuModPerfect.cs | 4 +-- .../OsuSkinnableTestScene.cs | 2 +- .../TestSceneMissHitWindowJudgements.cs | 4 +-- .../TestSceneOsuPlayer.cs | 2 +- .../Mods/TestSceneTaikoModPerfect.cs | 4 +-- .../TaikoSkinnableTestScene.cs | 2 +- .../TestSceneTaikoPlayer.cs | 2 +- .../Visual/Gameplay/TestPlayerTestScene.cs | 2 +- osu.Game/Tests/Visual/PlayerTestScene.cs | 30 ++++++++++++++----- osu.Game/Tests/Visual/SkinnableTestScene.cs | 19 +++++++----- 18 files changed, 58 insertions(+), 39 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/CatchSkinnableTestScene.cs b/osu.Game.Rulesets.Catch.Tests/CatchSkinnableTestScene.cs index c0060af74a..0c46b078b5 100644 --- a/osu.Game.Rulesets.Catch.Tests/CatchSkinnableTestScene.cs +++ b/osu.Game.Rulesets.Catch.Tests/CatchSkinnableTestScene.cs @@ -16,6 +16,6 @@ namespace osu.Game.Rulesets.Catch.Tests typeof(CatchLegacySkinTransformer), }; - protected override Ruleset CreateRuleset() => new CatchRuleset(); + protected override Ruleset CreateRulesetForSkinProvider() => new CatchRuleset(); } } diff --git a/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModPerfect.cs b/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModPerfect.cs index 1e69a3f1b6..3e06e78dba 100644 --- a/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModPerfect.cs +++ b/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModPerfect.cs @@ -13,6 +13,8 @@ namespace osu.Game.Rulesets.Catch.Tests.Mods { public class TestSceneCatchModPerfect : ModPerfectTestScene { + protected override Ruleset CreatePlayerRuleset() => new CatchRuleset(); + public TestSceneCatchModPerfect() : base(new CatchModPerfect()) { @@ -50,7 +52,5 @@ namespace osu.Game.Rulesets.Catch.Tests.Mods // We only care about testing misses, hits are tested via JuiceStream [TestCase(true)] public void TestTinyDroplet(bool shouldMiss) => CreateHitObjectTest(new HitObjectTestData(new TinyDroplet { StartTime = 1000 }), shouldMiss); - - protected override Ruleset CreateRuleset() => new CatchRuleset(); } } diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayer.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayer.cs index 722f3b5a3b..e1de461e3b 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayer.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayer.cs @@ -16,6 +16,6 @@ namespace osu.Game.Rulesets.Catch.Tests typeof(CatchRuleset), }; - protected override Ruleset CreateRuleset() => new CatchRuleset(); + 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 72ef58ec73..2e3b21aed7 100644 --- a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModPerfect.cs +++ b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModPerfect.cs @@ -10,6 +10,8 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods { public class TestSceneManiaModPerfect : ModPerfectTestScene { + protected override Ruleset CreatePlayerRuleset() => new ManiaRuleset(); + public TestSceneManiaModPerfect() : base(new ManiaModPerfect()) { @@ -22,7 +24,5 @@ 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); - - protected override Ruleset CreateRuleset() => new ManiaRuleset(); } } diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/ManiaSkinnableTestScene.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/ManiaSkinnableTestScene.cs index f41ba4db42..a3c1d518c5 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/ManiaSkinnableTestScene.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/ManiaSkinnableTestScene.cs @@ -34,6 +34,8 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning typeof(ManiaSettingsSubsection) }; + protected override Ruleset CreateRulesetForSkinProvider() => new ManiaRuleset(); + protected ManiaSkinnableTestScene() { scrollingInfo.Direction.Value = ScrollingDirection.Down; @@ -58,8 +60,6 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning AddStep("change direction to up", () => scrollingInfo.Direction.Value = ScrollingDirection.Up); } - protected override Ruleset CreateRuleset() => new ManiaRuleset(); - private class TestScrollingInfo : IScrollingInfo { public readonly Bindable Direction = new Bindable(); diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaPlayer.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaPlayer.cs index 11663605e2..f4640fd05b 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaPlayer.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaPlayer.cs @@ -14,6 +14,6 @@ namespace osu.Game.Rulesets.Mania.Tests typeof(ManiaRuleset), }; - protected override Ruleset CreateRuleset() => new ManiaRuleset(); + protected override Ruleset CreatePlayerRuleset() => new ManiaRuleset(); } } diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModDifficultyAdjust.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModDifficultyAdjust.cs index 6c5949ca85..7c396054f1 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModDifficultyAdjust.cs @@ -15,6 +15,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods { public class TestSceneOsuModDifficultyAdjust : ModTestScene { + protected override Ruleset CreatePlayerRuleset() => new OsuRuleset(); + [Test] public void TestNoAdjustment() => CreateModTest(new ModTestData { @@ -77,7 +79,5 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods { return Player.ScoreProcessor.JudgedHits >= 2; } - - protected override Ruleset CreateRuleset() => new OsuRuleset(); } } diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModDoubleTime.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModDoubleTime.cs index c61ef2724b..94ef6140e9 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModDoubleTime.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModDoubleTime.cs @@ -10,6 +10,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods { public class TestSceneOsuModDoubleTime : ModTestScene { + protected override Ruleset CreatePlayerRuleset() => new OsuRuleset(); + [TestCase(0.5)] [TestCase(1.01)] [TestCase(1.5)] @@ -26,7 +28,5 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods Precision.AlmostEquals(Player.GameplayClockContainer.GameplayClock.Rate, mod.SpeedChange.Value) }); } - - protected override Ruleset CreateRuleset() => new OsuRuleset(); } } diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModPerfect.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModPerfect.cs index ddbbf9554c..985baa8cf5 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModPerfect.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModPerfect.cs @@ -13,6 +13,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods { public class TestSceneOsuModPerfect : ModPerfectTestScene { + protected override Ruleset CreatePlayerRuleset() => new OsuRuleset(); + public TestSceneOsuModPerfect() : base(new OsuModPerfect()) { @@ -48,7 +50,5 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods CreateHitObjectTest(new HitObjectTestData(spinner), shouldMiss); } - - protected override Ruleset CreateRuleset() => new OsuRuleset(); } } diff --git a/osu.Game.Rulesets.Osu.Tests/OsuSkinnableTestScene.cs b/osu.Game.Rulesets.Osu.Tests/OsuSkinnableTestScene.cs index 1458270193..90ebbd9f04 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuSkinnableTestScene.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuSkinnableTestScene.cs @@ -16,6 +16,6 @@ namespace osu.Game.Rulesets.Osu.Tests typeof(OsuLegacySkinTransformer), }; - protected override Ruleset CreateRuleset() => new OsuRuleset(); + protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); } } diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneMissHitWindowJudgements.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneMissHitWindowJudgements.cs index 13457ccaf9..f3221ffe32 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneMissHitWindowJudgements.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneMissHitWindowJudgements.cs @@ -19,6 +19,8 @@ namespace osu.Game.Rulesets.Osu.Tests { public class TestSceneMissHitWindowJudgements : ModTestScene { + protected override Ruleset CreatePlayerRuleset() => new OsuRuleset(); + [Test] public void TestMissViaEarlyHit() { @@ -61,8 +63,6 @@ namespace osu.Game.Rulesets.Osu.Tests }); } - protected override Ruleset CreateRuleset() => new OsuRuleset(); - private class TestAutoMod : OsuModAutoplay { public override Score CreateReplayScore(IBeatmap beatmap) => new Score diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuPlayer.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuPlayer.cs index 102f8bf841..4ae19624c0 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuPlayer.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuPlayer.cs @@ -16,6 +16,6 @@ namespace osu.Game.Rulesets.Osu.Tests typeof(OsuRuleset), }; - protected override Ruleset CreateRuleset() => new OsuRuleset(); + protected override Ruleset CreatePlayerRuleset() => new OsuRuleset(); } } diff --git a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModPerfect.cs b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModPerfect.cs index a9c962bfa0..a83cc16413 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModPerfect.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModPerfect.cs @@ -12,6 +12,8 @@ namespace osu.Game.Rulesets.Taiko.Tests.Mods { public class TestSceneTaikoModPerfect : ModPerfectTestScene { + protected override Ruleset CreatePlayerRuleset() => new TestTaikoRuleset(); + public TestSceneTaikoModPerfect() : base(new TaikoModPerfect()) { @@ -29,8 +31,6 @@ namespace osu.Game.Rulesets.Taiko.Tests.Mods [TestCase(true)] public void TestSwell(bool shouldMiss) => CreateHitObjectTest(new HitObjectTestData(new Swell { StartTime = 1000, EndTime = 3000 }), shouldMiss); - protected override Ruleset CreateRuleset() => new TestTaikoRuleset(); - private class TestTaikoRuleset : TaikoRuleset { public override HealthProcessor CreateHealthProcessor(double drainStartTime) => new TestTaikoHealthProcessor(); diff --git a/osu.Game.Rulesets.Taiko.Tests/TaikoSkinnableTestScene.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoSkinnableTestScene.cs index 98e6c2ec52..6db2a6907f 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TaikoSkinnableTestScene.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TaikoSkinnableTestScene.cs @@ -16,6 +16,6 @@ namespace osu.Game.Rulesets.Taiko.Tests typeof(TaikoLegacySkinTransformer), }; - protected override Ruleset CreateRuleset() => new TaikoRuleset(); + protected override Ruleset CreateRulesetForSkinProvider() => new TaikoRuleset(); } } diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayer.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayer.cs index 4c5ab7eabf..bc6f664942 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayer.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayer.cs @@ -14,6 +14,6 @@ namespace osu.Game.Rulesets.Taiko.Tests typeof(TaikoRuleset) }; - protected override Ruleset CreateRuleset() => new TaikoRuleset(); + protected override Ruleset CreatePlayerRuleset() => new TaikoRuleset(); } } diff --git a/osu.Game.Tests/Visual/Gameplay/TestPlayerTestScene.cs b/osu.Game.Tests/Visual/Gameplay/TestPlayerTestScene.cs index 2130171449..bbf0136b00 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestPlayerTestScene.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestPlayerTestScene.cs @@ -11,6 +11,6 @@ namespace osu.Game.Tests.Visual.Gameplay /// public abstract class TestPlayerTestScene : PlayerTestScene { - protected override Ruleset CreateRuleset() => new OsuRuleset(); + protected override Ruleset CreatePlayerRuleset() => new OsuRuleset(); } } diff --git a/osu.Game/Tests/Visual/PlayerTestScene.cs b/osu.Game/Tests/Visual/PlayerTestScene.cs index f5e78fbbd1..53abf83e72 100644 --- a/osu.Game/Tests/Visual/PlayerTestScene.cs +++ b/osu.Game/Tests/Visual/PlayerTestScene.cs @@ -3,6 +3,7 @@ using System; using System.Linq; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Testing; @@ -23,6 +24,22 @@ namespace osu.Game.Tests.Visual protected OsuConfigManager LocalConfig; + /// + /// Creates the ruleset for setting up the component. + /// + [NotNull] + protected abstract Ruleset CreatePlayerRuleset(); + + protected sealed override Ruleset CreateRuleset() => CreatePlayerRuleset(); + + [NotNull] + private readonly Ruleset ruleset; + + protected PlayerTestScene() + { + ruleset = CreatePlayerRuleset(); + } + [BackgroundDependencyLoader] private void load() { @@ -46,7 +63,7 @@ namespace osu.Game.Tests.Visual action?.Invoke(); - AddStep(CreateRuleset().RulesetInfo.Name, LoadPlayer); + AddStep(ruleset.Description, LoadPlayer); AddUntilStep("player loaded", () => Player.IsLoaded && Player.Alpha == 1); } @@ -56,28 +73,27 @@ namespace osu.Game.Tests.Visual protected void LoadPlayer() { - var beatmap = CreateBeatmap(Ruleset.Value); + var beatmap = CreateBeatmap(ruleset.RulesetInfo); Beatmap.Value = CreateWorkingBeatmap(beatmap); + Ruleset.Value = ruleset.RulesetInfo; SelectedMods.Value = Array.Empty(); - var rulesetInstance = Ruleset.Value.CreateInstance(); - if (!AllowFail) { - var noFailMod = rulesetInstance.GetAllMods().FirstOrDefault(m => m is ModNoFail); + var noFailMod = ruleset.GetAllMods().FirstOrDefault(m => m is ModNoFail); if (noFailMod != null) SelectedMods.Value = new[] { noFailMod }; } if (Autoplay) { - var mod = rulesetInstance.GetAutoplayMod(); + var mod = ruleset.GetAutoplayMod(); if (mod != null) SelectedMods.Value = SelectedMods.Value.Concat(mod.Yield()).ToArray(); } - Player = CreatePlayer(rulesetInstance); + Player = CreatePlayer(ruleset); LoadScreen(Player); } diff --git a/osu.Game/Tests/Visual/SkinnableTestScene.cs b/osu.Game/Tests/Visual/SkinnableTestScene.cs index d648afd504..98164031b0 100644 --- a/osu.Game/Tests/Visual/SkinnableTestScene.cs +++ b/osu.Game/Tests/Visual/SkinnableTestScene.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Text.RegularExpressions; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Graphics; @@ -13,6 +14,7 @@ using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; using osu.Game.Beatmaps; using osu.Game.Graphics.Sprites; +using osu.Game.Rulesets; using osu.Game.Skinning; using osuTK; using osuTK.Graphics; @@ -26,16 +28,17 @@ namespace osu.Game.Tests.Visual private Skin specialSkin; private Skin oldSkin; + /// + /// Creates the ruleset for adding the ruleset-specific skin transforming component. + /// + [NotNull] + protected abstract Ruleset CreateRulesetForSkinProvider(); + + protected sealed override Ruleset CreateRuleset() => CreateRulesetForSkinProvider(); + protected SkinnableTestScene() : base(2, 3) { - // avoid running silently incorrectly. - if (CreateRuleset() == null) - { - throw new InvalidOperationException( - $"No ruleset provided, override {nameof(CreateRuleset)} to the ruleset belonging to the skinnable content." - + "This is required to add the legacy skin transformer for the content to behave as expected."); - } } [BackgroundDependencyLoader] @@ -110,7 +113,7 @@ namespace osu.Game.Tests.Visual { new OutlineBox { Alpha = autoSize ? 1 : 0 }, mainProvider.WithChild( - new SkinProvidingContainer(Ruleset.Value.CreateInstance().CreateLegacySkinProvider(mainProvider, beatmap)) + new SkinProvidingContainer(CreateRulesetForSkinProvider().CreateLegacySkinProvider(mainProvider, beatmap)) { Child = created, RelativeSizeAxes = !autoSize ? Axes.Both : Axes.None, From 029d15f2a2f19ed2f88bb61d58af947df3411702 Mon Sep 17 00:00:00 2001 From: Tim Oliver Date: Thu, 23 Apr 2020 20:14:39 +0800 Subject: [PATCH 0425/1117] Fixed syntax warning for playfield children --- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 3d371d5260..66894fe883 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Taiko.UI public TaikoPlayfield(ControlPointInfo controlPoints) { - InternalChildren = new Drawable[] + InternalChildren = new[] { backgroundContainer = new Container { From 085b6ae25f0f6bd4c01b4077090d01be35ef3b67 Mon Sep 17 00:00:00 2001 From: Shivam Date: Thu, 23 Apr 2020 20:24:03 +0200 Subject: [PATCH 0426/1117] Add background video for showcase scene (Tournament Client) --- .../Screens/Showcase/ShowcaseScreen.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs b/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs index d809dfc994..1bf67fe607 100644 --- a/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs +++ b/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs @@ -2,6 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Tournament.Components; namespace osu.Game.Tournament.Screens.Showcase { @@ -10,7 +12,14 @@ namespace osu.Game.Tournament.Screens.Showcase [BackgroundDependencyLoader] private void load() { - AddInternal(new TournamentLogo()); + AddRangeInternal(new Drawable[] { + new TournamentLogo(), + new TourneyVideo("showcase") + { + Loop = true, + RelativeSizeAxes = Axes.Both, + } + }); } } } From 28dcfe867c3afc866791d2d43934ae0a7626586d Mon Sep 17 00:00:00 2001 From: Shivam Date: Thu, 23 Apr 2020 21:09:12 +0200 Subject: [PATCH 0427/1117] Add Chroma keying to the background of the showcase video. --- .../Screens/Showcase/ShowcaseScreen.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs b/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs index 1bf67fe607..85cf8d2e1f 100644 --- a/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs +++ b/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs @@ -4,11 +4,14 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Tournament.Components; +using osu.Framework.Graphics.Shapes; +using osuTK.Graphics; namespace osu.Game.Tournament.Screens.Showcase { - public class ShowcaseScreen : BeatmapInfoScreen + public class ShowcaseScreen : BeatmapInfoScreen, IProvideVideo { + private Box chroma; [BackgroundDependencyLoader] private void load() { @@ -18,6 +21,16 @@ namespace osu.Game.Tournament.Screens.Showcase { Loop = true, RelativeSizeAxes = Axes.Both, + }, + chroma = new Box + { + // chroma key area for stable gameplay + Name = "chroma", + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Height = 695, + Width = 1366, + Colour = new Color4(0, 255, 0, 255), } }); } From dba737105ee9d74050d55fdd61e525a4cc388973 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 24 Apr 2020 08:57:01 +0900 Subject: [PATCH 0428/1117] Update test scene for dynamic compilation --- .../Skinning/TestSceneDrawableBarLine.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableBarLine.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableBarLine.cs index e72f42a0b4..58d69fc32a 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableBarLine.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableBarLine.cs @@ -12,6 +12,7 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Objects.Drawables; +using osu.Game.Rulesets.Taiko.Skinning; using osu.Game.Rulesets.Taiko.UI; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Tests.Visual; @@ -24,6 +25,8 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(DrawableBarLine), + typeof(LegacyBarLine), + typeof(BarLine), }).ToList(); [Cached(typeof(IScrollingInfo))] From 608596c3b39a83d61fa21d30dd92eab4334bfee5 Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Fri, 24 Apr 2020 02:50:10 +0200 Subject: [PATCH 0429/1117] Rename DefaultTaikoDonTextureAnimation to TaikoDonTextureAnimation --- .../UI/DrawableTaikoCharacter.cs | 14 ++++++-------- ...ureAnimation.cs => TaikoDonTextureAnimation.cs} | 11 ++++++----- 2 files changed, 12 insertions(+), 13 deletions(-) rename osu.Game.Rulesets.Taiko/UI/{DefaultTaikoDonTextureAnimation.cs => TaikoDonTextureAnimation.cs} (79%) diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoCharacter.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoCharacter.cs index 897670d049..85f18b3ec7 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoCharacter.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoCharacter.cs @@ -1,16 +1,14 @@ using osu.Framework.Allocation; using osu.Framework.Audio.Track; -using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics.Containers; -using osuTK; namespace osu.Game.Rulesets.Taiko.UI { public sealed class DrawableTaikoCharacter : BeatSyncedContainer { - private static DefaultTaikoDonTextureAnimation idleDrawable, clearDrawable, kiaiDrawable, failDrawable; + private static TaikoDonTextureAnimation idleDrawable, clearDrawable, kiaiDrawable, failDrawable; private TaikoDonAnimationState state; @@ -22,7 +20,7 @@ namespace osu.Game.Rulesets.Taiko.UI var xd = new Vector2(1); } - private DefaultTaikoDonTextureAnimation getStateDrawable() => State switch + private TaikoDonTextureAnimation getStateDrawable() => State switch { TaikoDonAnimationState.Idle => idleDrawable, TaikoDonAnimationState.Clear => clearDrawable, @@ -50,10 +48,10 @@ namespace osu.Game.Rulesets.Taiko.UI { InternalChildren = new[] { - idleDrawable = new DefaultTaikoDonTextureAnimation(TaikoDonAnimationState.Idle), - clearDrawable = new DefaultTaikoDonTextureAnimation(TaikoDonAnimationState.Clear), - kiaiDrawable = new DefaultTaikoDonTextureAnimation(TaikoDonAnimationState.Kiai), - failDrawable = new DefaultTaikoDonTextureAnimation(TaikoDonAnimationState.Fail), + idleDrawable = new TaikoDonTextureAnimation(TaikoDonAnimationState.Idle), + clearDrawable = new TaikoDonTextureAnimation(TaikoDonAnimationState.Clear), + kiaiDrawable = new TaikoDonTextureAnimation(TaikoDonAnimationState.Kiai), + failDrawable = new TaikoDonTextureAnimation(TaikoDonAnimationState.Fail), }; // sets the state, to make sure we have the correct sprite loaded and set. diff --git a/osu.Game.Rulesets.Taiko/UI/DefaultTaikoDonTextureAnimation.cs b/osu.Game.Rulesets.Taiko/UI/TaikoDonTextureAnimation.cs similarity index 79% rename from osu.Game.Rulesets.Taiko/UI/DefaultTaikoDonTextureAnimation.cs rename to osu.Game.Rulesets.Taiko/UI/TaikoDonTextureAnimation.cs index 1fefed953d..315cd57f13 100644 --- a/osu.Game.Rulesets.Taiko/UI/DefaultTaikoDonTextureAnimation.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoDonTextureAnimation.cs @@ -6,18 +6,19 @@ using osu.Game.Skinning; namespace osu.Game.Rulesets.Taiko.UI { - public sealed class DefaultTaikoDonTextureAnimation : TextureAnimation + public sealed class TaikoDonTextureAnimation : TextureAnimation { - private readonly TaikoDonAnimationState _state; + private readonly TaikoDonAnimationState state; private int currentFrame; - public DefaultTaikoDonTextureAnimation(TaikoDonAnimationState state) : base(false) + public TaikoDonTextureAnimation(TaikoDonAnimationState state) : base(false) { - _state = state; + this.state = state; this.Stop(); Origin = Anchor.BottomLeft; Anchor = Anchor.BottomLeft; + AutoSizeAxes = Axes.Y; } [BackgroundDependencyLoader] @@ -25,7 +26,7 @@ namespace osu.Game.Rulesets.Taiko.UI { for (int i = 0;; i++) { - var textureName = $"pippidon{_getStateString(_state)}{i}"; + var textureName = $"pippidon{_getStateString(state)}{i}"; Texture texture = skin.GetTexture(textureName); if (texture == null) From bbe831698c5b315a373d16ef2b5996f682607619 Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Fri, 24 Apr 2020 02:50:33 +0200 Subject: [PATCH 0430/1117] Remove unused code --- .../UI/DrawableTaikoCharacter.cs | 13 ------------- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 1 - 2 files changed, 14 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoCharacter.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoCharacter.cs index 85f18b3ec7..aace96aa9b 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoCharacter.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoCharacter.cs @@ -15,9 +15,6 @@ namespace osu.Game.Rulesets.Taiko.UI public DrawableTaikoCharacter() { RelativeSizeAxes = Axes.Both; - //Size = new Vector2(1f, 2.5f); - //Origin = Anchor.BottomLeft; - var xd = new Vector2(1); } private TaikoDonTextureAnimation getStateDrawable() => State switch @@ -63,16 +60,6 @@ namespace osu.Game.Rulesets.Taiko.UI base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); getStateDrawable().Move(); - - //var signature = timingPoint.TimeSignature == TimeSignatures.SimpleQuadruple ? 4 : 3; - //var length = timingPoint.BeatLength; - //var rate = 1000d / length; - //adjustableClock.Rate = rate; - // - //// Start animating on the first beat. - //if (beatIndex < 1) - // adjustableClock.Start(); - // Logger.GetLogger(LoggingTarget.Information).Add($"Length = {length}ms | Rate = {rate}x | BPM = {timingPoint.BPM} / {signature}"); } } } diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index f4e24afb32..c86a6f61b2 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -22,7 +22,6 @@ using osuTK; using osuTK.Graphics; using osu.Game.Rulesets.Scoring; using osu.Framework.Bindables; -using osu.Game.Skinning; namespace osu.Game.Rulesets.Taiko.UI { From 6de08db65316679f99830c6448540aeb5b89ea06 Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Fri, 24 Apr 2020 02:50:47 +0200 Subject: [PATCH 0431/1117] Add removed skin component back --- osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs b/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs index bd98c2ea87..ff7590c561 100644 --- a/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs +++ b/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs @@ -6,6 +6,7 @@ namespace osu.Game.Rulesets.Taiko public enum TaikoSkinComponents { InputDrum, + CentreHit, RimHit, DrumRollBody, DrumRollTick, From 1c13fa6c6199ed5a51d985f140b209fb804ddd4a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 24 Apr 2020 10:27:32 +0900 Subject: [PATCH 0432/1117] Fix editor crashing when entering with no beatmap selected --- .../Edit/Compose/Components/Timeline/Timeline.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index 1cb4f737c1..8e6b3d5424 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -60,9 +60,12 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline waveform.Waveform = b.NewValue.Waveform; track = b.NewValue.Track; - MaxZoom = getZoomLevelForVisibleMilliseconds(500); - MinZoom = getZoomLevelForVisibleMilliseconds(10000); - Zoom = getZoomLevelForVisibleMilliseconds(2000); + if (track.Length > 0) + { + MaxZoom = getZoomLevelForVisibleMilliseconds(500); + MinZoom = getZoomLevelForVisibleMilliseconds(10000); + Zoom = getZoomLevelForVisibleMilliseconds(2000); + } }, true); } @@ -135,7 +138,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private void scrollToTrackTime() { - if (!track.IsLoaded) + if (!track.IsLoaded || track.Length == 0) return; ScrollTo((float)(adjustableClock.CurrentTime / track.Length) * Content.DrawWidth, false); From ca692f30e66fabf102579803561c96ef2d176d9b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 24 Apr 2020 12:27:56 +0900 Subject: [PATCH 0433/1117] Do full placement on mouse down --- .../Blueprints/HitCircles/HitCirclePlacementBlueprint.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/HitCirclePlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/HitCirclePlacementBlueprint.cs index 2f400160b8..dad199715e 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/HitCirclePlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/HitCirclePlacementBlueprint.cs @@ -33,19 +33,13 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles { if (e.Button == MouseButton.Left) { - BeginPlacement(); + EndPlacement(true); return true; } return base.OnMouseDown(e); } - protected override void OnMouseUp(MouseUpEvent e) - { - if (e.Button == MouseButton.Left) - EndPlacement(true); - } - public override void UpdatePosition(Vector2 screenSpacePosition) => HitObject.Position = ToLocalSpace(screenSpacePosition); } } From 2b0deec491f5260a3c68763cfaccf45384ec32a0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 24 Apr 2020 13:20:41 +0900 Subject: [PATCH 0434/1117] Finish note placement on mouse down --- .../Edit/Blueprints/HoldNotePlacementBlueprint.cs | 7 +++++++ .../Edit/Blueprints/ManiaPlacementBlueprint.cs | 6 ------ .../Edit/Blueprints/NotePlacementBlueprint.cs | 12 +++++++----- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs index 7bbde400ea..b3dd392202 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs @@ -3,6 +3,7 @@ using System; using osu.Framework.Graphics; +using osu.Framework.Input.Events; using osu.Game.Rulesets.Mania.Edit.Blueprints.Components; using osu.Game.Rulesets.Mania.Objects; using osuTK; @@ -46,6 +47,12 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints bodyPiece.Height = (bottomPosition - topPosition).Y; } + protected override void OnMouseUp(MouseUpEvent e) + { + base.OnMouseUp(e); + EndPlacement(true); + } + private double originalStartTime; public override void UpdatePosition(Vector2 screenSpacePosition) diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs index f228daa5e3..400abb6380 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs @@ -54,12 +54,6 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints return true; } - protected override void OnMouseUp(MouseUpEvent e) - { - EndPlacement(true); - base.OnMouseUp(e); - } - public override void UpdatePosition(Vector2 screenSpacePosition) { if (!PlacementActive) diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/NotePlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/NotePlacementBlueprint.cs index 888ce695c2..2b7b383dbe 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/NotePlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/NotePlacementBlueprint.cs @@ -2,9 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; +using osu.Framework.Input.Events; using osu.Game.Rulesets.Mania.Edit.Blueprints.Components; using osu.Game.Rulesets.Mania.Objects; -using osuTK; namespace osu.Game.Rulesets.Mania.Edit.Blueprints { @@ -28,12 +28,14 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints Position = SnappedMousePosition; } - public override void UpdatePosition(Vector2 screenSpacePosition) + protected override bool OnMouseDown(MouseDownEvent e) { - base.UpdatePosition(screenSpacePosition); + base.OnMouseDown(e); - // Continue updating the position until placement is finished on mouse up. - BeginPlacement(TimeAt(screenSpacePosition), PlacementActive); + // Place the note immediately. + EndPlacement(true); + + return true; } } } From dbf39be6070c863109150cc8610cc275ee1aca1a Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Fri, 24 Apr 2020 06:59:05 +0200 Subject: [PATCH 0435/1117] Decide on the name "Mascot", add testing, bug fixed, etc. --- .../Skinning/TestSceneDrawableTaikoMascot.cs | 155 ++++++++++++++++++ .../Skinning/TaikoLegacySkinTransformer.cs | 2 +- .../UI/DrawableTaikoCharacter.cs | 65 -------- .../UI/DrawableTaikoMascot.cs | 99 +++++++++++ .../UI/TaikoDonTextureAnimation.cs | 61 ------- ...nState.cs => TaikoMascotAnimationState.cs} | 2 +- .../UI/TaikoMascotTextureAnimation.cs | 88 ++++++++++ osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 16 +- 8 files changed, 353 insertions(+), 135 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs delete mode 100644 osu.Game.Rulesets.Taiko/UI/DrawableTaikoCharacter.cs create mode 100644 osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs delete mode 100644 osu.Game.Rulesets.Taiko/UI/TaikoDonTextureAnimation.cs rename osu.Game.Rulesets.Taiko/UI/{TaikoDonAnimationState.cs => TaikoMascotAnimationState.cs} (86%) create mode 100644 osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs new file mode 100644 index 0000000000..dc89fa3a59 --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs @@ -0,0 +1,155 @@ +// 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.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; +using osu.Game.Beatmaps.ControlPoints; +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.Objects.Drawables; +using osu.Game.Rulesets.Taiko.UI; +using osu.Game.Rulesets.UI.Scrolling; +using osu.Game.Skinning; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Taiko.Tests.Skinning +{ + [TestFixture] + public class TestSceneDrawableTaikoMascot : TaikoSkinnableTestScene + { + public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] + { + typeof(DrawableTaikoMascot), + }).ToList(); + + [Cached(typeof(IScrollingInfo))] + private ScrollingTestContainer.TestScrollingInfo info = new ScrollingTestContainer.TestScrollingInfo + { + Direction = { Value = ScrollingDirection.Left }, + TimeRange = { Value = 5000 }, + }; + + private readonly List mascots = new List(); + private readonly List skinnables = new List(); + private readonly List playfields = new List(); + + [Test] + public void TestStateTextures() + { + AddStep("Create mascot (idle)", () => + { + skinnables.Clear(); + SetContents(() => + { + var skinnable = getMascot(); + skinnables.Add(skinnable); + return skinnable; + }); + }); + + AddUntilStep("Wait for SkinnableDrawable", () => skinnables.Any(d => d.Drawable is DrawableTaikoMascot)); + + AddStep("Collect mascots", () => + { + mascots.Clear(); + + foreach (var skinnable in skinnables) + { + if (skinnable.Drawable is DrawableTaikoMascot mascot) + mascots.Add(mascot); + } + }); + + AddStep("Clear state", () => setState(TaikoMascotAnimationState.Clear)); + + AddStep("Kiai state", () => setState(TaikoMascotAnimationState.Kiai)); + + AddStep("Fail state", () => setState(TaikoMascotAnimationState.Fail)); + } + + private void setState(TaikoMascotAnimationState state) + { + foreach (var mascot in mascots) + { + if (mascot == null) + continue; + + mascot.Dumb = true; + mascot.State = state; + } + } + + private SkinnableDrawable getMascot() => + new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.TaikoDon), _ => new Container(), confineMode: ConfineMode.ScaleToFit) + { + RelativePositionAxes = Axes.Both + }; + + [Test] + public void TestPlayfield() + { + AddStep("Create playfield", () => + { + playfields.Clear(); + SetContents(() => + { + var playfield = new TaikoPlayfield(new ControlPointInfo()) + { + Height = 0.4f, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + }; + + playfields.Add(playfield); + + return playfield; + }); + }); + + AddUntilStep("Wait for SkinnableDrawable", () => playfields.Any(p => p.ChildrenOfType().Any())); + + AddStep("Collect mascots", () => + { + mascots.Clear(); + + foreach (var playfield in playfields) + { + var mascot = playfield.ChildrenOfType().SingleOrDefault(); + + if (mascot != null) + mascots.Add(mascot); + } + }); + + AddStep("Create hit (miss)", () => + { + foreach (var playfield in playfields) + addJudgement(playfield, HitResult.Miss); + }); + + AddAssert("Check if state is fail", () => mascots.Where(d => d != null).All(d => d.PlayfieldState.Value == TaikoMascotAnimationState.Fail)); + + AddStep("Create hit (great)", () => + { + foreach (var playfield in playfields) + addJudgement(playfield, HitResult.Great); + }); + + AddAssert("Check if state is idle", () => mascots.Where(d => d != null).All(d => d.PlayfieldState.Value == TaikoMascotAnimationState.Idle)); + } + + private void addJudgement(TaikoPlayfield playfield, HitResult result) + { + playfield.OnNewResult(new DrawableRimHit(new Hit()), new JudgementResult(new HitObject(), new TaikoJudgement()) { Type = result }); + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs index cc79822417..dfc9297a33 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs @@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning case TaikoSkinComponents.TaikoDon: if (GetTexture("pippidonclear0") != null) - return new DrawableTaikoCharacter(); + return new DrawableTaikoMascot(); return null; diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoCharacter.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoCharacter.cs deleted file mode 100644 index aace96aa9b..0000000000 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoCharacter.cs +++ /dev/null @@ -1,65 +0,0 @@ -using osu.Framework.Allocation; -using osu.Framework.Audio.Track; -using osu.Framework.Graphics.Textures; -using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Graphics.Containers; - -namespace osu.Game.Rulesets.Taiko.UI -{ - public sealed class DrawableTaikoCharacter : BeatSyncedContainer - { - private static TaikoDonTextureAnimation idleDrawable, clearDrawable, kiaiDrawable, failDrawable; - - private TaikoDonAnimationState state; - - public DrawableTaikoCharacter() - { - RelativeSizeAxes = Axes.Both; - } - - private TaikoDonTextureAnimation getStateDrawable() => State switch - { - TaikoDonAnimationState.Idle => idleDrawable, - TaikoDonAnimationState.Clear => clearDrawable, - TaikoDonAnimationState.Kiai => kiaiDrawable, - TaikoDonAnimationState.Fail => failDrawable, - _ => null - }; - - public TaikoDonAnimationState State - { - get => state; - set - { - state = value; - - foreach (var child in InternalChildren) - child.Hide(); - - getStateDrawable().Show(); - } - } - - [BackgroundDependencyLoader] - private void load(TextureStore textures) - { - InternalChildren = new[] - { - idleDrawable = new TaikoDonTextureAnimation(TaikoDonAnimationState.Idle), - clearDrawable = new TaikoDonTextureAnimation(TaikoDonAnimationState.Clear), - kiaiDrawable = new TaikoDonTextureAnimation(TaikoDonAnimationState.Kiai), - failDrawable = new TaikoDonTextureAnimation(TaikoDonAnimationState.Fail), - }; - - // sets the state, to make sure we have the correct sprite loaded and set. - State = TaikoDonAnimationState.Idle; - } - - protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) - { - base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); - - getStateDrawable().Move(); - } - } -} diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs new file mode 100644 index 0000000000..fbac1e9d0b --- /dev/null +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs @@ -0,0 +1,99 @@ +using osu.Framework.Allocation; +using osu.Framework.Audio.Track; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Textures; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Graphics.Containers; + +namespace osu.Game.Rulesets.Taiko.UI +{ + public sealed class DrawableTaikoMascot : BeatSyncedContainer + { + private static TaikoMascotTextureAnimation idleDrawable, clearDrawable, kiaiDrawable, failDrawable; + private EffectControlPoint lastEffectControlPoint; + private TaikoMascotAnimationState state; + + public Bindable PlayfieldState; + + /// + /// Determines if there should be no "state logic", intended for testing. + /// + public bool Dumb { get; set; } + + public TaikoMascotAnimationState State + { + get => state; + set + { + state = value; + + foreach (var child in InternalChildren) + child.Hide(); + + var drawable = getStateDrawable(State); + + drawable?.Show(); + } + } + + public DrawableTaikoMascot(TaikoMascotAnimationState startingState = TaikoMascotAnimationState.Idle) + { + RelativeSizeAxes = Axes.Both; + PlayfieldState = new Bindable(); + PlayfieldState.BindValueChanged((b) => + { + if (lastEffectControlPoint != null) + State = getFinalAnimationState(lastEffectControlPoint, b.NewValue); + }); + + State = startingState; + } + + private TaikoMascotTextureAnimation getStateDrawable(TaikoMascotAnimationState state) => state switch + { + TaikoMascotAnimationState.Idle => idleDrawable, + TaikoMascotAnimationState.Clear => clearDrawable, + TaikoMascotAnimationState.Kiai => kiaiDrawable, + TaikoMascotAnimationState.Fail => failDrawable, + _ => null + }; + + private TaikoMascotAnimationState getFinalAnimationState(EffectControlPoint effectPoint, TaikoMascotAnimationState playfieldState) + { + if (playfieldState == TaikoMascotAnimationState.Fail) + return playfieldState; + + return effectPoint.KiaiMode ? TaikoMascotAnimationState.Kiai : TaikoMascotAnimationState.Idle; + } + + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + InternalChildren = new[] + { + idleDrawable = new TaikoMascotTextureAnimation(TaikoMascotAnimationState.Idle), + clearDrawable = new TaikoMascotTextureAnimation(TaikoMascotAnimationState.Clear), + kiaiDrawable = new TaikoMascotTextureAnimation(TaikoMascotAnimationState.Kiai), + failDrawable = new TaikoMascotTextureAnimation(TaikoMascotAnimationState.Fail), + }; + + // making sure we have the correct sprite set + State = state; + } + + protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) + { + base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); + + if (!Dumb) + State = getFinalAnimationState(lastEffectControlPoint = effectPoint, PlayfieldState.Value); + + if (State == TaikoMascotAnimationState.Clear) + return; + + var drawable = getStateDrawable(State); + drawable.Move(); + } + } +} diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoDonTextureAnimation.cs b/osu.Game.Rulesets.Taiko/UI/TaikoDonTextureAnimation.cs deleted file mode 100644 index 315cd57f13..0000000000 --- a/osu.Game.Rulesets.Taiko/UI/TaikoDonTextureAnimation.cs +++ /dev/null @@ -1,61 +0,0 @@ -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Animations; -using osu.Framework.Graphics.Textures; -using osu.Game.Skinning; - -namespace osu.Game.Rulesets.Taiko.UI -{ - public sealed class TaikoDonTextureAnimation : TextureAnimation - { - private readonly TaikoDonAnimationState state; - private int currentFrame; - - public TaikoDonTextureAnimation(TaikoDonAnimationState state) : base(false) - { - this.state = state; - this.Stop(); - - Origin = Anchor.BottomLeft; - Anchor = Anchor.BottomLeft; - AutoSizeAxes = Axes.Y; - } - - [BackgroundDependencyLoader] - private void load(ISkinSource skin) - { - for (int i = 0;; i++) - { - var textureName = $"pippidon{_getStateString(state)}{i}"; - Texture texture = skin.GetTexture(textureName); - - if (texture == null) - break; - - AddFrame(texture); - } - } - - /// - /// Advances the current frame by one. - /// - public void Move() - { - if (FrameCount <= currentFrame) - currentFrame = 0; - - GotoFrame(currentFrame); - - currentFrame++; - } - - private string _getStateString(TaikoDonAnimationState state) => state switch - { - TaikoDonAnimationState.Clear => "clear", - TaikoDonAnimationState.Fail => "fail", - TaikoDonAnimationState.Idle => "idle", - TaikoDonAnimationState.Kiai => "kiai", - _ => null - }; - } -} diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoDonAnimationState.cs b/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimationState.cs similarity index 86% rename from osu.Game.Rulesets.Taiko/UI/TaikoDonAnimationState.cs rename to osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimationState.cs index 773710ee7e..02bf245b7b 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoDonAnimationState.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimationState.cs @@ -3,7 +3,7 @@ namespace osu.Game.Rulesets.Taiko.UI { - public enum TaikoDonAnimationState + public enum TaikoMascotAnimationState { Idle, Clear, diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs b/osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs new file mode 100644 index 0000000000..19a533156e --- /dev/null +++ b/osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs @@ -0,0 +1,88 @@ +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Animations; +using osu.Framework.Graphics.Textures; +using osu.Game.Skinning; + +namespace osu.Game.Rulesets.Taiko.UI +{ + public sealed class TaikoMascotTextureAnimation : TextureAnimation + { + private const float clear_animation_speed = 1000 / 10F; + private static readonly int[] clear_animation_sequence = new[] { 0, 1, 2, 3, 4, 5, 6, 5, 6, 5, 4, 3, 2, 1, 0 }; + private int currentFrame; + + public TaikoMascotAnimationState State { get; } + + public TaikoMascotTextureAnimation(TaikoMascotAnimationState state) + : base(true) + { + State = state; + + // We're animating on beat if it's not the clear animation + if (state == TaikoMascotAnimationState.Clear) + DefaultFrameLength = clear_animation_speed; + else + this.Stop(); + + Origin = Anchor.BottomLeft; + Anchor = Anchor.BottomLeft; + } + + [BackgroundDependencyLoader] + private void load(ISkinSource skin) + { + if (State == TaikoMascotAnimationState.Clear) + { + foreach (var textureIndex in clear_animation_sequence) + { + var textureName = _getStateTextureName(textureIndex); + Texture texture = skin.GetTexture(textureName); + + if (texture == null) + break; + + AddFrame(texture); + } + } + else + { + for (int i = 0;; i++) + { + var textureName = _getStateTextureName(i); + Texture texture = skin.GetTexture(textureName); + + if (texture == null) + break; + + AddFrame(texture); + } + } + } + + /// Advances the current frame by one. + public void Move() + { + if (FrameCount == 0) // Frames are apparently broken + return; + + if (FrameCount <= currentFrame) + currentFrame = 0; + + GotoFrame(currentFrame); + + currentFrame += 1; + } + + private string _getStateTextureName(int i) => $"pippidon{_getStateString(State)}{i}"; + + private string _getStateString(TaikoMascotAnimationState state) => state switch + { + TaikoMascotAnimationState.Clear => "clear", + TaikoMascotAnimationState.Fail => "fail", + TaikoMascotAnimationState.Idle => "idle", + TaikoMascotAnimationState.Kiai => "kiai", + _ => null + }; + } +} diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index c86a6f61b2..3bf5d084ee 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -268,16 +268,18 @@ namespace osu.Game.Rulesets.Taiko.UI break; } - if (characterDrawable.Drawable is DrawableTaikoCharacter character) + if (characterDrawable.Drawable is DrawableTaikoMascot mascot) { - if (result.Type == HitResult.Miss && result.Judgement.AffectsCombo) + var isFailing = result.Type == HitResult.Miss; + + // Only take combo in consideration when it's not a strong hit (it's always false) + if (!(judgedObject.HitObject is StrongHitObject)) { - character.State = TaikoDonAnimationState.Fail; - } - else - { - character.State = judgedObject.HitObject.Kiai ? TaikoDonAnimationState.Kiai : TaikoDonAnimationState.Idle; + if (isFailing) + isFailing = result.Judgement.AffectsCombo; } + + mascot.PlayfieldState.Value = isFailing ? TaikoMascotAnimationState.Fail : TaikoMascotAnimationState.Idle; } } } From ac44185f091266d551080feb35c8d3c4caf2da00 Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Fri, 24 Apr 2020 07:09:20 +0200 Subject: [PATCH 0436/1117] Fix formatting --- .../UI/DrawableTaikoMascot.cs | 19 +++++++++++-------- .../UI/TaikoMascotTextureAnimation.cs | 19 +++++++++++-------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs index fbac1e9d0b..dae0b47900 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs @@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Taiko.UI { RelativeSizeAxes = Axes.Both; PlayfieldState = new Bindable(); - PlayfieldState.BindValueChanged((b) => + PlayfieldState.BindValueChanged(b => { if (lastEffectControlPoint != null) State = getFinalAnimationState(lastEffectControlPoint, b.NewValue); @@ -50,14 +50,17 @@ namespace osu.Game.Rulesets.Taiko.UI State = startingState; } - private TaikoMascotTextureAnimation getStateDrawable(TaikoMascotAnimationState state) => state switch + private TaikoMascotTextureAnimation getStateDrawable(TaikoMascotAnimationState state) { - TaikoMascotAnimationState.Idle => idleDrawable, - TaikoMascotAnimationState.Clear => clearDrawable, - TaikoMascotAnimationState.Kiai => kiaiDrawable, - TaikoMascotAnimationState.Fail => failDrawable, - _ => null - }; + return state switch + { + TaikoMascotAnimationState.Idle => idleDrawable, + TaikoMascotAnimationState.Clear => clearDrawable, + TaikoMascotAnimationState.Kiai => kiaiDrawable, + TaikoMascotAnimationState.Fail => failDrawable, + _ => null + }; + } private TaikoMascotAnimationState getFinalAnimationState(EffectControlPoint effectPoint, TaikoMascotAnimationState playfieldState) { diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs b/osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs index 19a533156e..dd4785ea81 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs @@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Taiko.UI public sealed class TaikoMascotTextureAnimation : TextureAnimation { private const float clear_animation_speed = 1000 / 10F; - private static readonly int[] clear_animation_sequence = new[] { 0, 1, 2, 3, 4, 5, 6, 5, 6, 5, 4, 3, 2, 1, 0 }; + private static readonly int[] clear_animation_sequence = { 0, 1, 2, 3, 4, 5, 6, 5, 6, 5, 4, 3, 2, 1, 0 }; private int currentFrame; public TaikoMascotAnimationState State { get; } @@ -76,13 +76,16 @@ namespace osu.Game.Rulesets.Taiko.UI private string _getStateTextureName(int i) => $"pippidon{_getStateString(State)}{i}"; - private string _getStateString(TaikoMascotAnimationState state) => state switch + private string _getStateString(TaikoMascotAnimationState state) { - TaikoMascotAnimationState.Clear => "clear", - TaikoMascotAnimationState.Fail => "fail", - TaikoMascotAnimationState.Idle => "idle", - TaikoMascotAnimationState.Kiai => "kiai", - _ => null - }; + return state switch + { + TaikoMascotAnimationState.Clear => "clear", + TaikoMascotAnimationState.Fail => "fail", + TaikoMascotAnimationState.Idle => "idle", + TaikoMascotAnimationState.Kiai => "kiai", + _ => null + }; + } } } From abb687286be341b609ab92705f012f5c9a6a4496 Mon Sep 17 00:00:00 2001 From: Joehu Date: Thu, 23 Apr 2020 22:34:00 -0700 Subject: [PATCH 0437/1117] Fix score multiplier being cut off in mod select at higher ui scales --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index e9b3598625..b94f5cb570 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -103,7 +103,7 @@ namespace osu.Game.Overlays.Mods { new Dimension(GridSizeMode.Absolute, 90), new Dimension(GridSizeMode.Distributed), - new Dimension(GridSizeMode.Absolute, 70), + new Dimension(GridSizeMode.AutoSize), }, Content = new[] { @@ -197,7 +197,8 @@ namespace osu.Game.Overlays.Mods // Footer new Container { - RelativeSizeAxes = Axes.Both, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, Origin = Anchor.TopCentre, Anchor = Anchor.TopCentre, Children = new Drawable[] @@ -215,7 +216,6 @@ namespace osu.Game.Overlays.Mods AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, Width = content_width, - Direction = FillDirection.Horizontal, Padding = new MarginPadding { Vertical = 15, From 118db03b56ec35eba67b900b930a73d61838e191 Mon Sep 17 00:00:00 2001 From: Joehu Date: Thu, 23 Apr 2020 22:41:38 -0700 Subject: [PATCH 0438/1117] Fix vertical spacing and score multiplier splitting apart Also cleans up margin and its hacks (alignment done with anchor/origin now). --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 73 +++++++++++----------- 1 file changed, 36 insertions(+), 37 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index b94f5cb570..6ab72bc02a 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -57,6 +57,8 @@ namespace osu.Game.Overlays.Mods protected Color4 HighMultiplierColour; private const float content_width = 0.8f; + private const float footer_button_spacing = 20; + private readonly FillFlowContainer footerContainer; private SampleChannel sampleOn, sampleOff; @@ -216,6 +218,7 @@ namespace osu.Game.Overlays.Mods AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, Width = content_width, + Spacing = new Vector2(footer_button_spacing, footer_button_spacing / 2), Padding = new MarginPadding { Vertical = 15, @@ -228,10 +231,8 @@ namespace osu.Game.Overlays.Mods Width = 180, Text = "Deselect All", Action = DeselectAll, - Margin = new MarginPadding - { - Right = 20 - } + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, }, CustomiseButton = new TriangleButton { @@ -239,49 +240,47 @@ namespace osu.Game.Overlays.Mods Text = "Customisation", Action = () => ModSettingsContainer.Alpha = ModSettingsContainer.Alpha == 1 ? 0 : 1, Enabled = { Value = false }, - Margin = new MarginPadding - { - Right = 20 - } + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, }, CloseButton = new TriangleButton { Width = 180, Text = "Close", Action = Hide, - Margin = new MarginPadding - { - Right = 20 - } + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, }, - new OsuSpriteText + new FillFlowContainer { - Text = @"Score Multiplier:", - Font = OsuFont.GetFont(size: 30), - Margin = new MarginPadding + AutoSizeAxes = Axes.Both, + Spacing = new Vector2(footer_button_spacing / 2, 0), + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + Children = new Drawable[] { - Top = 5, - Right = 10 - } + new OsuSpriteText + { + Text = @"Score Multiplier:", + Font = OsuFont.GetFont(size: 30), + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + }, + MultiplierLabel = new OsuSpriteText + { + Font = OsuFont.GetFont(size: 30, weight: FontWeight.Bold, fixedWidth: true), + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + }, + UnrankedLabel = new OsuSpriteText + { + Text = @"(Unranked)", + Font = OsuFont.GetFont(size: 30, weight: FontWeight.Bold), + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + }, + }, }, - MultiplierLabel = new OsuSpriteText - { - Font = OsuFont.GetFont(size: 30, weight: FontWeight.Bold), - Margin = new MarginPadding - { - Top = 5 - } - }, - UnrankedLabel = new OsuSpriteText - { - Text = @"(Unranked)", - Font = OsuFont.GetFont(size: 30, weight: FontWeight.Bold), - Margin = new MarginPadding - { - Top = 5, - Left = 10 - } - } } } }, From 0f6ec274f9547881484b73559e6d27a0e709eaad Mon Sep 17 00:00:00 2001 From: Joehu Date: Thu, 23 Apr 2020 22:44:17 -0700 Subject: [PATCH 0439/1117] Add transitions to footer when flowing to another row --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 6ab72bc02a..a7b7e50422 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -219,6 +219,8 @@ namespace osu.Game.Overlays.Mods RelativeSizeAxes = Axes.X, Width = content_width, Spacing = new Vector2(footer_button_spacing, footer_button_spacing / 2), + LayoutDuration = 100, + LayoutEasing = Easing.OutQuint, Padding = new MarginPadding { Vertical = 15, From 84aa37d7c3a9cd1b92c792441b1ea6e217d03d0e Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Fri, 24 Apr 2020 07:57:16 +0200 Subject: [PATCH 0440/1117] Fix all local tests --- osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs | 5 ++++- osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs | 5 ++++- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs index dae0b47900..f05c335456 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs @@ -1,4 +1,7 @@ -using osu.Framework.Allocation; +// 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.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Graphics; diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs b/osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs index dd4785ea81..4a95717c8c 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs @@ -1,4 +1,7 @@ -using osu.Framework.Allocation; +// 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.Animations; using osu.Framework.Graphics.Textures; diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 3bf5d084ee..6d9d263141 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -284,7 +284,7 @@ namespace osu.Game.Rulesets.Taiko.UI } } - class ProxyContainer : LifetimeManagementContainer + internal class ProxyContainer : LifetimeManagementContainer { public new MarginPadding Padding { From 8ae119ab62e68968f84caff0d101281bee5d88e9 Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Fri, 24 Apr 2020 08:05:57 +0200 Subject: [PATCH 0441/1117] Fix last formatting error --- osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs b/osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs index 4a95717c8c..922b2d08b6 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs @@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Taiko.UI } else { - for (int i = 0;; i++) + for (int i = 0; true; i++) { var textureName = _getStateTextureName(i); Texture texture = skin.GetTexture(textureName); From 05b3db0147aadad9a8114d9f74f4cfdfd045aa14 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 24 Apr 2020 16:56:18 +0900 Subject: [PATCH 0442/1117] Remove masking --- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 8402ebb4c4..6f7d4a1854 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -55,7 +55,6 @@ namespace osu.Game.Rulesets.Taiko.UI Name = "Right area", RelativeSizeAxes = Axes.Both, RelativePositionAxes = Axes.Both, - Masking = true, Children = new Drawable[] { new Container From cbcd915ec8489caf449c02f42f3e3abf801a69fc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 24 Apr 2020 18:18:10 +0900 Subject: [PATCH 0443/1117] Fix crash on switching comments page at an inopportune time --- osu.Game/Overlays/Comments/CommentsContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Comments/CommentsContainer.cs b/osu.Game/Overlays/Comments/CommentsContainer.cs index 591a9dc86e..e7bfeaf968 100644 --- a/osu.Game/Overlays/Comments/CommentsContainer.cs +++ b/osu.Game/Overlays/Comments/CommentsContainer.cs @@ -153,7 +153,7 @@ namespace osu.Game.Overlays.Comments request?.Cancel(); loadCancellation?.Cancel(); request = new GetCommentsRequest(id.Value, type.Value, Sort.Value, currentPage++, 0); - request.Success += onSuccess; + request.Success += res => Schedule(() => onSuccess(res)); api.PerformAsync(request); } From 39a593120cbf84493263f6b1ec7cc99a8f7c390c Mon Sep 17 00:00:00 2001 From: Shivam Date: Fri, 24 Apr 2020 17:00:35 +0200 Subject: [PATCH 0444/1117] Fixed CodeInspect errors --- .../Screens/Showcase/ShowcaseScreen.cs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs b/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs index 85cf8d2e1f..e48ec63d01 100644 --- a/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs +++ b/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs @@ -11,26 +11,26 @@ namespace osu.Game.Tournament.Screens.Showcase { public class ShowcaseScreen : BeatmapInfoScreen, IProvideVideo { - private Box chroma; [BackgroundDependencyLoader] private void load() { - AddRangeInternal(new Drawable[] { + AddRangeInternal(new Drawable[] + { new TournamentLogo(), new TourneyVideo("showcase") { Loop = true, RelativeSizeAxes = Axes.Both, }, - chroma = new Box - { - // chroma key area for stable gameplay - Name = "chroma", - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Height = 695, - Width = 1366, - Colour = new Color4(0, 255, 0, 255), + new Box + { + // chroma key area for stable gameplay + Name = "chroma", + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Height = 695, + Width = 1366, + Colour = new Color4(0, 255, 0, 255), } }); } From 2be3a8184d0c2e3726a29683d3c18cd2d96ca9fe Mon Sep 17 00:00:00 2001 From: Tim Oliver Date: Sat, 25 Apr 2020 00:15:37 +0800 Subject: [PATCH 0445/1117] Removed modifications to drum roll object --- .../Objects/Drawables/DrawableDrumRoll.cs | 22 +------------------ 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs index e416c23c30..5e731e5ad6 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs @@ -36,14 +36,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables private Color4 colourIdle; private Color4 colourEngaged; - private bool judgingStarted; - - /// - /// A handler action for when the drumroll has been hit, - /// regardless of any judgement. - /// - public Action OnHit; - public DrawableDrumRoll(DrumRoll drumRoll) : base(drumRoll) { @@ -103,27 +95,15 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables protected override SkinnableDrawable CreateMainPiece() => new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.DrumRollBody), _ => new ElongatedCirclePiece()); - public override bool OnPressed(TaikoAction action) - { - if (judgingStarted) - OnHit.Invoke(action, HitObject.IsStrong); - - return false; - } + public override bool OnPressed(TaikoAction action) => false; private void onNewResult(DrawableHitObject obj, JudgementResult result) { if (!(obj is DrawableDrumRollTick)) return; - DrawableDrumRollTick drumRollTick = (DrawableDrumRollTick)obj; - if (result.Type > HitResult.Miss) - { - OnHit.Invoke(drumRollTick.JudgedAction, HitObject.IsStrong); - judgingStarted = true; rollingHits++; - } else rollingHits--; From 477fe72fcf8d4966ea25f7ee476305b0a2019112 Mon Sep 17 00:00:00 2001 From: Tim Oliver Date: Sat, 25 Apr 2020 00:15:59 +0800 Subject: [PATCH 0446/1117] Changed note playback to happen on new result --- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 66894fe883..a03010af00 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -12,6 +12,8 @@ using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Rulesets.Taiko.Objects.Drawables; @@ -222,24 +224,23 @@ namespace osu.Game.Rulesets.Taiko.UI barlineContainer.Add(barline.CreateProxy()); break; - case DrawableDrumRoll drumRoll: - drumRoll.OnHit += onDrumrollArbitraryHit; - break; - case DrawableTaikoHitObject taikoObject: topLevelHitContainer.Add(taikoObject.CreateProxiedContent()); break; } } - private void onDrumrollArbitraryHit(TaikoAction action, bool isStrong) + private void playDrumrollHit(DrawableDrumRollTick drumrollTick) { - DrawableHit drawableHit; + TaikoAction action = drumrollTick.JudgedAction; + bool isStrong = drumrollTick.HitObject.IsStrong; + double time = drumrollTick.HitObject.GetEndTime(); + DrawableHit drawableHit; if (action == TaikoAction.LeftRim || action == TaikoAction.RightRim) - drawableHit = new DrawableFlyingRimHit(Time.Current, isStrong); + drawableHit = new DrawableFlyingRimHit(time, isStrong); else - drawableHit = new DrawableFlyingCentreHit(Time.Current, isStrong); + drawableHit = new DrawableFlyingCentreHit(time, isStrong); drumRollHitContainer.Add(drawableHit); } @@ -249,6 +250,9 @@ namespace osu.Game.Rulesets.Taiko.UI if (!DisplayJudgements.Value) return; + if ((judgedObject is DrawableDrumRollTick) && result.Type != HitResult.Miss) + playDrumrollHit((DrawableDrumRollTick)judgedObject); + if (!judgedObject.DisplayResult) return; From 364f5bf7885ac853a968b7796cdbd37f4f006b1b Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Fri, 24 Apr 2020 22:57:23 +0200 Subject: [PATCH 0447/1117] Update osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs 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/UI/TaikoMascotTextureAnimation.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs b/osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs index 922b2d08b6..2c04d3e1dc 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs @@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Taiko.UI { public sealed class TaikoMascotTextureAnimation : TextureAnimation { - private const float clear_animation_speed = 1000 / 10F; + private const float clear_animation_speed = 1000 / 10f; private static readonly int[] clear_animation_sequence = { 0, 1, 2, 3, 4, 5, 6, 5, 6, 5, 4, 3, 2, 1, 0 }; private int currentFrame; From 4b60be87b591a2bb6d8b1b5378790e7a0509e478 Mon Sep 17 00:00:00 2001 From: Joehu Date: Fri, 24 Apr 2020 16:34:41 -0700 Subject: [PATCH 0448/1117] Move unranked label under multiplier number to avoid width changes --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 30 +++++++++++++++------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index a7b7e50422..36d21c8b46 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -268,18 +268,30 @@ namespace osu.Game.Overlays.Mods Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, }, - MultiplierLabel = new OsuSpriteText + new FillFlowContainer { - Font = OsuFont.GetFont(size: 30, weight: FontWeight.Bold, fixedWidth: true), - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - }, - UnrankedLabel = new OsuSpriteText - { - Text = @"(Unranked)", - Font = OsuFont.GetFont(size: 30, weight: FontWeight.Bold), + AutoSizeAxes = Axes.Both, Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, + Direction = FillDirection.Vertical, + LayoutDuration = 100, + LayoutEasing = Easing.OutQuint, + Children = new Drawable[] + { + MultiplierLabel = new OsuSpriteText + { + Font = OsuFont.GetFont(size: 25, weight: FontWeight.Bold, fixedWidth: true), + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + }, + UnrankedLabel = new OsuSpriteText + { + Text = @"(Unranked)", + Font = OsuFont.GetFont(size: 15, weight: FontWeight.Bold), + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + }, + } }, }, }, From 3cc0b21eaef64e58730d8a7732230290a1dc10b1 Mon Sep 17 00:00:00 2001 From: Tim Oliver Date: Sat, 25 Apr 2020 13:18:02 +0800 Subject: [PATCH 0449/1117] Added more smart checking to removing rewound drumroll hits --- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index a03010af00..cae6b1c80b 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -295,7 +295,10 @@ namespace osu.Game.Rulesets.Taiko.UI if (Time.Elapsed < 0) { foreach (var o in drumRollHitContainer.Objects) - drumRollHitContainer.Remove(o); + { + if (o.HitObject.StartTime >= Time.Current) + drumRollHitContainer.Remove(o); + } } } From c1c930c472208bd5b0fddde8389863863c06de93 Mon Sep 17 00:00:00 2001 From: Tim Oliver Date: Sat, 25 Apr 2020 13:47:20 +0800 Subject: [PATCH 0450/1117] Fixed linting warnings --- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 2 +- osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 203d37006a..d1d2571ec7 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Taiko.UI [BackgroundDependencyLoader] private void load(OsuColour colours) { - InternalChildren = new Drawable[] + InternalChildren = new[] { new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.PlayfieldBackgroundRight), _ => new PlayfieldBackgroundRight()), rightArea = new Container diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index b5a8f1604c..7727f25967 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -240,7 +240,7 @@ namespace osu.Game.Beatmaps.Formats } else { - if (hitObject is IHasEndTime _) + if (hitObject is IHasEndTime) addEndTimeData(writer, hitObject); writer.Write(getSampleBank(hitObject.Samples)); From 37cc1ed5a244cae4d2cddbf2c8dc54e9c2ea5c34 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 25 Apr 2020 09:45:11 +0300 Subject: [PATCH 0451/1117] Fix potential null reference while hiding toolbar --- 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 227347112c..bac73c4379 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -148,7 +148,7 @@ namespace osu.Game.Overlays.Toolbar protected override void PopOut() { - userButton?.StateContainer.Hide(); + userButton.StateContainer?.Hide(); this.MoveToY(-DrawSize.Y, transition_time, Easing.OutQuint); this.FadeOut(transition_time); From 1953c8fc107d87a9941597ec94235a180f05b339 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 25 Apr 2020 09:53:09 +0300 Subject: [PATCH 0452/1117] Fix ruleset selector not receiving key input on toolbar absence --- osu.Game/Overlays/Toolbar/Toolbar.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs index bac73c4379..301747acbc 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -33,6 +33,10 @@ namespace osu.Game.Overlays.Toolbar private readonly Bindable overlayActivationMode = new Bindable(OverlayActivation.All); + // Required for toolbar components that need to listen for key input + // to invoke specific actions while toolbar is in hidden state. + public override bool PropagateNonPositionalInputSubTree => true; + public Toolbar() { RelativeSizeAxes = Axes.X; From f0ebbb1807aaf293751bce16c6c08c7bae15ea6d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 25 Apr 2020 09:54:37 +0300 Subject: [PATCH 0453/1117] Rewrite toolbar test scene and add test cases --- .../Visual/Menus/TestSceneToolbar.cs | 62 ++++++++++++++++--- 1 file changed, 52 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneToolbar.cs b/osu.Game.Tests/Visual/Menus/TestSceneToolbar.cs index f24589ed35..8fbbc8ebd8 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneToolbar.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneToolbar.cs @@ -5,13 +5,17 @@ using System; using System.Collections.Generic; using System.Linq; using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; using osu.Game.Overlays.Toolbar; +using osu.Game.Rulesets; +using osuTK.Input; namespace osu.Game.Tests.Visual.Menus { [TestFixture] - public class TestSceneToolbar : OsuTestScene + public class TestSceneToolbar : OsuManualInputManagerTestScene { public override IReadOnlyList RequiredTypes => new[] { @@ -21,24 +25,62 @@ namespace osu.Game.Tests.Visual.Menus typeof(ToolbarNotificationButton), }; - public TestSceneToolbar() + private Toolbar toolbar; + + [Resolved] + private RulesetStore rulesets { get; set; } + + [SetUp] + public void SetUp() => Schedule(() => + { + Child = toolbar = new Toolbar { State = { Value = Visibility.Visible } }; + }); + + [Test] + public void TestNotificationCounter() { - var toolbar = new Toolbar { State = { Value = Visibility.Visible } }; ToolbarNotificationButton notificationButton = null; - AddStep("create toolbar", () => - { - Add(toolbar); - notificationButton = toolbar.Children.OfType().Last().Children.OfType().First(); - }); - - void setNotifications(int count) => AddStep($"set notification count to {count}", () => notificationButton.NotificationCount.Value = count); + AddStep("retrieve notification button", () => notificationButton = toolbar.ChildrenOfType().Single()); setNotifications(1); setNotifications(2); setNotifications(3); setNotifications(0); setNotifications(144); + + void setNotifications(int count) + => AddStep($"set notification count to {count}", + () => notificationButton.NotificationCount.Value = count); + } + + [TestCase(false)] + [TestCase(true)] + public void TestRulesetSwitchingShortcut(bool toolbarHidden) + { + ToolbarRulesetSelector rulesetSelector = null; + + if (toolbarHidden) + AddStep("hide toolbar", () => toolbar.Hide()); + + AddStep("retrieve ruleset selector", () => rulesetSelector = toolbar.ChildrenOfType().Single()); + + for (int i = 0; i < 4; i++) + { + var expected = rulesets.AvailableRulesets.ElementAt(i); + var numberKey = Key.Number1 + i; + + AddStep($"switch to ruleset {i} via shortcut", () => + { + InputManager.PressKey(Key.ControlLeft); + InputManager.PressKey(numberKey); + + InputManager.ReleaseKey(Key.ControlLeft); + InputManager.ReleaseKey(numberKey); + }); + + AddUntilStep("ruleset switched", () => rulesetSelector.Current.Value.Equals(expected)); + } } } } From b50e8471d20fd113db31027692d472524b0d02f5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 25 Apr 2020 18:23:09 +0900 Subject: [PATCH 0454/1117] Reword comment --- osu.Game/Overlays/Toolbar/Toolbar.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs index 301747acbc..1b748cb672 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -33,8 +33,7 @@ namespace osu.Game.Overlays.Toolbar private readonly Bindable overlayActivationMode = new Bindable(OverlayActivation.All); - // Required for toolbar components that need to listen for key input - // to invoke specific actions while toolbar is in hidden state. + // Toolbar components like RulesetSelector should receive keyboard input events even when the toolbar is hidden. public override bool PropagateNonPositionalInputSubTree => true; public Toolbar() From a756486a4d74073f7e131ff47235ba91d892649f Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 25 Apr 2020 20:35:46 +0200 Subject: [PATCH 0455/1117] Make settings section icons actual drawables. --- osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs | 8 +++++++- osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs | 8 +++++++- osu.Game/Overlays/Settings/Sections/AudioSection.cs | 8 ++++++-- osu.Game/Overlays/Settings/Sections/DebugSection.cs | 7 ++++++- osu.Game/Overlays/Settings/Sections/GameplaySection.cs | 7 ++++++- osu.Game/Overlays/Settings/Sections/GeneralSection.cs | 7 ++++++- osu.Game/Overlays/Settings/Sections/GraphicsSection.cs | 7 ++++++- osu.Game/Overlays/Settings/Sections/InputSection.cs | 7 ++++++- osu.Game/Overlays/Settings/Sections/MaintenanceSection.cs | 7 ++++++- osu.Game/Overlays/Settings/Sections/OnlineSection.cs | 7 ++++++- osu.Game/Overlays/Settings/Sections/SkinSection.cs | 6 +++++- osu.Game/Overlays/Settings/SettingsSection.cs | 3 +-- osu.Game/Overlays/Settings/SidebarButton.cs | 7 ++++--- 13 files changed, 72 insertions(+), 17 deletions(-) diff --git a/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs b/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs index 56e93b6a1e..214da69dfb 100644 --- a/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs +++ b/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.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.Framework.Graphics.Sprites; using osu.Game.Input.Bindings; using osu.Game.Overlays.Settings; @@ -9,7 +10,12 @@ namespace osu.Game.Overlays.KeyBinding { public class GlobalKeyBindingsSection : SettingsSection { - public override IconUsage Icon => FontAwesome.Solid.Globe; + public override Drawable CreateIcon() => new SpriteIcon + { + RelativeSizeAxes = Axes.Both, + Icon = FontAwesome.Solid.Globe + }; + public override string Header => "Global"; public GlobalKeyBindingsSection(GlobalActionContainer manager) diff --git a/osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs b/osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs index 1f4042c57c..b2814907eb 100644 --- a/osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs +++ b/osu.Game/Overlays/KeyBinding/RulesetBindingsSection.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.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Overlays.Settings; @@ -10,7 +11,12 @@ namespace osu.Game.Overlays.KeyBinding { public class RulesetBindingsSection : SettingsSection { - public override IconUsage Icon => (ruleset.CreateInstance().CreateIcon() as SpriteIcon)?.Icon ?? OsuIcon.Hot; + public override Drawable CreateIcon() => ruleset?.CreateInstance()?.CreateIcon() ?? new SpriteIcon + { + RelativeSizeAxes = Axes.Both, + Icon = OsuIcon.Hot + }; + public override string Header => ruleset.Name; private readonly RulesetInfo ruleset; diff --git a/osu.Game/Overlays/Settings/Sections/AudioSection.cs b/osu.Game/Overlays/Settings/Sections/AudioSection.cs index b18488b616..5f55b268d0 100644 --- a/osu.Game/Overlays/Settings/Sections/AudioSection.cs +++ b/osu.Game/Overlays/Settings/Sections/AudioSection.cs @@ -13,9 +13,13 @@ namespace osu.Game.Overlays.Settings.Sections { public override string Header => "Audio"; - public override IEnumerable FilterTerms => base.FilterTerms.Concat(new[] { "sound" }); + public override Drawable CreateIcon() => new SpriteIcon + { + RelativeSizeAxes = Axes.Both, + Icon = FontAwesome.Solid.VolumeUp + }; - public override IconUsage Icon => FontAwesome.Solid.VolumeUp; + public override IEnumerable FilterTerms => base.FilterTerms.Concat(new[] { "sound" }); public AudioSection() { diff --git a/osu.Game/Overlays/Settings/Sections/DebugSection.cs b/osu.Game/Overlays/Settings/Sections/DebugSection.cs index f62de0b243..a3ca6cf6de 100644 --- a/osu.Game/Overlays/Settings/Sections/DebugSection.cs +++ b/osu.Game/Overlays/Settings/Sections/DebugSection.cs @@ -10,7 +10,12 @@ namespace osu.Game.Overlays.Settings.Sections public class DebugSection : SettingsSection { public override string Header => "Debug"; - public override IconUsage Icon => FontAwesome.Solid.Bug; + + public override Drawable CreateIcon() => new SpriteIcon + { + RelativeSizeAxes = Axes.Both, + Icon = FontAwesome.Solid.Bug + }; public DebugSection() { diff --git a/osu.Game/Overlays/Settings/Sections/GameplaySection.cs b/osu.Game/Overlays/Settings/Sections/GameplaySection.cs index 97d9d3c697..38f68b97ba 100644 --- a/osu.Game/Overlays/Settings/Sections/GameplaySection.cs +++ b/osu.Game/Overlays/Settings/Sections/GameplaySection.cs @@ -13,7 +13,12 @@ namespace osu.Game.Overlays.Settings.Sections public class GameplaySection : SettingsSection { public override string Header => "Gameplay"; - public override IconUsage Icon => FontAwesome.Regular.Circle; + + public override Drawable CreateIcon() => new SpriteIcon + { + RelativeSizeAxes = Axes.Both, + Icon = FontAwesome.Regular.Circle + }; public GameplaySection() { diff --git a/osu.Game/Overlays/Settings/Sections/GeneralSection.cs b/osu.Game/Overlays/Settings/Sections/GeneralSection.cs index d9947f16cc..bc87281f20 100644 --- a/osu.Game/Overlays/Settings/Sections/GeneralSection.cs +++ b/osu.Game/Overlays/Settings/Sections/GeneralSection.cs @@ -10,7 +10,12 @@ namespace osu.Game.Overlays.Settings.Sections public class GeneralSection : SettingsSection { public override string Header => "General"; - public override IconUsage Icon => FontAwesome.Solid.Cog; + + public override Drawable CreateIcon() => new SpriteIcon + { + RelativeSizeAxes = Axes.Both, + Icon = FontAwesome.Solid.Cog + }; public GeneralSection() { diff --git a/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs b/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs index 89caa3dc8f..3ac6686e36 100644 --- a/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs +++ b/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs @@ -10,7 +10,12 @@ namespace osu.Game.Overlays.Settings.Sections public class GraphicsSection : SettingsSection { public override string Header => "Graphics"; - public override IconUsage Icon => FontAwesome.Solid.Laptop; + + public override Drawable CreateIcon() => new SpriteIcon + { + RelativeSizeAxes = Axes.Both, + Icon = FontAwesome.Solid.Laptop + }; public GraphicsSection() { diff --git a/osu.Game/Overlays/Settings/Sections/InputSection.cs b/osu.Game/Overlays/Settings/Sections/InputSection.cs index 2a348b4e03..2c395d325a 100644 --- a/osu.Game/Overlays/Settings/Sections/InputSection.cs +++ b/osu.Game/Overlays/Settings/Sections/InputSection.cs @@ -10,7 +10,12 @@ namespace osu.Game.Overlays.Settings.Sections public class InputSection : SettingsSection { public override string Header => "Input"; - public override IconUsage Icon => FontAwesome.Regular.Keyboard; + + public override Drawable CreateIcon() => new SpriteIcon + { + RelativeSizeAxes = Axes.Both, + Icon = FontAwesome.Solid.Keyboard + }; public InputSection(KeyBindingPanel keyConfig) { diff --git a/osu.Game/Overlays/Settings/Sections/MaintenanceSection.cs b/osu.Game/Overlays/Settings/Sections/MaintenanceSection.cs index 0f3acd5b7f..69f00d1549 100644 --- a/osu.Game/Overlays/Settings/Sections/MaintenanceSection.cs +++ b/osu.Game/Overlays/Settings/Sections/MaintenanceSection.cs @@ -11,7 +11,12 @@ namespace osu.Game.Overlays.Settings.Sections public class MaintenanceSection : SettingsSection { public override string Header => "Maintenance"; - public override IconUsage Icon => FontAwesome.Solid.Wrench; + + public override Drawable CreateIcon() => new SpriteIcon + { + RelativeSizeAxes = Axes.Both, + Icon = FontAwesome.Solid.Wrench + }; public MaintenanceSection() { diff --git a/osu.Game/Overlays/Settings/Sections/OnlineSection.cs b/osu.Game/Overlays/Settings/Sections/OnlineSection.cs index 80295690c0..c22174f050 100644 --- a/osu.Game/Overlays/Settings/Sections/OnlineSection.cs +++ b/osu.Game/Overlays/Settings/Sections/OnlineSection.cs @@ -10,7 +10,12 @@ namespace osu.Game.Overlays.Settings.Sections public class OnlineSection : SettingsSection { public override string Header => "Online"; - public override IconUsage Icon => FontAwesome.Solid.GlobeAsia; + + public override Drawable CreateIcon() => new SpriteIcon + { + RelativeSizeAxes = Axes.Both, + Icon = FontAwesome.Solid.GlobeAsia + }; public OnlineSection() { diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index b229014c84..3edfc4346f 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -19,7 +19,11 @@ namespace osu.Game.Overlays.Settings.Sections public override string Header => "Skin"; - public override IconUsage Icon => FontAwesome.Solid.PaintBrush; + public override Drawable CreateIcon() => new SpriteIcon + { + RelativeSizeAxes = Axes.Both, + Icon = FontAwesome.Solid.PaintBrush + }; private readonly Bindable dropdownBindable = new Bindable { Default = SkinInfo.Default }; private readonly Bindable configBindable = new Bindable(); diff --git a/osu.Game/Overlays/Settings/SettingsSection.cs b/osu.Game/Overlays/Settings/SettingsSection.cs index be3696029e..97e4ba9da7 100644 --- a/osu.Game/Overlays/Settings/SettingsSection.cs +++ b/osu.Game/Overlays/Settings/SettingsSection.cs @@ -11,7 +11,6 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using System.Collections.Generic; using System.Linq; -using osu.Framework.Graphics.Sprites; namespace osu.Game.Overlays.Settings { @@ -20,7 +19,7 @@ namespace osu.Game.Overlays.Settings protected FillFlowContainer FlowContent; protected override Container Content => FlowContent; - public abstract IconUsage Icon { get; } + public abstract Drawable CreateIcon(); public abstract string Header { get; } public IEnumerable FilterableChildren => Children.OfType(); diff --git a/osu.Game/Overlays/Settings/SidebarButton.cs b/osu.Game/Overlays/Settings/SidebarButton.cs index 68836bc6b3..30a53b351d 100644 --- a/osu.Game/Overlays/Settings/SidebarButton.cs +++ b/osu.Game/Overlays/Settings/SidebarButton.cs @@ -11,12 +11,13 @@ using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Graphics.Containers; namespace osu.Game.Overlays.Settings { public class SidebarButton : OsuButton { - private readonly SpriteIcon drawableIcon; + private readonly ConstrainedIconContainer iconContainer; private readonly SpriteText headerText; private readonly Box selectionIndicator; private readonly Container text; @@ -30,7 +31,7 @@ namespace osu.Game.Overlays.Settings { section = value; headerText.Text = value.Header; - drawableIcon.Icon = value.Icon; + iconContainer.Icon = value.CreateIcon(); } } @@ -78,7 +79,7 @@ namespace osu.Game.Overlays.Settings Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, }, - drawableIcon = new SpriteIcon + iconContainer = new ConstrainedIconContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, From 801968ed515e32990af009c4bfa2ee4a04c97b5e Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sun, 26 Apr 2020 21:17:40 +0200 Subject: [PATCH 0456/1117] Remove un-needed RelativeSizeAxes specifications. --- osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs | 1 - osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs | 1 - osu.Game/Overlays/Settings/Sections/AudioSection.cs | 1 - osu.Game/Overlays/Settings/Sections/DebugSection.cs | 1 - osu.Game/Overlays/Settings/Sections/GraphicsSection.cs | 1 - osu.Game/Overlays/Settings/Sections/InputSection.cs | 1 - osu.Game/Overlays/Settings/Sections/MaintenanceSection.cs | 1 - osu.Game/Overlays/Settings/Sections/OnlineSection.cs | 1 - osu.Game/Overlays/Settings/Sections/SkinSection.cs | 1 - 9 files changed, 9 deletions(-) diff --git a/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs b/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs index 214da69dfb..5b44c486a3 100644 --- a/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs +++ b/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs @@ -12,7 +12,6 @@ namespace osu.Game.Overlays.KeyBinding { public override Drawable CreateIcon() => new SpriteIcon { - RelativeSizeAxes = Axes.Both, Icon = FontAwesome.Solid.Globe }; diff --git a/osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs b/osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs index b2814907eb..332fb6c8fc 100644 --- a/osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs +++ b/osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs @@ -13,7 +13,6 @@ namespace osu.Game.Overlays.KeyBinding { public override Drawable CreateIcon() => ruleset?.CreateInstance()?.CreateIcon() ?? new SpriteIcon { - RelativeSizeAxes = Axes.Both, Icon = OsuIcon.Hot }; diff --git a/osu.Game/Overlays/Settings/Sections/AudioSection.cs b/osu.Game/Overlays/Settings/Sections/AudioSection.cs index 5f55b268d0..69538358f1 100644 --- a/osu.Game/Overlays/Settings/Sections/AudioSection.cs +++ b/osu.Game/Overlays/Settings/Sections/AudioSection.cs @@ -15,7 +15,6 @@ namespace osu.Game.Overlays.Settings.Sections public override Drawable CreateIcon() => new SpriteIcon { - RelativeSizeAxes = Axes.Both, Icon = FontAwesome.Solid.VolumeUp }; diff --git a/osu.Game/Overlays/Settings/Sections/DebugSection.cs b/osu.Game/Overlays/Settings/Sections/DebugSection.cs index a3ca6cf6de..44d4088972 100644 --- a/osu.Game/Overlays/Settings/Sections/DebugSection.cs +++ b/osu.Game/Overlays/Settings/Sections/DebugSection.cs @@ -13,7 +13,6 @@ namespace osu.Game.Overlays.Settings.Sections public override Drawable CreateIcon() => new SpriteIcon { - RelativeSizeAxes = Axes.Both, Icon = FontAwesome.Solid.Bug }; diff --git a/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs b/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs index 3ac6686e36..c1b4b0bbcb 100644 --- a/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs +++ b/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs @@ -13,7 +13,6 @@ namespace osu.Game.Overlays.Settings.Sections public override Drawable CreateIcon() => new SpriteIcon { - RelativeSizeAxes = Axes.Both, Icon = FontAwesome.Solid.Laptop }; diff --git a/osu.Game/Overlays/Settings/Sections/InputSection.cs b/osu.Game/Overlays/Settings/Sections/InputSection.cs index 2c395d325a..b43453f53d 100644 --- a/osu.Game/Overlays/Settings/Sections/InputSection.cs +++ b/osu.Game/Overlays/Settings/Sections/InputSection.cs @@ -13,7 +13,6 @@ namespace osu.Game.Overlays.Settings.Sections public override Drawable CreateIcon() => new SpriteIcon { - RelativeSizeAxes = Axes.Both, Icon = FontAwesome.Solid.Keyboard }; diff --git a/osu.Game/Overlays/Settings/Sections/MaintenanceSection.cs b/osu.Game/Overlays/Settings/Sections/MaintenanceSection.cs index 69f00d1549..73c88b8e71 100644 --- a/osu.Game/Overlays/Settings/Sections/MaintenanceSection.cs +++ b/osu.Game/Overlays/Settings/Sections/MaintenanceSection.cs @@ -14,7 +14,6 @@ namespace osu.Game.Overlays.Settings.Sections public override Drawable CreateIcon() => new SpriteIcon { - RelativeSizeAxes = Axes.Both, Icon = FontAwesome.Solid.Wrench }; diff --git a/osu.Game/Overlays/Settings/Sections/OnlineSection.cs b/osu.Game/Overlays/Settings/Sections/OnlineSection.cs index c22174f050..150cddb388 100644 --- a/osu.Game/Overlays/Settings/Sections/OnlineSection.cs +++ b/osu.Game/Overlays/Settings/Sections/OnlineSection.cs @@ -13,7 +13,6 @@ namespace osu.Game.Overlays.Settings.Sections public override Drawable CreateIcon() => new SpriteIcon { - RelativeSizeAxes = Axes.Both, Icon = FontAwesome.Solid.GlobeAsia }; diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 3edfc4346f..75c8db1612 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -21,7 +21,6 @@ namespace osu.Game.Overlays.Settings.Sections public override Drawable CreateIcon() => new SpriteIcon { - RelativeSizeAxes = Axes.Both, Icon = FontAwesome.Solid.PaintBrush }; From a436f8e6d42165e5bf392fb2cf2be3a9378e72b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 26 Apr 2020 22:54:01 +0200 Subject: [PATCH 0457/1117] Trim other leftover RelativeSizeAxes --- osu.Game/Overlays/Settings/Sections/GameplaySection.cs | 1 - osu.Game/Overlays/Settings/Sections/GeneralSection.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/GameplaySection.cs b/osu.Game/Overlays/Settings/Sections/GameplaySection.cs index 38f68b97ba..aca507f20a 100644 --- a/osu.Game/Overlays/Settings/Sections/GameplaySection.cs +++ b/osu.Game/Overlays/Settings/Sections/GameplaySection.cs @@ -16,7 +16,6 @@ namespace osu.Game.Overlays.Settings.Sections public override Drawable CreateIcon() => new SpriteIcon { - RelativeSizeAxes = Axes.Both, Icon = FontAwesome.Regular.Circle }; diff --git a/osu.Game/Overlays/Settings/Sections/GeneralSection.cs b/osu.Game/Overlays/Settings/Sections/GeneralSection.cs index bc87281f20..fefc3fe6a7 100644 --- a/osu.Game/Overlays/Settings/Sections/GeneralSection.cs +++ b/osu.Game/Overlays/Settings/Sections/GeneralSection.cs @@ -13,7 +13,6 @@ namespace osu.Game.Overlays.Settings.Sections public override Drawable CreateIcon() => new SpriteIcon { - RelativeSizeAxes = Axes.Both, Icon = FontAwesome.Solid.Cog }; From 3c1730d0caaa933263b950a20ba1aadbde16522a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 26 Apr 2020 23:59:24 +0200 Subject: [PATCH 0458/1117] Expose SongBar's height --- osu.Game.Tournament/Components/SongBar.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tournament/Components/SongBar.cs b/osu.Game.Tournament/Components/SongBar.cs index 8d766ec9ba..e86fd890c1 100644 --- a/osu.Game.Tournament/Components/SongBar.cs +++ b/osu.Game.Tournament/Components/SongBar.cs @@ -22,7 +22,7 @@ namespace osu.Game.Tournament.Components { private BeatmapInfo beatmap; - private const float height = 145; + public const float HEIGHT = 145 / 2f; [Resolved] private IBindable ruleset { get; set; } @@ -157,7 +157,7 @@ namespace osu.Game.Tournament.Components new Container { RelativeSizeAxes = Axes.X, - Height = height / 2, + Height = HEIGHT, Width = 0.5f, Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight, @@ -229,7 +229,7 @@ namespace osu.Game.Tournament.Components { RelativeSizeAxes = Axes.X, Width = 0.5f, - Height = height / 2, + Height = HEIGHT, Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight, } From b9e0fed4679d5b7c6193cb1c48ef8659eaa59f3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 27 Apr 2020 00:02:58 +0200 Subject: [PATCH 0459/1117] Use SongBar height instead of hard-coded dimensions --- .../Screens/Showcase/ShowcaseScreen.cs | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs b/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs index e48ec63d01..aea531e88d 100644 --- a/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs +++ b/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs @@ -3,6 +3,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Game.Tournament.Components; using osu.Framework.Graphics.Shapes; using osuTK.Graphics; @@ -22,15 +23,19 @@ namespace osu.Game.Tournament.Screens.Showcase Loop = true, RelativeSizeAxes = Axes.Both, }, - new Box + new Container { - // chroma key area for stable gameplay - Name = "chroma", - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Height = 695, - Width = 1366, - Colour = new Color4(0, 255, 0, 255), + Padding = new MarginPadding { Bottom = SongBar.HEIGHT }, + RelativeSizeAxes = Axes.Both, + Child = new Box + { + // chroma key area for stable gameplay + Name = "chroma", + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.Both, + Colour = new Color4(0, 255, 0, 255), + } } }); } From 743b4f05b3640e728319a1eed500e73bf760de63 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 27 Apr 2020 08:36:36 +0900 Subject: [PATCH 0460/1117] Rename out of place variable --- .../Skinning/TestSceneDrawableBarLine.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableBarLine.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableBarLine.cs index 58d69fc32a..cffe47f62a 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableBarLine.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableBarLine.cs @@ -94,7 +94,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning private BarLine createBarLineAtCurrentTime(bool major = false) { - var drumroll = new BarLine + var barline = new BarLine { Major = major, StartTime = Time.Current + 2000, @@ -103,9 +103,9 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning var cpi = new ControlPointInfo(); cpi.Add(0, new TimingControlPoint { BeatLength = 500 }); - drumroll.ApplyDefaults(cpi, new BeatmapDifficulty()); + barline.ApplyDefaults(cpi, new BeatmapDifficulty()); - return drumroll; + return barline; } } } From 75c588c59d8f8548b8c1b307bc7a5dc3daaf91e0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 27 Apr 2020 08:36:48 +0900 Subject: [PATCH 0461/1117] Remove stray space --- .../Skinning/TestSceneDrawableBarLine.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableBarLine.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableBarLine.cs index cffe47f62a..70493aa69a 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableBarLine.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableBarLine.cs @@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning [BackgroundDependencyLoader] private void load() { - AddStep("Bar line ", () => SetContents(() => + AddStep("Bar line", () => SetContents(() => { ScrollingHitObjectContainer hoc; From dc6acf6ec9f133225a45391723c7b2b9647688fe Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Mon, 27 Apr 2020 01:40:57 +0200 Subject: [PATCH 0462/1117] Various code changes, fixes --- .../Resources/old-skin/pippidonclear0.png | Bin 0 -> 72589 bytes .../Resources/old-skin/pippidonclear1.png | Bin 0 -> 40613 bytes .../Resources/old-skin/pippidonclear2.png | Bin 0 -> 73308 bytes .../Resources/old-skin/pippidonclear3.png | Bin 0 -> 34541 bytes .../Resources/old-skin/pippidonclear4.png | Bin 0 -> 71177 bytes .../Resources/old-skin/pippidonclear5.png | Bin 0 -> 77056 bytes .../Resources/old-skin/pippidonclear6.png | Bin 0 -> 78392 bytes .../Resources/old-skin/pippidonclear7.png | Bin 0 -> 77056 bytes .../Resources/old-skin/pippidonclear8.png | Bin 0 -> 71177 bytes .../Resources/old-skin/pippidonfail0.png | Bin 0 -> 67970 bytes .../Resources/old-skin/pippidonfail1.png | Bin 0 -> 69118 bytes .../Resources/old-skin/pippidonfail2.png | Bin 0 -> 73351 bytes .../Resources/old-skin/pippidonidle0.png | Bin 0 -> 68649 bytes .../Resources/old-skin/pippidonidle1.png | Bin 0 -> 69329 bytes .../Resources/old-skin/pippidonkiai0.png | Bin 0 -> 76964 bytes .../Resources/old-skin/pippidonkiai1.png | Bin 0 -> 75434 bytes .../Skinning/TestSceneDrawableTaikoMascot.cs | 191 ++++++++++++------ .../UI/DrawableTaikoMascot.cs | 99 ++++----- .../UI/TaikoMascotTextureAnimation.cs | 39 ++-- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 14 +- 20 files changed, 207 insertions(+), 136 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear0.png create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear1.png create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear2.png create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear3.png create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear4.png create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear5.png create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear6.png create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear7.png create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear8.png create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonfail0.png create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonfail1.png create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonfail2.png create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonidle0.png create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonidle1.png create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonkiai0.png create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonkiai1.png diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear0.png b/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear0.png new file mode 100644 index 0000000000000000000000000000000000000000..a5f4d03e2a61075513c85f24ab39280c31f591d9 GIT binary patch literal 72589 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�F*Q&jpbtTl0ro%YGC`VnPkLo4Qt@L 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 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear1.png b/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear1.png new file mode 100644 index 0000000000000000000000000000000000000000..b239cc561c5cfccc67f04badfa8ab1b54ef6852f GIT binary patch literal 40613 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�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 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear3.png b/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear3.png new file mode 100644 index 0000000000000000000000000000000000000000..31e0b91e9fdcb82988b6b8d7b2505a38fdf70358 GIT binary patch literal 34541 zcmX6^WmFtZ(@hAvI4mqqaEAqgTW}5TzG!fF3$nO`5S&1eK!UsL;vU>RxVyu*&-?wD zGiT;UcTZQe{B8?Cj@M5A|s6r^i3qj!Svkywg9qA4+b@6<%&!1)v%4aD#&@zEdc_GPDx>sPnG zdzmy=l18AR{51%00wOc9B57}N=4`F~0uS5{j(-i2*gE_*xO*39bHQ`r^kK^@mkEP0 zm9q$e;ZGI$(;E2xZOeXin-_$hnJ?#7(9^(l86Q%i30#g z-3DnDU_eyI3<4Ah)sCSwz4AT8KMsf&h&+B6G6horiDoOhZ~@SvG)uNZ{Aw3C|73liK4v!P|37+6E5~+V+6lQ`c1B6&(Vc)F+ z3Q)d7cmj#{ug?M|-M*Pk{Z>QJh`v-cEvOu51&9z3-~1KI{Q`E9FyEU}B`82^(OSf2i7@JKKk$e2}B$Dhe!I*>;Gj+#L)F#-)1$v^6D&rV5YU@dYih8rL zlU0cf6x}aD#ao+4U9!EM_nsb2nu&=~VO+{ZyYMDUK!|jK*k)j8?4x!z|Jb;+r#OY{ z2+{x>>&I-I%CJ_}tf674T%2NCiHV<)svq~Z$q4FAbgO3=C4giZ{^ZgMJC3meq)}JY zXbHcF6Dv|p(sui{e1qXu&yq!;L9aTJNLdB+hEgctLzITk-%sd%&{=2)0HkTn1Az*&?@ z5`C{!_#gl8gsx*3QC)qn+{ZgfUTtyLO8><5~QYaniWPBt(u zr=RRA#`c&Ee<9ox;|xvjwsckXoS9fLpaqqca}4V9A0aEyi6o|^-MA(3dc*@WAM9}_LcC?j)3Sg(Fu&iLP0SofSfZVC5)b0-2Y zf`A4R^wI@$Fx5!dr1reUp@0Z6xe5q>XQUA=RqW~ggV#n5ftHIP6FLiswLkl`y>G0$ zrILx8a3863$piUPXbn|}F&53*SIO00_n4n=j9+WNWB>8i3UDchLw_-8+__PiBNv)-lg@JfsM-Z3PSN2HoRet_0q(6*P zDt`d{y|~6%9uB-DiEy-#b4jGag@zd``av{G`;V9=n*tA%TCWC|P|-}IWz=QlEJ`uoAfRq}TP_OhX&nyz6iERPzPqTBaW03v*^B_-RJp~Y&P zl<9PvKGOEr@7{Rc7$O_SX`eSRt*0#W-@H+sXR-tS=q<_V8W6mOy2^N1q5k-_g+j-G zvbX08LM-{>%tNfX{MbPEewY$1o%Lo<`7{6ht%+QuVA%Aulv+AKjdkdgynK~6D?&tJ zB)4~qPv>wqe!Fq|88<Ns_ZS6xuyqiWVa1`O$l62NnHB{8E)N%}~*+c^Z2J+N;~a z2?!FtBAO+ij}^K9lUJBpMnk1cuk?8Wv-`vkQIY{Gyru>}IxO4i(4;Q>+?S8ne*`c*Vfq;Ugszls!< zC=0>hj$xU)g-x8z=owJ(;P4*;C5Z1XT^Q$JI~1qmV^{>Qq-xFVS+qU0u=|mw+5vG< z29f_C-|Y8p>M;GQp4YR}Xp<@)HbNWU_&FICkdxBEJmtHSc5DrHVgR4UJvWd;`yo>&iKe*7Gxx797WsnZZw!lXkhz166Dz4bDpF z_)Q~ql|_+e`Ip zy$mD0o65jr}CzN4l^ogkUQ*Yn4HRnH{1%9THV(5&}_dk(S81Bux4eIHu5bX<3 zVjAzq1`t_!feiV4v{!q;10Y$h7u! z5@Dl@U@XL$D2u-8r~&itHi!e#yWzlZVMS}+OQD_{VlL|AAw|v}EuG=$o0ut1$_ZGzkd?@<(7A565T!d5Bg`XQup6;rdrO(9w zTF%R)_6j{XN9TR3f5S`nrFOdiXhLnYd7#m0(WIyjprbywB@p@z!+JAyf$|aisnNSgh9P}mjSb3B9A1=Q^|52Z7Ia7t0XhU9clJV_Q z>kJ?WfF_dM4r}yB5CQ$LV=#_W3H+h6MfI#S#{TF+7yo)j&U>^H_QS?ydk2eFT;f%~ z=`r8(Bln%x2?|CgBHyWLqi;;b9szqGU64MT8g;vDARziu1B&{qYkQ9Zc&eW;Jr(ga zE%6IKmBRTBS%LONfMRzE@O_|U+93X?<+h1wF$`St=AZpMb3e^r**}xw>OzbQcujR~ zv2TTgJ9Ic;STDSXmj!>4P#(PYVnx|ns4ZX?u`L$gailYM>ul^kI1-8BL~!xXDu;$h zVDETAM*6exc2qh31JHxuPB}>Ksp8w*w94rLOZ?BmRtTx%!+N7f;U}!IXodhR6G;`B z-PHx`jo#brs%))P%Vd>G>tl6$0S`o!%c-GjOo<=F_ z3+o%pezReEeQg&$wjz_~yjkZAIHjTeMxwLEBqQ(1s=%ev&1Op)ed{1|vCl|r0dQjA zLr2@Or;Yh~s-#dE@flyg2f^y1$)8k3*+!h+w0}G z2&?#yUc%#8Y~*7`2vx)-x7zSIv(l`(@W0;%DY^j|ik7jT7YP%B>fhwEulMY6TR$&K z>ONkO>eks|)<>s^7HOs_gG!Q0D1D`GLe^$(VSjqxQ^mO#lDyS+>c8G|Y?O77JxC;v z{3HOKKXUKcv9_T$(E5Yr;VMab`q3Q_BUQwt5oXPaSf(#y`tjCkF;3LrhC&{8k-FAB z)Qet8MMLO=oNIG@5I1Jj{L}5T;)f{v(AX`8^RvzI(x4er?<*+Smy-+BjoV&#(hG;& zUI9EJCUziJA6_*|WVQkbVjjj>A(8M)U8$VDFEZ)lJ?#$(PbSDjE5YkdWR@_>3C%0bOBZv8`qs|^96AYi;hPFjUJX*zM2m|c zoiaZ8jwdrdxo7I_r9((=Ksc5CBxCInH){7|ZEJa;{6AgtU)Ru<-pA(qH(@Zo>K+>Z$F;KF1#?EDUorymd|AlG z#&H{f>0F0Zh>bpG`nz33{09<^4R+8_vB1y-UbY!StG<6zkMTXF;9U_Q)EOd##Q966%WannDC5uv**KT_)wd-l zF4J^RrsG1_;)4QFZ8?cNS|r_=;^_OGYh-IlHa3*}ShYVhcUFOF$w!-2iT-0T!++hu zwg16FMv?@GcSGw!@|H3^WS#^M*W?jD?<^_&@eA=+@oY zJ(1Tfm1m~=K!aa4_GZk101iLAUeO0Ii8Ru8i<~dJeI|+3FFIs$v3-f9|G129TldLV z5dPMg%evbwQjAtK0yK&+MeP#8hhy2juMLijZ)j3Zm&5R-M^+ZWpbRzB*$#9h%V4-S zjr9JF`EyM{_a6YDAV?CJib6LfDxfRFsi-w>_~qo@RK~h@1Q{gFROa8RIZq#&?#|Xs zpg{(aRJn+7brrR|sD^z2T#E^VaMMCgA z8CZ(r+>A+{bb?|F+DfYS=P8Z`m8%Y)sgy=;>}Rmv`NsA9jX3#u8t=(sH65AHpi#BA z0Xm;sq|;?-B^2<@xNLHD-7olj7UO(uI;QbWM8UO5rMaR`-6a{G2E-$UGk1ouD-y^q zbK(2tg8^|em6@Q`2Qw=6j_+IB_5~LIwx$kGyJT6~Wo_lD`f5+Z6OClnQ|`yJCrm4I zL=B>$93sI%X8A`KtXYp-xl`}ctlGo!Oyj3CrjZ>vm)4uFB<)w*@C>*%hiONTIp0(nYu=MLM9%%Pe~=#Zu?h`c8gJ6+VULK0xte?A|HI6KcVp{eHwf!z0k z6Ic4zXUB7@+w;sWMx^h~!u!ugDNQzh>Zv?Laco4pj0)usUbU!A`-NZYNtY#YP$$ip zZ1z-FRElmBsh1L%O!kW4B>7=Wt}UJR6vPO4939C;$`joBu?NmyHBfL_?k+-=l}5lI zNse%ufbDNsSq=^rAw2&nMF+a3^Ga3yk*kW7kOaUKUUL_{ihr}kyTVvAhHse4x3BAc z{ovEvdw%^iXjNH@sYL^Bd0C9JMhWmp>vo0 z&eM3j)9T&ZwT>HF?O#9evh9QGi@p=kTcsUsTgWvPguAJb&Y~PP#ISF6A!gA$Du& zpP#QJ3THB9Jl+YZpz4%(fqeHU`(Y7v%)h!r> z$&|%a!r9&0(9+>rE*7_`Ql6S=nrc!>Z0^rSR40=F?i2^(*pB=gTMn|-VwwjplK7aC zG=etkAXd?AAMzK4nvtKDHfgK1D2u{1L}M?u_KKmMg9H>t?2-}v?On-iD3ScVcz_zR zCtONs>PZuEhp>=}UwL5|lJDlq$|0DkfNl9x{c^&WP*DzoKHTlBx^WL^EOoc95r>q_ zHMJI&6VMvnl?>xCKLXIAlvW?V-T4#sXt<7*YkGzTClwy#OcU(~chN>RspxS3y8PxAS&b3Fl)?ale?BjIEWZmB)U9 z<)Eyp=`6|yZaG1BHr+|dsE0-4M}i`v0d{-BN(Gk218KpAvjc9a%~3O~aLBj7h+ExW z=;1WhG4~r#+u0g(QuA8dvq<1tvjixWcWialzLP5kBYeL3qD+Akg?~Qw5RQls)W?xsz=187q?lv zmTaRSH!LB>`Bmp7udGvDz6lb=@1F?D* z$ji-7dpiBZV72r=q(aE@5@CvNsPH>LZFX`~;J|AZMFiwtc z3VPpVE(Dm8RXpoI>hRNU=uKitJ`i*77=8{Y^jGe)Hrusx zg4ld0`mQVLt)BDeY;VvfDY{QW{tzAK3YtLSGy$Zha1C1)WaZZF$#S-Wj4exwz- z_bcWHnKtY#o%@z7=qH07a(HcLWu{|90)xkIvsnkRsDR5Ib30x)*N+7?49&t9 zag7h2<>h&t8=II3O_$Cd6-@e!N{z*PDW7d#2W<&6zT2Cgg3 zy(VW~RwLURu8B;DWB4kD^uXRIzu{;2FC9836^)*pBO=o(ek8q#pyA8w>AqH#=0C%D zsCWO@Z{hLM>5j!V!gGYsVFamWeL3vbc;dIq`In`#oXvyPB2r+aiki}d{P$X+LT;XM|iXB*g;+n#89&t%7Ho?Bsjxpy#RR_G8% zV`WU zi^Ln_WqZY|ki(IkQ?8iH!qe&({qio_WCd^(NCYAp)sQ z#Lc=7q;}|Z;FOr57#4W2cYuLrM?+DTWV%JSVlC;*;6L)RhOc*+v)0U3$MH$J9apkF z$-2!9{ZdySV zYCB5)XT6zsyD?&Qml@92nE0G-L)Q=1cse>d?iXmmH3162mg$?YWsJEu`}FX-#nD*# zjB(**Q>~&)uB%2@+@=IyC^y!97BULod0`ydX&4$vyG?U5W3<2OAZP75Ah+M0DEb?{ zdn9m|N5aDJ^o2sB+aZ+Y_FvKFcErwRr(-#dR)Fr;)2AysB|GJ|$_dVD9^=-}yvT@d z#ow@mICRGrhTWmy_CIA{-+dr4+dWIEw$zS40a1SA-++Rzm3|4Eu=HY0gv?zD7u<6; zqb0qZK=tk9qTA~GUwoBg&EuF}x>s2=tsrO!Y9d;|5g*2F{V{SC6$?ehNF#F8pBsbp zET(p2dSSS_v9|?TvbI}NQ&vH6d^|%J=J()zQu0(3Q&rVnZ5pf1vg-8he0bmet7gX^ zNObTnr~xB}C{%q@fF=2y3E$LrOgb8!c&Zg$25U>?PZp0I_QoluVqNn7eFEm8ko=vT zJuMKxLP52t`#Zk8beu3SVIiTRXywaDRz_T+%^Vl%+;|}Br?Qe0m#zy;KR&9?e#-Hu zJBq+NAD+HCtAskNnMlmTWEek2Xn!eoDaf^SM9-xi5PKB8llEr8K*}poBL9}~8jvvE zEA7i+4O_G}f$d-nQ>(YFNUr-H*DO{^Kzf>N#4vKsf7Z(pZ@B(hJGn!-Jn?Djd+wbR z<0hSI#+YEaNtrk2zv!LQ&!yz9;v=-b2XUh))jmxiOuZZFc6QD-Gzpjd@Q+$sOW-Kk zwH*zEgODI}khMvJMugDV@S&u+(QwfkXz1p^HaNfYgVPhVW3K*qiMAC-nwmia{ZwTo=kjfP{HMez3Bg41fJ zhL^AdT)d?2)?08@a}g-}LaA=fvEz?NwBH8)@a^4x+K6R{A}Dpv*bg(+_=WOFq}squ zZ!og(c%q}?`9*fSD)7*ybg^CEkFo{B535C~XA#4OAX)(jQu|u=D(S^HYlA+FhqaYD zCpvwjRE)+q9)wGiS^XSV-#^<&*)f^oovdn6-h8XQ{K5GfTsWVN8-`YiR>q$#f1~+L zwVTwb8kH`Sx{7Wj&RoA5aeKj1VS2LMG+%4~4vRro`+gVcK_*2|h-?{s{%zV`LKAu7`TqoElCD=~%*e8f_# zs^sB7v!n2nSW<4JH@tpw*dv87g&MM(4YxSi^vg8Y$8>g@mVfUb3C7YuwpSY$obYQ$ z#Ry`+*ZywX$BL041DN_Jvdo9-)bR|!uDe#jK3OW|uvw@u(!toXY1!WvXYlMEJ~egA z=snzD-$b7qKAnyyi01AmCbt$T8r7p=)#=N#gHQ&cS7>Cf?YOC&?#YWI%gF_HM%3?yXCgWHbj2#3qBOQ~k{WxyE zFL_JDU1Y_wVRYGn@|R^`QlOcE0qIrP`n1=1NO4NevtY3cK_&k-?^~<&8?ChH5mu6x zbs`Fz(Vnkoo@s(J_1F^=IYIp!_QMoCA|fFZxeN?B+8EYKP61nz_6qg27-awc1-!c* z&|$OGd8P}g-^EjJ^YO=*@{iTmFSEqbL@0vNu%F-}fRLqY% zg5FOF?*2BJVh2NAMbjnJxU*os=Jw+gdEo{J*ZZ5o_Wm#TB)5!les}EdH< z*L6sXRXsDKn2AV%i~(&-E`C$t!XY9WTDSW&_{M(z9`EHuiu1=cc+_>6K<3?SXR-$WlLZ zx+E8#?-7p1m1pB0@AxEBv662QT#CLpu%zyB7oyxXe6hCMfqPX=H<!9<^Y%QAM~wAxUJ6*2`U}ZTPK+-G(~F}7e`}DiV?l2k^A@Hunr9p zs0__}DH9dI(&{T&1Ujo*f-h3T$2E!+$SgsXSuce>fAf+rvOBSV?pXpqnpW;_5kc%$ z-Ja_d>P)uQ!(%&W>JO8e6;#3T=C|Z4Y&4Tz^gsH(6|GIgU%I(&92M)Jn*q~mHvMsl ze7ZiTu_@71bmVa?J{V_*(Yu68ucKF8m zqJ4Ey!xQ$nsk}ap#8PTKV}2F3x;Hga&5k=4t=C|Mdm**JF)%9KerAS2bckNom!*P0 zbDKKLp{xJRax?{#6x-Z7+S1twCHHJYgJU+HWz$;;nb+RrmrD52n&+GK(XK9l^s)LT zZmh9GQ(Yod(#MAXW{LVx?|L0Mctuu8;%5reo7rCS-h}Z{S%|TyYuEshsR(2vCC(*I zddD9JuFzkx2*BiSIk5XE<|z2uZ?N*pn!cBRqW)V20yV%BbElv<2d4U9k9N20xp)GA ztSZ+wazL^muWie%Q?X0hbinNP9`0wooJhOgr?z#PjW!Ev?vB__B58PSijJrknyKVc z!xYEk5|Q}Bx-X@yq}I(iR{7ZXr@_*YUji@3i*k#Ag{rO}KaytK_fQEn4F!EHagd2Y zL{EE|#@%B-58MkTiVP4RqPf^nno8rvLVBk9om>ApvYF1}&k{3nDVP?20mX>Z_d>bx z>XQAxp}SU|_0P7QE)G+q*U_UQ>zVwJ`a<@>^u+P+@WV?pQB z59n%0>HKHi*zk!qAb6IZ7f^i_CWz$y?9DbKYi`P(1)2teTlg8m#LCakGuSbH;bUmU zXq&RZ*NEC1G^v=4<;ey!?eq&kt<)yuKU5Qp_m9V_}`3AIBY&uPS1d;Q-46ITpvRzqu%5{dw!hL)S`9Ew23nInBX||m)J?Ju!T!!w^j=C6a(|T_XL;2FtFObE+4uV9SRQgSm;Z_W?AF_D zUoUe#2NGL9#M5umr0UL9>r3~xhJR#nc_-Pg)$Y17IoB?s8E^Tk1&iW^agWbfNSpBh zVJ(~Hdh}B7(Cc_5k9;ny-bgSnoniMy!G1NibFWFR1UJse^zQMWl5M$FKO{D*VGJqm zdX;vvPmIVx$W8n@df|Q?FF-F`nJUp4v(y)#7n)Z;urGqeCTySQEX+-e_Q!K&*jgz^ zdE+zT=fiYnSxH+~W*L&2PC1uc$^p(NrSgGKv)-w-8;Q!Zqg_I^pA$}wnNPBJ^f@0( zNf+GA%;m;$8C_c!W@X* z)Zvm@`O4z^OtAq&57Zo4;zMN+1D^*P*J_82KNs_lTNR7tezcPeIAghtYL zQ+M<;N|D@fMk_0po?G1)z7a!g);;*5M1bo3u_A{;EVavAAvnQ4O;4WxOZasp!N+LK ze5C!A`H6XWkD974N9&tbqAO!YcHh^_SW%jZ#o_wjt`VLqI27tGw^!M}rN~sO?BzG< z+4YMf zB26~$fy3a+mhWZI=u1^nf=ARV02?G2HJION=d@pYHX6ME_aZeBfh}t5zZZ6eQWtq4Q?LzQk<)2&&7PajRNcLRr38Gsxk1p7&A? z4?05S?8Yy-s2PEr&5wG`C7;Ttk%jhhRX;XHVoYHy+omgutg;@7!RXPqkWDdi3I^tV zxZ>nZ??Thu^P(>zE{a`HScdV8Lkt{S#Ql_1qGX#rgwat*ulG#T+l?t)1_k)*EkiwA zzkI#prRvZD4lv2B2{WzPu~lSc7rB)A!c~*#|unG18~UA z2bpHWQo19e(q!ED-B_~k^5}TusO}bV9N`yVsNVcMWc>>s@e^Z4v=aaJZxU)C#Ci*qcqHy<+&0@dI3#&{`@z-(DZ;}@ zE`($ngC+;s6C0neAD!3o#9Y+UEF0ul?5ER55)q~tMTlM~Uckm|qPE^Zpy|}uXI29* zqKNv^DMwVa+cUOpqQ!2EfaR396nyc>BJVc+Wdk*AM#m}1>Cg2=gm^cnbrf2ZDt!3D zKO9ygTWXHoSj=>DQubELIF?u3A!Y&QrzrhB;OKP}K|bx^jfAO+$;Cz*U6-|h`Il88 zU9z^Rhf-9cW2>YTI(8;fGzJIa2UE)U6i200r3uwRry1hh@_Iu#eY4o>-w)otjj*Z8 z^NggsipJ=;8@&bLcM@Sb<_t1Nx{w#?(_%-~x?i=8s@m5kV|1Ki^9=xwCtmD7v(6Dx z?whUs(GYBPGvw6xDxBYVt+E} zVI^&0m3E3U*m26Hclp1wH2+5TKN~=1Tc7Dtb;pMoIQ3AKvgu5^gFh9bCI_iaV`r^| zY&dbDDg_eaP1)A-V%#cTxYuo)?sfgk4%QVwSNrzvrF1|hJE2yF3Je4l)Ad4ivYx$; zm}~C+xBtQ=`rKn?N^ojoVmNKhb|I2b3sprF9+9+27dKHm#RHhbBJDCU!VYpFD)*RIbiI~p|SpbPVn|8Lf*ud@IP%nZc zlk!Td-WmmO&kDM7cCu}Xgj&%Z;$BI@`TmB(y!h%$z%PPl&BQuBYBP=@S*@ww8AROG@PZ0x z?fQATrutPW!(jhFnrvV?D=NDiq544_dHv8^(|L^^{)p}*Ca^Exc-%XB)4%VRoNbG% z7Z|~TBPcpbR-P2!(Czhnc6Z!kfzc140<2`B;pZ$nAC_oW56oQMYCHslglIVCxL(V- z6yOILP|O*?&6RQ_)tzT8mj`fw@4%3|mIrnt$5&D4!E535OY1`K8!yya|1jWx)%-j- zWC!}QgmbK$`m*X)AFPu7Sy|GN->vkh>$#$1qM0vDuNX+-Yadc?PY+ET z=KhR`k1rDs2?ik*hj%Rz&y1)mmF7&*G0i>V127i6;1pdqI zv`=*xF*lTo{5K>V@}*5~$KBwiidq4n_{&?G2cNzq?q>cgQuvw?0ON7qy?0Msx@p+SwSQtpT4`)LCaa9OOViYd-m$u< zIE2)xSj}puts^6^hbcTv?9E?12*$Ovx!&gbN?^sEfSVVbi?^J!fXUSBD=;ACUk zua9l5D9LP0uZJuEq}w`s@g2iG4B>}421G(nwlBaw-l)Ne6tVcxuEkgl3JOE07&D(h zAkm4xlC;L~1+SnF&)|mDSFxJe)ADg*`e+BAyL&}A)eIrgtJEUnQHIOX(}ANOph+cs z%NT8P%WV0D+{H%1%j;%FZBPV_EI(w5*ho}n)Wm{=54zalYfFsa+U> zjjsk4={o;5_JccT9~|kozgY*coo^O!)lIbJWp@SV82&U+*VNQC4|XBUp_8Ol_GBHa zXwGcbMO9*o%I!Qu+wc8v)Kx%5DUc{Wc*6IqzZCzJ*_UVLeOfUd%!OV;?7mMuWved) zezeQXye7-6Q3=-Dp%?%C-!mcUPzkjS^f6QNR9DcF5Tk)6$kkR(c$n9UqVNmkcG}p+XEBz54-{dbHRgr)@L{+-@7T zAZh}dSBw-rM(pTl3Tgp#?83@}aVD-*Tq;29m6=?q^2J4qhN(BygV<@G;0<3x^`_d_ zlz7AAhIc2*?9O~zPg7Ie7l|fjIB6ewV)WHi5*M(S4WNc=nlu6{^!5 z_JHmzx%B%{>&PS=P(F%=#o0PbUgd99?srGNTmYp|%*03_QARo8D#hw{xR~CbdPi$M zfD#>*`erVTgefa1UeQkQei3_7gYf_p8M|+xS5FrcsSk&mbHl)%gKS$}^}TSwqapki z5j_vwE(0t~P?_$fVc9S-%U&9xDc-AvA9hjN)Zm4pEj828Iq%2>_HwhGItiC@@LmiK z2=s}}7D0rFm^unuyvoHP3BXu6-sS9ouN=OaG#O*_mjZ(`IyOAP>C17?#}o*4+F)|P z%XZt4gs}wPMOnj`mo$&x-XKso@{sE;6-N#X~FF{&J8-XPT- z-0M;`y&ENIMaee;9gjqNy$98^C@Pm*dcE1)F}`ojYrS&+M~nxv+U{aJV4_1->6S(f z!*DCc>>YeC4p-b%5-N{8YL)o6#1O*7sBU=otCnL{6&~N2n-YV+_61J}O$|^NN0V7!YI(u00hy?U6pKXnj5svN2(Yw_pLk9}m^zgC>Au9i%&haO6v1E3sH zMvaoG0L7f8#Bt0K^{lM*ycCr^w*L>Z)S+~7-iUQyPt4aLWm5$|@S=-ksemR5Nr%u1 zo=9%q@s)`@l-P*FfUbY1n_wvX^1hY`~h*_qu+Q z#pb=JNnrQ=P1-s+Fy1nzz3~JQ>iHJzwKb+S0u*|I1x2aZ5wu7$88VVUqWvy8^R$9o zAMlOe_JKDM?=Aa4iNCSI<96Dqo$0K zQtiziTiDRjQJ87RkY8f7LiAVt&kdJvRQ3*Ps{dx%oKeJnki;tdM(bpO*jmc5Eg^89 zjEbStn#35aHm$>@(bSiO#E~TApD}BHyeI62yE%zhXU;*T2~Yg`?VNx-au?imW5xFukzTlUukd}&KU!uzOqBTsE|*vA%^mmv2u|FKR)=NhUR)z( zw;P{T`_lW|-w#w+yb`H+8B~{kG%PZE)YZu4zXfi;7a8Y0Tw7Xs_v2AJJFP78_i+TS zbv^6APM492LF&p<+Xv!%Pa&~tu3l6-O^Vo?cZeyhsb5?m(iE`?schaYdx0q7HS}BF#Vge?Pa= zTozp{E&6-SRHDmb3_A(u=R+;jM8zcuh-^F&y4&=lxsIC0k5F-PvE`ZBKQ2z-FS__3 zh9h_O|JC}DO~}A*kY11?;W9Q5)qq)j>EbDU*=__EK4?7pv3fuTupd_J6np$G1)he) zj|~;lX2C`x=CQqwjQhQNqyCbZiHxit`UFz08onGh0t#t`dk+_xs`l=M5)gxIm2 zRPg;VhiwbpTE#_M9tffzK5|Z$zT1kdK;0F>+_w@Da^v5)wV0+*3ueWV4U3>$tSj}; z{HH~lV0wSP(s!&r-|MTX8xFoPs!`_k#ts+zQce^UOMyW#tRSvd=nz^rdFOfX(*a2l z59@e_Vz_&Fg`ms0UP?#7vC!Gn-XNV3xclwfk#5iS{7`;Qi4ZKremF6UNsU3ng>@li z-GO)+Blj&q1yjwf9NucHFLM00nxgyOP!>=o7h+@)y0#7%R=#i{;HTz5pAa*vQ~S8{ zh@Z+XWAX6KzM6C181uO~fB z-4vz{$QU&g3z90<8zwDh8{2bcHY0EIZzU2eJ1TV{N4DEOp(+=6@S3xtv5iQeK(%M` z^>5ab?Ci6e8!S4jLTdV|P5)2Mm%RFuF$KV#?&BT^JdNX@`#%6fLA<^c9omUxTj~)J zO=>npAYsjVoGP#gl{Z|_`TDE0V}GKQP;G7t2J@a$yu0dN52> z+O^sZIYisj?Uy?2{voh^FM!qpqi!$Y)V$&$Zz;@3ML$7e4+j-QV2 zM6u0tAqRym6~_Eaf-njUle`UHV2BhLhDVfOefsmUtYoB~wCh6C2@mmupLK2C#KK&p zrd878)V>XZJS=mP_>ZL@uQ+F;P}C(Ur)}Oedw!aYE}bdWBf}$(64Ga0 z?aNNa$G1FcJ+r4}Q$K`=c$K6GeM&sW+*J9rAKn{*zP91li7BV8M+(I`q@)mWnVh(6 zp_!*IdXDaEJv(#u?uo`pL;fYQT}SL%zaLMIz6!UE8;1Wb`VfDf*sm|6X@zxdt+20z zu^X0dJzH~j-AeDp&dq7aIT-#R{xDwcKZR+*A-A*qWX(cs{FCBpkGq=-nuJ;wGv;MG zwPcBQTpq0=L(s&}){_*Ul`8&KH5Nv3ZYjw;f*7+cE6~C}46Ume+Fu^Wm#oAoL&rs~ zFYwlbx8t47ORzN~7P*GK!31o;qlA4S>~~>1t>^55qUg|jVg0ic7t+u@7{)m4HloX> zCrOUwq~qaP*WmEMIO{VuLR+@ee{|fzJ=UYL_<6e_JkYkxWdBWi2}g0~_zQ46#=d*mGayi0P-{<8YGS3k3j_s)!^b+S zI-xKRZu{12#|p){qUhhndQE1}Py1=ptY>FVPJSVdMzc+}(8?zW1<7fmLKPeLo2GEE z$7HoI&P&)OjBh$7jP*>8d*K@@tV9i2V5qLV2n-}SS%{gh6~gqX&&y*D;ErJqC?T4SiU&0`m^0jH2qP}oz%M-H` zl4FV#igQD8MiinlY(1ncy!_B1(lUX!I_zDv97po#fQb4ayr8BnBGBT(OE6*PZQ65I zSij^LGaBoZF(hJ|qEc}o4Ow9L*2~==5>;;P#hsdfQfb z`C7JjNG{69v1R{i$K}y`;$Q^&+Y)FeQp7)z!_ZTqsCx=`uELQ*+t7-Z!s~FU!Cusd z7yqV@e`(|4kH}(w%(!YIZhY!ic-R)a=cFVqGQ&n(&XlIGROz)CX~+UYq{v96WvIjf zKtBs(UPkZF+nbq)xgS1?FE=knPNro-WTtrQ@eK?y%cpJ2AVm1sCV!pCN`rF@Ls5mI z?#W#Bi>Td}y3MU(yIy>Wc3d5z<1(=Sn9UP!)~p#K#*T-Nr%9WGH@t8gOOQ1&7 zSSK2~z!17>R91=%dz3hOh*ywVZ$8?^Ks!zz#mYF_sN4fldn%lh#I_+KqP;%P<5W>D zJa=;#fI?Bnm@^R-Z!5PQOn&Hp`GwZAGiU$dSRBY^TWk@j_RS-4;jGEpIT_h+wDm}X zL)qCQLbry!9o5#0#vm}1beWH#YeJD>K8AhJ-wNZ1L!RqGW8AbmDm?+)_Efx}RFl2~ z5Mu3hDim6lw7=qI9ux{kQ5d%!2lMs*3-k_ejgGA=Us|C&_I~#R;;r4>txVo%9^48Z z!gG=MgoIg-G}yyIk>P6M6qstqV~s&zh!h#diZbcT5Q!cPlfH|EF?hB0%~?5KkcmBC z|7txeOZQ7JKx-e%=wA|@bV6%Q6pFee|NFP}ya|^a>2b+b@Gx11P@F0B)ORmqRj|-9 zv?)4VHN$#l!6U#jUBC*J=e+k|B;|QBbWIpC(SzA8%;Jg|$P-@4q1DT*XJzTqE({$) zBdljaV6szDlEO5PLQyxAWS_>d6Smx6SC3${omBPGmZjMpm56r!3uer7^iWqg{ud!cVkZ zs*{DeaNF^ZcC1j;3Hw&yK(=lEQJ*$lF|<$h*Wf*{eK(F4S;l=eclJa?t8QhypnwRw z-3e3tKdzqHN(vRtmPQ*-!H56*mcF^#>Ec{O|H7FD<vx(k^T|vdwx1^z;tJpfS}Sy&IdHjjcyGjmScmrjZyjqKoy+hJUCZI=60XJ=5w7 zjTJ?CFs0ycqp&a&PI2LeyIKazK-#)|9&bn?(by)e^N*pY-%*zWD)uMHme(?6NdGV5z zgfs*;u?v&RjM>ic7s{Fb_cYiTO(kjnt?!rZEeQ438)<^eHXVq6HmWSzN&WJzJu8i;miH zy10JFwy)JTZ!3>Z?ZeSo+!)Eh^msUDsa5V2j=?$OsPJBX(}q!y5K)}RJ@7c(Ox3Tl zzrFq;PL^=W9$k4?wC^z(0Y2qNKKqydZ9URqTqT!f;FqbE7t>e;hQG_0f8jEAT7oyi zI{;0CExBHXKAkSU458MMx(DK;v14cXi%N0Tu-1JBYd45Q7iGhJ`A6EZLQ&iJ{{AL5 zb8@YfiILqgX1d)l{xc#g*NnA4a)nC^9ejcidD+F*GkdsB%)BLETF*3?l;nza?AvB< ztZfMV8PeDVhDj3&4BZqhM|)AvQGC!DF>@-q`8UyyPoyMb%O~Gy$K?@t!DzJcw9Kxi zl%LpI@c=!AqNbQmZp7AuYqZI@q_vAT2Hf`$S~Rf?m8cGXFWrV68Md`wI)}Byz#(OO zkK&SZFttN(>`T>G3t(8ZRTu}XGF0Lyc@-jAVdj=@i4+*UkGX5Ne%2!kZ6gBEr&B*o zeU24o;rQ;26|QpEs(mYTZ(<9Rb|fB$b36wKD-^Yd+n?`Z(`gPqx3r2=x(~&~i-v3G z)n)BV&p|GY$zd6t`V1+vd*i;N$(Yfj7gomD&7`6G#H9u*+>+!>Xp92Gdm;r!=3w;x zIA8m@78+e<{s%2xytLqQ%aOfUy|Vm;rFwR3iXM|Dqp6c6ox`;`oPT>qJ60%a1J7+g zVfl{LT27Gk^a#e_C!eU%*05W5p1}GewkZbfJp&LnX@d34!t!OiaCN^P*pOfmG%H-y zit8$InhiY|j*4fvq&yfa6c`yIQD7{6(SpF>vTo++iw2`-kY%gb-I0bxQ(Gp*SndnTT2RhCc3vJBv;nIvEp3Rexi9viyk`vDYvI zyJJ`cI`rtE9p{;g@Zke*W6mX`us74zQzej|3zrR$dmb?dV{wD3$@$>HFu|gatM>A- zcMnOBpF8@Ey;!`-3Aw@x+_ZfaVv{U$HcB&cP*?QrHAGv9o$L@d$8)_ciKbB06w^=7 zVo9`Zr;EY<;kfvJ|EjZePysmih&@PJOw+At3pkm|@17fa_f6<%vJ92k9)A)` zUzx8Rm!^quRYNYEg5J$qYv)YFomw_e997$UR48nQvJ}ng#gcRy}attM_T=)rDiZ)-3-?^Ts8un>0h_ z?w$DP(-*NK+HScK=2z&g{3wk3^6-xo7k(KT8r4W#NMp^x7}0;Dmzz6MPL)46jyHAa z{SP738v1NLc?f@gw^BPUO<15OLI#aMuW(C`(y8KHoLKOZxU=*m6^d$5lmoYK{)?q2 zExTTjF@Y^{$@?F`*Guo4s21Np_XRd3+i!~;pO%8p|Na?U;_b$HF~7nmI5vY_(i9iI zkn!lYa8$LOJ!5$4r}tJbaf?o%X}*ym(7 zr^n(*zTGB-^ibH^aydsPemIvv?t4_Ntw}1QF$xSX2=5l%30^3D=GOY72XM?i6DLmC zW~Ukn_Vd7?i?0TkB;*9M4F|X4SXB9f#MgGc>N@levGhK1Im0z8zl3vk`5~A>Q5xr^ zFXM+*-yo*gvgdh(vnMW}bS0+Fv0uirI{fh7FW7impGzcycxMa>Y_4@+6xjBgrg*TR zt`bJCg&V7}_ehFMjk=`Eqp=DMFCdds9Ole{0J_Mm`DX=weduNDSy_hMat-?USQ2YH zQ)2P^f_RH&t79kR0KJSWtV=?L3 z2g?kEJ5!P~^00CK`#5f0wT=u7ZGp*yCSg>#WoU&JhClRL7#=a-!lAyEZem|k8nXw( zJI5?I2MbZ5wTrelITl~8{t=(t`6^G#(5qt;4Cy{p%gHdKzj5DMq{o%-@sMdRK89gU zEIms62NsLhyX{!0ohuaOksq}f>pp!Hhpn5anM5_bsB1rrd*Rs{t@R@L@$q-@&#}GM zGa%f_17ms)#~t6jiV0&cw?3=Ev;jRB8tVwZ2;Ha}bwyW3V;2}1XD$(@&m7wxe*$lQ z^(sER{pB*VrztGm=iZ8rZk9Pmdrut4H}}7y9hXPPZY?ox)HJkm;dnkv!gu4R_rf{d zwug;EAu%8M13O;39ed4m7%Ys7?1Ygw+=0RUEe%yoIIuqk|NiwOxH=N0p`<$cYy89l6`1KD9KxFb~fzosXLbjK{&Fw&W~F_I96s z6`EL=aag}^7rwl=;vOQ7w7mBH*D)s4wiwXwvHMZ{{v0?J>xnhR`HGWhz?Q#`6>r^v zjiT|93=3$EOK-XtGasF4eWqqC`RXsM*|S!->(mOaeCL6QL#E)O|FiAb=|s6Yo}$WV%mTxU$DSm*5fl~1EKK-v!3*YO^qT+0?p3&T^ho^p#d1Al>T|_~=oi{j zJLXP!|7`gO(Fe+3Qj>J<(gN2#`4YOhTQ+Gc5cloJ-OEt2@G0$Fah@VP;G$3G;^*(4 z#Xhseqh+O=(5wSy-2NzLJw6N0HJAz@;b72B-+YeEahCBd{Ol>6`{MeKpViJKC*$n8 zs3|VeC@}Ued@a&g1%^;5WN#94G0c{82=q>2^kkSt*RT5On8SEr?p1i>mS=D(Aq%}a zhGD>fvHH#f>l32!#oR}=$WY0c@RcINq0!vQUiVo~=A`4{&z{8#7tX-azjq<B#1YllzOR&FPU3Nx{0-s8Z=FSK*T`9V?g4jg-83cfwMMjx`zuITB# z24U)L597+)r)kff4e_Uvuw~Z@Y_%>D#a^RH6R*PLONVIZM!tE^Zny>OgG}y9FUBD_ zmDo>ss`ohA$C76X-y|nLaz-Op`sOYJ+yJJRunUB7Ql!OQ5%dX(z*Qrs;`ph2cBKLt*HN)*U!i1FCNFv z0^3pv+!0{Hpb@xe_FWivSX&a3J06t|M^w;sx4-TqE2Qsby2W z`;r-vRa`(UbK)s><%`p7GPn8iD^1?lKp*_q024NHx zzM1txc`%Y1dN2~cnR>whm3M-&&h=ti2%|SMSlB>e^l7;8g^WDec=Ux6%A=TwrOgcWeawb08u}Y{zu@)NLannP%`IWoNSPKB5z*-1j&xoi)k&?AgP~j33?jC_Y&M0lFl6ix#kPkg=J2X9UYebZ3l3^3Jl=~ar6=M zGIH-EW^_4wk>b)>*jQowTaYjvH8YD?Nz2N|w-3IGg$w6n zW2R*o4(Bgj+`Jo_-trdA;eEB|6b*@DIIaE&JAZr?>$BtRVvMdHfw-{W7+mq;D`?Zg zwpm|o@Y!?o@!RLGW2JQs-Ejdeaowk1W9kJxv~vjuk52756w427u|A`HMWzI}9xOXK z_yH6gy4DU0kzAg9@=5Dq!{Di>YIR%*)s+{Q3eM(-IT{Y1<~o85qAC5{m4JWKMW(2@ zSh&H(IF)b`-Z{SLJGQ^|FXiLm=8Dc^2Eoa#37q!q!;zd+6pDX(Z)QA7HZDPANj8cj zyNkPFSxj6}&&fQBoY$_!!j7xt>MygRg; z`h50aj4))P8zOTvvZsl`tMq2Z2xE!S_qaBRkZGuYbKLjo_n0uHkM-HgS^Up#EV*+o zzK`Ci#m^+n$qi#8dcf;HkHD?v2=Q&`R4M8mTybvMd)WHtXV{#ZXqU9$IxZIt9FNh@ zJcj*Q-N?%BuCwXJ3QW0ICtfd7n~h{g8e!cd6A zGU(25Kn%|ruX8Mn5*Q;65*We{rqGlZHpwn+=4P0y(dWKXWG;{Bj{BD`vAfZgvG+(4 zzMXRyzTWhQaFcAkgKb>AF{1NGn6ADDu1$qkq>{k(gQ8qGt^ESW7rl>F$;b6UZNyI$ zmy3k=Ib`mAm_Bo&w(~@7!*J&V(fG{&<10j&Gqu5=H}oEbUJpHr8Ix?`_2ym2aMSp) z*kiw%9eYfa5UUp zO_%q?bKX2I)U;@5Xy(k4gByTE_EyL%u8_L_v^Dc8ZRX+IS@oimEz zvg2pO{`fzvh&`mw^^x@P2*$K&Gcon)yAj#6@?`(n@X38| z3e!CqPA0o`Dso4Rhj+LT8h5)~ zv%-Oxj;%oI*U#Z^5z5LH=Omm8GOe%@>N^MqyB#o=MC zaM6^@&};UM7|^X5qE4pamAQB0v&D8>=g=*txNvXdO~N?MS}x?x3BV2=`(i{1OywB8 z8RlkK!|BOTWSDfBD9moDu)8mwjTe7-w^|d}cn6j&-iohon~hZ|$3^pEbHTX;L}zz@ zbZOE7UZZEg)OS3bylcL0h@x^_u2!yp^e(kokqAI{MhE5G4g@OG5wmc@Gw>0 zdw90|`tf4?I`1v~wtJIMjAC^22*RbqC!xvAYcOF{UwrZL4|wl|2XQ>zwi1AVofKT6 zJ4ydA>wZ@J?E^bDZ)eX+Vm~rmwkav zF$ZzjoM{(3a1PO^ur?SzdoCv4a0w#9*s{=N;8qrB?!T<}hTEc^M{uDKhk4 zxS7%=!kGNk_oaRO{~pH^?>}KZs~(!rJoO2FoBtLzXU5qj6(xb9(0BI+WOp5dVxK1B8`plXMuVoL5WZ{YBX#4?*nE5+_U15k zYky5mqAeLLuHA&I=istO=9D|X#}Vw`8iik|)>33R&R=$0)05$T6Luo=@P)JS z^e^vO&uY%bO-Hfe&G)fm*E$@GJB`En>18Sg@f59Y3zHWDJiHO;;e}vte+2r6!9TQx z__krl4Q`HtmYq@TFG4GiVBwe5&it7AOEFyIcEWqdpGe!k0f&T8A5KfeN$c9RMuLR2 z^zv_lk&~~&wXfW#ZB0`b&};eT$xra_s)bm;Ya62SGmKk|R0IctQc$>;G(#cw5K&m9 z*CKn09NraO8A@Pkh7=i=Kq)fxWTeP2F{0;W?i@Z1k9_|rfB#!V}`7*I`MtG9K39QLi- zhNNYG;_$v5h{;JsYECv%^K+0WDjMVWnnE%17GmV%Y=W;4BX5e4nMFIV~1$r=sAQ8jr%1IHV_>KvHHR z;&U^QkeQC?!W>b{%D)Djf!sZ$83yzmj*<7zMgPI=t$%SmdFCSgvuZE)zyAr=ZCi;g z$)|-fSi~=q)5d=(>;&FLuNP7HbJa7~PcOHwRc(&JGq9`n}jcrP$xG;dIz9a%MR#0emX9F{04`2H5=jV=0AR2kKOOTi?s*0Vs}omPy>4b zGzbjGvCu2wtY?NoOxj8>MH=VK$*o5yL=={qw<-=m3C!73p2)Nl#_6r}WO%NriHox{ zLfpMEM!4hSX3WM_Pt1bJ)iKin=;0)#=OHOocr@v`h}(Ar8Ap#JKRy9Dai@`*5QB`2 zRHWx*B13pQnFV>s6s~zT1*q7pRoI%AR4yS9?MP?L=#~gFM__xi-5yGLFcrLr7NE;JWlM>3S8t&|g7Q*9d71#u=+jd3s{(~{} z`YC7~d1g~OY6~vev1r~BZ2aggtUrCI%=j9@+4XJQ{joWXPKyQ;x9n=(VSVHun0Jwa zLZi1Lg@u=?-c@ELFttNzk>OyxPQo}C?;>Fo8Epomx2MIS33qnK$mX3e_Ra?|ZPsLm z2~B0NmntDO2T3V8q7vjFJtYmP2ah7}WDE)u6Oj>r8W|~xNY6||dTy5R7;=yygdmGX z4}v04LjkBwc=72?g$Qq|rSMQ%hen`v_dW<7F&ym&_d$<#q403P9HGjf!0$PlgthN~ zgEc?T$HtTsWxByhq>C52Hfe#ro%@Tj4Ltb!N7lb+9t$@N+$@Y<49}UorKr$dqo~kG zy{*JbU}_I3GW1qBt$?8ty_iA5IJH0u>`0tl5HN zKYWjMJJ(@*PEwg%7#E}J>K}%Ft-7G=*b6cJ!Rz4ZX~HkBF2LmvU1xn2;)QXuK#ptS zIxh5J7!uWcQ8A(frgq_%<2V_17je!$6TV!AbF?seGzjqYL4cF1RsoGyUh`-oI$_|{ zYcTGPYtW*J;|F!s7ED^kCubu?gkouF*@)kH0O|VHa!vhGZT?pQmlnf zp3W}t5a#Y;f`^MMJX}ri6vv(>H(?&|5(47s;RP=*A9w`>!8f8MTqByoqirj=d3nLx zm*V1srop~w7V1;wf?tk=JwZDT#$)eaYjO1FAF%h>UL4Fw0=IB2U9(!ddZSNx8}#ls z3K!gW6FPS1N+34k6EYCdTwkDyN!;&*vA2kebJK&Nz|^Z8OI0X=seM@5sSXU2*lkUL znJSDP4W@SNgYn}o#;I*Pu|U_Mm1pY?6ck z9Y1gqiSen($;^VYi!(gJL*e4-sV#Zo<>@ZW1Ya+A`1-iP&&#ded2h`pzya40*q%xr7I(%RWQ5JOvc#h_U?{s z3FBNPjg<6k5eAe$Qk2Ug{`SI0SpD-C*qU})TN=>_rvbG03P6{zmgpkHru|iyW8~QG z4p~L*d?Pj~8wZai;@H|vNLaZN(NTL4otA*z!s7C?M2)Vxp17IZFfJexpYGj?kieRa zbNYMvE=-)PuTsZl7P!svdSP-G0QPY!fpHWKKw#)va1tbwy6i#XR*uZa;D-Ja@zJ{9 zc~~hYM#yuB$XLR#+I_ZS0K6xvFSQ!Ec(s49{u~aR3X$Ez!HC4 zgs6v42tir40>_sxMfB-oIFXg2Ey$i!U^kk!T8s&d#GeV=!>?w<2%(D%=ST+)j?$HR z7~?LC<;P|95G63R3tb8JATi&<{0kSPnj%aqF(1G575;P26+Ap6xLVo2f3HK%%GEfu zXB)mfv>BP^e3%RElZmd*&gdlK{lO9K(PQ`|^#8xB(7R`gYAt%zm|+-t=y(c_olL-< zFTcUD4Qmjc6oZp_88~6iLQVlk*jEV$xyd;`?9HJHv7+T4x$-^jtsa+Tzan(Ea7%{8&AL}DroSd;_&3=p-sC6b)i}kxsA!^TB zym;%KIFzj?04t=4yElf1x5JQOLTqlCiT?fC+HGsm7-4sDOj4HCU7nhniS)!oB&^HFuBXUjJP8J^DykbZZLTmB`~!IzZe5=7P^kYxLMLo z!n7sluIxGl&;0Rwl{-OJ#KM1eW5xu#o(LuYIhssbxqY85;(Cn;YlHDaCSky>voUCB zC%BpFXZLSuILI>!_4aBIM;4AwRzWnMoN)icLmxOuRPrA^E@_aeNACX-P=UPS+-S z#ua4aRDKq6i^_HzR|1ur9tu4Z&iSFBFz3OLgk8q;TA2Uf5PfQ72&P8fL4vK45etFF(CNzo%duO<%2StYG(pZt{ zu~2l3^M5EXyw`Ftues(6lXuL+aNU;% zlZR0PQ+qJ~!g!oh0yss0V_(=Gtp(%nKlBd%@0mF~tPx$pTHtUhXXkIiP%v>UbLp&CWzZVLDG4oLfi`i;Iengm4cqR)iRR@X#MTD*ljiSMD zhZm9q*QAJ0aHNMqF_EIeP(%t5zZgG*6`PfY0>fMzXU-23#xedTXJ==;ec$W2>A73Q zY0cn=b9-U~?53V4j1wOlR31hNOzpv#oZbq%ry0xKD2%NentS@;t)Eul;!^tupD78c zxo94yhgK~3T3J9m4exn_$-^jtsU7&GSi$KvF#p1Vb+-u9D)dDyy5NaFmZMikZW392ep|2x zmtMy(UxP05H^R8z4c&ScHVO=zJbnijau$9nk`x;$I2@0|??fS_$Hh^{HP1#m4hn;@ zVDea2bXI(d0lf{1iwtXw;=p6RMq25=$zvKnPn!HUjDkhVt0bkTgD~HwwF39_n~ay% zvuC;HeDn5Cn0e2Y)-x>+!+X9_7?*Ki9)<#0@53Y|Ftr1}6f3nXaGV~%@h_~@xK9+g z$5;MT;{kEEj=2&auldP(rn&YU-6=QJq6fpu!5k1phMo#TBPlkl2>fp487l;{krWxe zo`OT;?_t4^#)>A7Bqw(d6HFEZ-|1`FYZezwu1qwUtR;DKJ{TZC3k1_;BAE9@dzH(JAQGPIoxjsL*5O zb|lQb$?-7!4gB8HK`21NORvWG> z;_hxY@|VK$rLb+njtP_VQ{;t_BEjz@#YPH_6dieNG`^Pap=ij@G8T@~1*PZT zglUUwzy0QSxNeTt$xvm%EW@Fb~7ywWeSeWC&|!|f0F`K z+t;8aukh*_X~u_4)jfk8sl~w^`4$*qgC9-DMWG zGo72li$>#jl}164qGB{6FLHGYM^yqN;jz(-q9Db>_#7W4#&hGb@ildU9B9`;7{@&Q zpD=9{<)DD(cxm|>4C$rmO^x|}-ZES>n*+f$n1`WIuop}wdKpquM5Lg|9x)kW%14Th z^a|@#Xjsq`HHX)a9svc0-NDRdu+ZtgaXa5ov_FeAV58S_2!sw>BNiGKBD|zzz|^(4Ncitd<8{@@EG$53A}{ON(f@*h)*}rL*krzp9`eP)*od*`io!Bb2JO7 z*;XET`Gr`#hYOnN$Z;ENtyrMXrZtiklb^xL%8JX+G*)J5e7+(p@AVa#@qI?2pc%cE zb0rwnL4HT0$Y^907a(a5_nkjGS~vH{q;5m4XLe91C>ZRPWSQfhE9_htC5&B96q~Dr z(X*n^aOeaR((FxSW5{w_Cv{&5O!blCLNA5AF!WlOB;ieDj{FQUwnlJhhB`KAuSQ%>8C9( zS_Moz(`(@}tsLz)L0E5L4Bsh6Yz*1r@q-**|5Xm=%;7)G8^XK=TM&u}vv|B|a&R5T zhikbP;_vQ-uYOvEi4!>1t@`}4?g%D~)RyukEV0b^trEs16xoBsu!3Tvgr@GH7eEh! z_nw{tNAl8JfS;!~p1x!*ZkzWEPtTey`{J-;(K6)kKY;v1@nUJ4H8V;IKPQ^wT%N6v}zo`=ZBhP_A(HROnC*#oDMCA?ypkb|F66x)G0G{~#{!)DHnBy{~~VG?IB4iXzJp z8%l>JewDyfj!|5AGZ^1eNaPG!X6+fn(38>N3i)l_{V;dfWq5Kicbzy3=r7q|4+0a( zx>`hZJF0MxnV>7)K1VC6bA|I`4u!&nPV}6c#4;Sy5SG>BVTU zQU!WZ_s<=k-tM?{{xi7m*8gHuJ1ugPaLfc(TBO%4%MJ@Fe_O4W9VIa3BMTG7g$2hb zE>{aF>jQr`mT4WHP-N)ta@PWi z48tZRGLA!*2R0^De2NS20efaRC4|>sAGUjOXh(E=P+z;}oHG64>+rzCuV8dLZDPNK z4I{rde}mDBsdYVYdGg68t%nVRr=EJsdgM^(xiH3NOGR;EE5$p+1$B9>*Z9u9p}2X- zMY#UMPcY-=X$bJ;+iHhgp>L)PU_d}qH1YJpp`=(8agi2FL7>3#yKtbI9K#~Juj_QQ z=6L|GH-(!Ok>VnAaMOjc;n7{yYx`b++rL|YF$49K+&QOh&$i-=H^rf?>k*&Bp;Q{u z8)L5$NB?qABg>KA-Ci50UZk?HFi|B~m?$pnC8FoTWG)Mqo;e%cqz$gU>nU7u#|(#V zU&;Y@oL#%_gKzNJPw!$!3_Fx`m{pdk2_d5(GCmFFok9QVK> z_Z&UzurUtj32boK(lZ2Q&V>mLdM;d|K@XFko^dB0{^ozyv--)=Q|Y*U=rAmg(MK86 zo#8@O#<^|uW@^ogsr8tvfy3*;SX3tQ=&=kJX6FXq*lh%E`}W5=7Z=i_S4+J1#n%|p zlntp43;r}={GPJ=k)D=A5_jhjymm4dCwmdu=&-9-7ng^p-dyj+g|vzA$MoCpfwQfV zW;v%f#*8#`ODy}fT3(dE*pP*ZtrZIsD=NJfuJxgJ?v?afjJM69xXi>CUd#g505o&AAc>L`cdENwcZojyDvI> z+btu(GESjkxo3H))mUE%j6Je2v4XM-k8|bqsnCOiTVnS6pJ8BkYR|c4;P`%c^v>sv z>cK)nkBco5b4P4pl^1h%NRO6fLQV@|uZwm)M>J`NJ3fA+L0#OI5lzs+m(IBke-|4g zDKNFF2})pWup-KyBaSzri=w+Jk)D3IZuSEhGgg~!QZKmS={cCv!mj&~-CMpX zWuc*HDr$xl7*;@fEu0cSfzg=x!kcK_OplZr1c81Y2=%ucqr>0FhDdrZN?;rWFD@^% z6d10u%?hf`ioUGd5KO$Cx!(E%$J)%i>+#Yc- zUc7F+u+odsFLLwFcEj%*3@2hTv|Td|P;EGrfyU!n9k-$c#)2$N^kCRpag-)K8JL`1 z(5X`&_;}h?;CjV`=bk_llNQZMcp|$VDVp@o6xGKlF!XBqsL?ds&TDKi#3f}QHk~Us z=s0u)xDpr(6c|=Qy2GrTdN*XKn-~0Bu_v$o(yMbgI(yl5J+dNjyavTm3XJk#Y6j~F zg+_XVnztAp(J%!jH7y;-v$^!Kj&V8DT3=U435*5l!N?YiLZcDr>VcM{%Uu_>-oOpr zy0owpLH2ZTJPZYfm4X7J?sfHHxsay2uuPI>$LjlpHz;CP{EKL_-BJ=9&C5lnC^8h7 zS{?bV3Y`U6h@{BSG(1IX)ut22SJiLY_Z$FEH{F9L^j!D^D~A4i6xBlJCm1@g+1HX6 zg~dfUu%4sq8y<)LT!h>LYKe~A`o_4Be667brhF(i8YUN41O?F%sNaNi=!jq!JAsiQ zpY&ez-=sKWq`0tUr@-(LMMXu3wV!a>AmCi2s8~*(*5PmBMsA$9=8&Z`mB3gqwp6k} zX=LQ*;@D|k|N2dfp*`X2qDOXw_f`sv5|`>D%LK~;E1sO4u91{wm&a%j{PWq5*pguv z+EG+E`D&y&#Mt%5)N7*SGO+HOkC9SfxAVYBVO%(#%X`Sh<7*WqFgA?hqL-Wua~|T> zZM2@%Ut)F~C|!7@C&ou5GBtzOjtL8j3zMN-g8w-ytE479a7z@&SVN1n?7uQ$OeaV=^dr(wdzw2l6k!Zvg*!2!jEM?Dt z?%FG=1+N>gBf|%_@+=oDBgiPs$IkW3>V3H&F-h6@;p?}x#?k=AlD~&5B61BF{?1x2 zCrV%}u#~VOQpH(I={o9nW7Y9p*s|AdhsAnF^2unJi}moCEmE{FxwkWaoAO|)53dZ8erg?Z8i;{b@a`zkGA|v-mfKMOud^D}~XEp|@@q{?#T*U~CvY7+!R} zAZ<&H!Jfs-t!MRPa82lD~z&-S$F zA%zY>{bS4T>#-@0%d6^0m!5@5dN7LW!*U@#TVBr?Vcfb`&(Zy`e*?aKsD3UuwR%Go z-kg0kqVx2&l2BZh2$Kn3mJupst&0m$0%L=%6)!X|INjkb!sr2L{B~$F)^6n1zV(ka zpMQg+w&CCW9So0H2`H8-L^?az@bDV4rDsBv*H#|{6O)&PAO4t!g$wOG$aBt~gRyw+ zs!Omc!EW3OMTK9KPs(+h9p=3dB``L4fhjN)6}DDXcP7HLu9^7!Y(#%HABkypJvHZ? z{G5FJy2&mNz~+kL$nQ>pp;xBDqnabj1sfig30Bf&!sy{@tUP`IKRk99w(ZiRr*q1w zge*KW^&mB=G z)9}d1@%TA!2_d=Aw*u#s>i%u_q3eP4^$#ho%30 z42PoY$0*Ki>-OWDRXSc~ZeXe`*j zQHab{ShuC_rxCEb{HLEc;E^%o@NE=3yL8ydupIv&jKaucdNUMEbBP5l3G4_{0#g?D z8n9xrb)xF85T-Xv-yYtC@9upZd1h+Sxx-vojQ8i>fgNdf-RJzC-1Me&?||a$DDBio_pZl_*IbIP-}nK=bu>oiL|i6bzxrN0F>5M*Id12F!?ZL}VZ%{Tqyw02`xKJ3ArYtWSe;nO~2Ohf#kB_?+$4=3ybPW7L-+#0S z4-FZKXMX(%hqCp~Eke;_IJ7_*%P-w3Has#f$HMDyfl)g#U!1ni6%V;ROr)?*!noUG2VvY(OQ&~WIIbOk4JQ5fPPA{&m!CZeskzv=^$<2b z{T#m9{ufT?@%G!n28JFMb1oD?dM+~W!p2h%n;l*MmB5q_)xubaxSBlo2f5aItgDI1 z*%_mP+hF2FSE1AOSE5_T=4ck;nB^T97QOk@J9zGYk07g%jgSTpxUcY+!gdK`Ys&6a zy1^>{Qu|1e;d(oA2MTT%!#&KotqB)g=W}{PLPTRW zg^1gLP-M7QD_g2dg>mJ6yOxS{@(w}2(3WW5djLAzFcYmJBhaIL7~D)}@7-+O9EI0! zy#?RxunUXmolsD|5w=?xg@y%8y1^Tdm6J1P$!KFY#B+1log&4Z^8S>^NNfBg7)P{rls zoXNdgrm!qYTdurTLApBVaY4|QxTOnDdlx45oK-R5o+=_fvph4xd|Tq7PmZYxk`I$U zZjuSeNU?nQPtCS9Fhj#->qb!(*VaP^-OFr^RVF!W%{a02fq_d}WP-D^|F%1yfD?hB zJMfO&2)Y=Z$oO!Yf{qK%+>D%!FRp%=v0hWy?y8c=hqVG3OWiF!={AmpI+A=l++B2tN%a9qpPW8)H6H+FiPwxbtClH&tMD*px6CZXstv}iUx;|97apDL0^wo!*TplJU zF1oRyV8aHFWW^-LNym!S7$Rp>ocX>%;FNrF%@V^SiP}e)4l}tVKK!G!Ek;DD%u*^j zM@&mp>?(hXJ<@=bUXL!o;K(?_n=YO8qPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D|D{PpK~#8N?41Q* zQ&$(q|4AivZz&au7k77e_YF358ygIpz!z>8W4POJcXy|>NZmDQd{yn9W(SV8xOZ3ICV& z38F2W4>@0?tjPG1$_fcLmE!ZnC>H8s3IoE0rz^U;!YdR3;W4`DGb@B31i}q36kX9K zvJwK}Il^CIo8E)S>UHlM!nvVyLc&bNiWO^CY*?}73)M-mNv!F)1RYBf<`h*CLz@oT46sG!Q!I7y4IKQeBB~EU$ENfukadrTo6^d z$%UI;*HX*u#uw`J7$H1Yu=<3eE8J9sZDGGqi1eTFt{|KT;T#C!Y{wTTRvdMT11t16 zohv#=bX@3skR?q59mh&6D>3|Eswc1_h&DY>IDf>udR#N5Sg4CJ1j0>A;+bB2g(8Tr zAgF?<3PpH~Ze*bd&lBE5cwhP}JVsU;iG=QJ=sAMO3f*a;WM!ezwr-)v>Gkw{dY#ZU z7hX?~3GX3%7VS^}xbPg|J%zuzg`oXK0pT3bai-%<;!74Y2{DPY3t!y%;>t=hR!Xo! z=T|s?gmCOgsPD7#kd+8l!uh|^d=Z43M4PN{L9m76`p#1<)WzrnT^Pa*npM#iTj6hd z5hhU;iXf`G&lg0OitrpN^d9sX!gmnflOFSA#e)@RR;a5@`$+r*(WD!jEHkpy=%$Zi zh4!ZxN!^>C_6x#G;!GADy^pSCCy237goZ%32#?YG(R&MZ;r03mIz29I6SQ4W(6JYe zI~{Amawah*Q6@2_Lg%Cv|Ci1cL9fdWIu`WX=osH(3j&s zxFC;-NhlWTVhG{H3!*ECp{_6rMORqqudrVbRb3&b=TH$IBkN1I2+t8-mzn4apF!V) zzK`%-buZSOL_mPVAT#JD5DFn|lL(L)ka&<#yypMvcEjmCX`A*5VooK!SPMniPeu2A zs0ad0+k%h_MR;6z4i#aW{?g;ZHbL8lf{wLtj_JJFvqI;H#F@^SH(wgCQk@myd}KH; zq%nfdD3!uj%>eyg`u=1=pJ3%VD|GDNvO>Zg$qETJHNe6#q~{y*Hy0|3h5G$MIKhJO zr59nku>^6YZ7RZkDs-djx*4bl&ljv=5?H#K1*=#PZQ56c6}q9Rn@o=j-y<)gTM&?d z2+)lq2qo<&!60EFArEAQgoO49LQ5sRpwr*Ira=mYf-9Lo zzlVPN6;`gXLdW(ME5WRgi-g=FblipGqt906 z=LqjXj| zBrDfhAuBtG6|%C)oh4Y;^c;ho$ihOgP`?`pH? zl(CWwr96q%6PaYuJ+DS1f=~)VO}<$2(~=+wf-ZE?lwer2KZtN>Z*N>EtQ$D0<5_-IICLZFI{U>)i(Bu0lLJ~jpk32}%|PC`OT z3X+)kC(2Th!h}CX!5RW4@Nz1t2}qSiv%ir{?Blh9&WciHa6uPDT@YUqE)sNd3Funr zf=CNOOk$o@AuFGXAmRjFGbGaFho%O)A}jq^A@@gGfwp7Bc?2Ik2LYJrc6fO%pBRB@3A4-ZE~Of(|n;t`XSh&X8?;-yJQmd7F`={4)xhcW?D z7+cWk4XMz6vNQ!j4`77^jp}5z$Mc0mp6Vpl^m@8B$lc+~N)1*zvy#S&Hk1XO6C~IQ zxq|hKbbliEyT}bfg8hgU`d#F^rttwmtn+3B5#F?Q3M#u!R#ORwZC%;|^PECpcS5P}158-i|gX z?N$ORM^8?O<-Y!qx=>HKnDmI47`yLZh>Z{K;mNb3P{~PCGKaG!NyLrgauI}_M42Kf zZm>ep6vE&fJx$)+h=vvEyDC@c`NQmhsA|VzExh+VPDLjg{b7du)k)PMh5*89K z*w}i*&fWvgw$>;(_HcG^h0@*~GG{-?eJUWu!w)Kv@b<;%gVTjwIC=g6t_IwK zESU^I!ypJ)8bN0zmlnDp-Xv^KSs~G;6I_cGy52O`kg2TbtYE(mQYp1B8tA)IPhlG? zG;!t`D{oj41{sUISQiVmX`us5g>HbX+!%r=3&Jc|$huZC2{9ECWg6h2=hFKT*@`mf zrVvYDVd)MVJ8w8Tm4us}J^bx0;P2!JOFK`bx|D&eYz-uN(zsDE`Nj6mIb1uo9_It@ zAtm7szt{ATz;Oc3EOMoSm8^xFgijaEH6@Wq3QVANq2EH5wT^z#cc$LJW>)U8LXC|O z_fGB{5^M6=7fFhR+PDa!oS85SqD+4UQ6?){*Nveo%H%7o!wSs;qt^@OY?i`IR|4oCrnK`?b)KNEu1^Vu(sT8rJX>jNVkT3pUR_?MwgNzh0L%jpCtjT9W zPu7ZF5usS94To^>1W^`*nJi^Nm~|~?L6iky))i$c6sKC56%uTEFM9thMVZc>nVAi& zZOg*Rximc8T+qo7=%m;sk}Q3uF#nPZ`?qKyh_>KDA?UiwDNem0q3|Vof1@B-RwZCx|sYxk!n1u~6p^ zI$*livS2Z%AN11|X2Fe-UYKcAo!l7oe0o13TWeWtZUtNW%J5(UT)KoEn%mpM+@Taw zeCi>&Y9l5fS-$+eLX;Q})$MbTzq*Hnn74?MB_WoHZ~{AfsS3K$l}Kep!4)~HE7Wn; zY+EVBTPw(NOJxGa{wk=;&Hl=z>@O9nOWDOhz3XC34T&`gH3>EkZIBpmW#u+2f>=}d z9!0m1gS$wHb+J(A4mvHFBU44e5bqWw*5tmS2~IkY^?ZO8nto2B zw=@%!#Jb2$j42lC>=6zK39}&1nF+J5Ag8yMDJ+hrR@3M?)#*LxeKQqhf+mPi_><7( zG`B1TcZVuyR=ygVR4za@{Uznrd0gJV1<&5zL{joyc5pS8 zRh|%=mw>Y?b#Q>_$UDq(rhtONg-GlITZf8pawq{WYY8ei*`ktLNtoL@v1>-mHSX&B zSqvq+j#PtMu2USl#v<81E+B-WWsjv?iy8|E}CLaa5NP#Sn7 zv8Klhy;v6ubw=nQsIPFfnCiMR$Wl%(%v1zHrb1?IC01w_7d!Y9 zRD`E<1+=SJ0ewdeMvb9?4>Y$iNCfEvlph#v5(`c2*=$hRE3e*-RDIfcNe zhmfVv{a2);uX09Y2YaWQ@U}032EM-VuUHfCtPYD(mALCAH>AeI!aVRIq5`gB)4lt6 z6?&ymHb!}cG)*eY5WYuo;xsXaVy#K6i`2xJVxiVTKVGn$NtlIb3=&%sXkAezixy&;rO#IUlfj>_)-=v=cV zBsF`%+OxFgiZKKyRMe6P@o z_4_W=^n(R~7A$AMzpQI1(_a#0@-0)LcsBAeQ*xqgg_({ixiPZJBxYg7EN9nB=vLkb zoqBdcn@`(j5NBOt*(J5=07VW<53QI|r26zV6P1%OY8&+`<3Ey-XfXEJ``A6Oj#EMi%j;zC> zE9dYu{1K#ybWgn>SlQJ;uNoat<>MK!RtYwMX-SC=!{fbku>I~8)^nl7R*Ehp)|pI< zp}|Co2%!s0cVY~M?-k03koQff=?4ndv#x7HaAlCSOi9rxTNst*tWd~YwpO!)Q)M{2 z`my6@g_!7IBquyzACzYF8Dg`NaCNVV=H4FY-MT5-eAydL?)1^Q=jM$>d_44Xym-C? zEo%-!jq$7AiRs*um{5q0{)vBY-o@LHGwfEONUHY)u>=*%cEd;QKZI3{PW;Y-gH$2$ z$vJG?x*X4f4>CJ5%>bS85m`fGQ%8r4Cw(f#3D(Ul!%{t0;`DFyhp`xyLnejRKb8(>ne5r}Rz zgnzW?VH^Am;?t|J@$p-{3OWOol6s==5h4dPs@VnIT8)InzX`vyaKN+>_t#IywrhJJ zmyxHySg@X`qzs%~%A<^{Gn$vMg^O)Th^&|mWyLzV*3J;wxql9+?xc1M?PacvWHA@73s6@(FQ5^epYU>BE(vgYeY9y zYN7D|yvssOKZXhkv=Fr+gwB!mOyW$|Gl?^m#;j15IeLBuadvjCj>bOa(Vht-y@d->h`4LJ zv0~5Ph=@4GY6j9S){C%rstZ4dl4$1V4R627Q2I56l~=i3#<66Cd>2wC*s8ln5&Gyn z{=NHv^>$COYfO0GLz$p%iYO;Dq{ z4?5a;q4DGys9H%3`@DHKa^&d7+ZZ)t79xU=unW99KI-`?5^B?;8$|=x<7=?z+Iidy zxC*(H?v3{mGc;&25Pixvg;~>K{Jz3Va>z>@-~J=cK0cAZa6XYW++3TXUp0St*Bb!y z@^xWuL?;6^E3$+zT;B8>4&B?$dcJ{~Ekr=#ypt75%0gWS%Dz`G zCieyjw6Iz-t?@=#=Nhp>e(DU?bA@spFv!0mTJ`CJMjy7s%eSn#UAz@bc5lb)2oh-O z8S$`-&efwCN_x5C<;y^nck{sy%h#a|v(JrrI1`e9?kz{)%Ka@cv-HK(Hr)|Da4!F7 z;l<|uC4^pHk8QUfASsR_3*KK!_;kU9x=f@xP2u+xeq>5H!nRMrs_R#f97l#|E)dzn z%dHvux99}-HbWs18@QX91EjQZp-=G7^67XPb_CKCaz|?*u_kv1Xt)x$ukYce@BZ(jDHu8TL)elXZ$#*{&F%L+ zemK0GT|QFOtu+YEKm8qYYigScC$0wAXR?MB7`Q$ z&`c^Jp_3ri1>y!N>OxIlmPc4|U zM=254oy%ePYt~$By3H=e_^bT0*@uZ;QMyD^^lH@!aj|D`E=Y+-tUE<3p!Bcb8?!d9 zKxq?Y4c)omC_edhCQ=e^!@;FFCU)wL$o8M|j}{&*<02s1`#-F^7lg=A@^Kc3QBtU= zUS$9VeKa4IPIRdjJt(ta1QY9}SFYzE)?$<^-4i3)^@C;IUi`j-hpxcjlk2f`&q5?8 zJiK_nnvEYYkXxg=Wwi8wiNf@)_n>YRtuivt3N@RtxEk6Q=F619A z9N67FiE9`C#i=`&A(y`3_rBjyE|DL*je|wU3H+WS3KQ$F9be&(t2eV0YbUps`0B&S zu&PXfRE3X-=XbH_kI#^lcr&vcA%a+w-&$9!1viMvD2hT&Us7;$2m^(} z(scq95*v8zorldY9^=7n>LDrN>d_v*kNq4yCU(@kpD9RTZTqKf$6?2fHC(-nUoUi> z^$)jQ7;roA8`j;tiI)Kshnr@G*n5pvi7uEh{a=XqJk~`Tg&d)~CSlpt+nI`WgNg&u zXVO}j72-}*yetumr}sm2?CJD5LP)G>fblXbWLeWn=R#gKp{xvMC5=dzEZH10yMKj^f3CxnoeR*kvsR>26~M;H|6=rn8Q6Sv zmqw&%kgi+J5g7K}A4u#ol@n?E>ql@PS&ySwq|?x~cC&XwqzN-~c=ld^(e-P< z#(~zrP0OQ@w@5DJ=ilDa7GKZ)7bV?0aZ8QqKqsj;E0p)JJ(P82w)Nbqs>vZE7K~JOc z@3%i<#oYjezoq0_bUeiHaBYi0L%xBsmUaSyBq|t>cTdNG^T)W@X_43+V_OcwtewAb zqA76VBKuP8o0VhVOrJyg_{<}0TDS@aoLV*Zhwxp6HHU3&FJd=%@Ui7hd$ z_YA~zE4VJTNU4C+_NiEU?IEJWPw;z-Hez#kb_4st&BhaDN|Z(k&r+~;D-F9c6e#>v4KR{rEiCUPta5Jj{z6d@YWh-wNYji$isqnL zXlmU^pedzje^xU1p?lQnjs8QwLXQ!RVMnjjfqwSECI4Z|p>5cI?LMT5v}%vqitbdY z9m;?H3lvt`AZQXG`G)cM^WquK*Q#P4*Rf_l{B>|SYYftSEdH6c1F!F2Koe%6H)-m~ z4N#_s2AXf3e~YtAm*a}!Efkg_n6qEm$*n$y{QepLtelCAVODj+A$aE|?JM90ATZ*;!ZsFz)3KgSJNikgA+hRnU&PeW* zF>=CX+jPu3dkV?%YS%*fGJUXN`TtO(MQN_COUN53M)dyzH=e)3>=B=0=)b-y+sdn{+e9t`jgDwuW#egIQ@tke0DG@^hfil ze{tKUgk~8kw||G#*Y4^avQ_BdXUf%bFO4?Mx{`n)EQUfJhi=tcqg(yzOu%2^R8RzN28SU! z@+QnJol&PoL-g}0jSqgG#NIde!PiQ5`o?sgh4p9Vadl6hE*SXzUuk*+#)fi5tJY7# zpI4a`l1MA{7bPg?b2a#Qlt-VkesEyD6NwLX((+H3SdPULnz0*R-CHA?)R>6gpa00$ z-c4k2k=XaoH@I|n8>^}HOJZ9U-}n9$)=fuo^}^08)>B?`@K@F=3elZ%PGU`wAFEj* zi;a@|P;L;4u{Np;#BA2AS^Oq2d-iO8tH2RNng$KYA59^5WJS{*RG$?ZEF@=!Mx;x5 zb;W{lGcaTABzXJTW-8Keo<(E+ND9=yjfjxr>_e1X{PDO(t>8ZF7o@mRJCGLJ+o!Sj z(m}juzm;B>pvZK)jPr5!EnA~%0V7-<1)fxwL)FJQcskr){w8go0v&# zP{G<34@2V-AFIw{r$`OQ%UCPatksekM(O;Jg%HOI_2Ci~jr*_fuq!2%TZx`QE?Bar zNR+9&Ko&N+KFCKx?hm6`)g~LY%mwL1nx=k}65(o%Uwr+J6r*j*0ZL^T&}8bBWvM=FtHYx(!0o;9t0ij`UcE2jSt3e{k<1bzha- zpwh>+8{)ISCTA)4R}AUT&)S!5UbR2QltGiwbogpmwjRsG!|**2 zD|YLa_bboW)Y#h;-kGC@#G+a|jHy+NJJtdLK}Ya>_s_=l5g3r#LuR9OrT#E8x8Rn_ z<00Llpm0jyCPabINu~Yyy_K`1FbcKc?w~ZJf+bC^j?S#KXGLRrN~~Nku<1DLy|Nmm zN~a6w&|&}5qxkasx%lteZYX3l1|Wj3cXy2Za6F<~Q#4;jXkaY)=p6Q%+uW)mG{#Ds))i0=Bw>ds2Zw>#f>`8lwQNo@%# zQr{poid<*L$L_{aY`Am|QDKyLtng!LT?uWg4#CvXi_rAbWw5N#$#{1bgWkJB?Pl!! zYkNy}_GEj!A4ocViw|m5=L{SURT4Jcxr~%MY5n$v6}mk;d_E7QJiEf&BF#-t8byOi zq)jwtMxnxFDH%A<#PBBY`zx?SrSc3VkUk0?wB}g zI7(O5ziaHke#L+<#>2&}1y{d+?=gItwr`%` z?1e3O5kQl-3O8mJeyCG%0A>#U9v#Q7gjHFsm1k@${p-TgN;}}}=t%eU`+-Vih6*hw zp$?>Ci^V<#aC=ek@o|>*Eh5vA^YEX~9PXs0EpfNOHxJkC9p;GA!u{ z@4QgccOfCBJ}9Xn9RNBIU0I-I6K1)UiZb1T?c3CUkXuy+s|)b|5(@ABwXy%oGm(`~HgQQ%Po?kG9~ zs*V*KqDTD!5Q)vwyFe(KfM!9{38oWkG^@I>2sQmML8Qs&Ln2M#cfuqVPL3%m#o4(D zcK`J^MtUo>M~nPiWQA$7Vq9sd)TAJJNI z%p@1t;KQob@WH~*bM#AR1~nuf&HWDkzLatVxcK4$cF$hV#c>-75^6J@l#x+M#uaLZ z1B>zRjq6a#X#l(sBC>{$Uk^-e^C5X?O)Ib;%8tpiz$J1$>_ESX~30bkEH82G~vu+GB0q7M}+S!2eeiQK>nWxCsT z3v1iv(&?KRvfHl*V4<{{PLhX~@1wvv( zzG|8!Z8WR8poN-#4D}_b6(Z*y{a7KwV<0OUGtt?p5sn?+h6**wb)7jR(*Lc$fu$Q) z;_|J1oKKqklS3Q!g!dPVGmY#>kIjjVc<}NJw=FjF!k689qfWj2MFh}~>^!^2LLe27oMTv5rtHMC?EqiATGBLpYbJv~WQKcBM*o$J7s}qt3A9uq<78 zX2JQ>RC9*doZZ8=@1I3YnAzB&a@~>e_R@|E-3~mD9;j<~LVOyt% zVeb$F;8vj_mpDr-wt}h%#1I<`%LM4gZ`?@6@&!NP?9D@LM?7~ijB3yx$|1QMv@kQSPyZBC>uC9e3o*BA_$(ka_y{7+s2 zE54eHL$?p9*E1B^qgU-tFdsUHe}?{q)5-xnrAm0fjlP`$Y1qsai+=hM4NEygYM?>C z!)q=wlU;j&?2I5-ii6AX{AJ71)N)b06%wTRa>U9swqN&j7^S`DNWNN)P#qf!pr}P&f1Z-3ZPrRG3;JWGu}X2N4{oj{0gN7xruU5vq}_j zqCPASql0nxaz2*D&jRWlRIFfuik0#xPW zkT~V<(0rE$l?LIPRg2-8&D;PQFWa@|J{Ip@#4Jm-n0lA!f=*+9g4{OmlM0+$_JPEf z#xsG)h-?IC9q$rqz52Nz->V_|R42NaM$^4$8gCj3Y8}2_u zL|DNpH7zo5<8%GcQ?KFq!#`MAY!FVP9#bpITF`p>VEJoY#r2@1Svz^z89|`y;n5y#?be7Y&R61?#EzdIgvF4?fj_+qdvAT-rT{-WjnZSQ$qs%;8rxI_n3yBAAN~MXZ}P)$Pp&c>H$wm zGSso=2N*SaCgQt};r1C4+u&z7`;um87A$U_9Wc6iN7xn-e{?bWVP)@yhB~>wBg4+& z#oet%$EsGCVL+`~%&JZka!Fc7`H9G?CaZ}a%(qo-DxnsvX1UE46fIJFr7J10te4Wy<##lMG6Ac@v+ zFcIW~4t$Y}--gUX_dcKE_me9S6>*Fyw|Z$HbIWpQ=syTw51WMw6ILR=wA$BaXaaVx zL~=sGE|zO)QyJsx)`ex$%(EO8Q*nhh)mN{V3L> zZIRxpruakZ+6z`Sxd-xPRhvSn>3q{Z*h!^D6@VIkonSlIus%z&%X81wH^pHSDL@Kd*%%AAd^%Kn6 zy96O22e}KFp!FPUmFthmAAgVfQ&uCcLDnnGtDJL zrVN;j=HIVDY*QM0H7?0}S0k2RV4S+OJh`~Sq(ig_2IJGXIV?R`&i)i=Xf=kavW zm|9NX->X)4xVSY-Z&i~aO552m>LPcvNiY`aN@xkqeXwjCjx(r zT!fFOev4~QwnC9gtI;z=Ht;Oj6<>E8g^p8KBDU=?qclpHvG?*RcJef9#hDx%hq@S8 zqdv^u!)$Z!8+C?!4Y3(XgPPZ`!f?y{B^2jOzesalkW_1fhQ1}z=KvwiqfkBF2{3uK zs*Qcu)B6iC)8wzA0mQnjXhIVzQhw-8AETsC_HoZwPKRQ}!3`QWK%?>OJ z=aG8g2X@aFd}wHP&}_?Q73#oJQVA9onjtq5Wt!1O?hoBiy*yz|p%$!Z8ub(g5XqXR zp?l7nmROa*$jar=Ya;a$vW6_#spF<2Dd8$tcW`QfW+T7nwhf8Hm4mpy@f$3-a0T~o zZDE#e1WK3fgE`%L;>W!|=P4g`c07C}#q{yh@Ym&iNKJXh?W0DaTfIT}?4!vrA2A;( z?&K*lEywq*XE%p|r)1^=XZMz9UcC#qLP#<1J|sfT{I!u@N_jXgT#Chm#|r%YG5{|& zk4JL!TmI3)iFuQu@GV&dw4kvLWJwD%6{%~O=Rrkd2{lO?eIYUfg)d7p5NXXr%@=K0 zr7Z@{{VqoV+%S6&E(aV@S40jN*04#A$^UbMd2#~GHhhm2J67VzvlnSqZ~XgKiCq-i*O_W&~(!dbV;9d)V>#5X;Mq4bzlNUTvXp=job(Flmp zu9|IfM7HQ%sT9oW_u+RG^FC0mX?JeYmO$w1gLrgzD<0q5isffFVe!H-NI12k&}Ofu z#gPY8@MBtLt1H%|Y&23z?vH$B)ib703j>JcOebraCbv*11!qk=xzxcIy#~O$Oq!WJ z83>P1VE)E`xmK1aP1?`X7KUpfjPZ~Vd3soX!~KE z+#gjvIK1KnHa`ziCy`=^MCNGgZH1_|Be;4&5wmk8Ql*din(1*X(G=zD8g>eNF@+yV znOg90p)~ZFph`wW_!0biU_D-K9tTw+U2oB`-e5SnHh{S{IT3A8>?{d2xj#tkNEZu* zP}7&twWew0lLWdaE1Zu;#JaTotJg=rsoklXB{bn<<+$Gw6ncuQQ;R#OWjllTYpueA zAYC&7zinNL`_Ff9D~(hB>V9=Tz@NYUijgb6fHQr2Bas*!h!keY3n&%AxF?fwE6fxm zryroU>j3v~%;do?C(*Pnl(>Z*7J7ePSKji50xOX)IE6<+M_)8m> zZG`xC!@2FeLPF?zXfbxLoQ^-wZ{d;|(P8i`(HXxCo`i*am!U-%tM z^+ZUcBlx|AhbDE}!IBw#I#xB&h7}5?5MruxJyX%pLQNk|-;i1j`ci^5O*3`4Z*FPn zhVhLX!oNoLna^lht+msCMP#IUSu7ie`e@!he_diq*O|O-Eav^U1J9Th%`Gs%E{x9A zhvKhQ%katXAHyY0K5*mF?c1K{=I9K27i|n_;LDq^d!%0ZydVhQzX5XDbG~MJDtVWK zMg4w!t(f->MTNGow#|9$iQI*!?(e{V|4c?|s&N-`&Mjv3`=XRbHC;b7=^lw4MSlqC zPjfw}XlS9P_akeX!tn%anv_WMrK+%#F?ilszLqsd|G9xJ5AHDKmqEqevS+1gNT}VK ze>C^l-8_!#+ke2?^T(JKt+rZi?Hgj+;MrLD-%_+_@4+n+Ybxlo#{N4OLmPHx&70a! zFPFZ+u4m66RuoM9^10guH^GJ0r|ez6baA9d7LY?s#>}83c6j31tsi5qLX?FHacrCR zrLxBgZVstoF!!klXKVeIRH7LFa}N*T=K62B-(Fat6?}SEtqNMmSO?NY%4$tkawmPQt`4o|D^5^2oEnd}kViJQOlLfQA+OZ6=*d7u~ci2l@;AHEFDka=dyK;4yH|P%wi!_2!=sC3H zbDVgvp08;Un|nYh594cEXrjy)ZHB?O&oqNuoq0tXABK6q4n=hMDacYa)0rvf#Zp!- zv+|G?I*Ey_=rt&1$lsQkHBAmj&1W>JRtNol$$1jPwpm+njT32F0%%aZ53yy>3UnG$up&+PdI4X}`4#pK>UHF#iFa|~?nQ3fl!WYF zUtl6lH1u$Sq*$agAiuT~^XCo6gaf=j_G6ootxNlDib8hr$hUvI;% z>&r1?;}rZfy*swd`vkEUw?L*W*i^mxQo9u8GRkN$dX#JcM<-gAFfB9)zw*pZL|rO8 zS@$gMJe~?DzYD&4Ggqpq)Ngg%dH0zLj)8uTT#3Y;)x_UImkh+cF z=#qU4PfK5IJaz=hynCXYUn|Ia=4}R|`>r4H$BoAbeRGi2RIqn$il6(8!s7pypn|`x zUj2^XaA5z63rJQct}_blJJ?}dhhAI~BH}{8bJ*TF$8DR6E7wl)Tc*d%(g$@aH`Ko$ z_#Q!SF#qyp;p*9*OKVH<_O#quj;P|07<&oVUi^z6H;l);X}xfE<#;6BImEe83M)?D z{_J|lm{VTNMDKsSyTQhWvLdI2!bz9xSdN%Nkdl|6!`rlOGxnW5U~ZBCR&1f8Ndduh#JO)exb0y4xqA&HHrdajTsvt4zWV!jE?K>W zRYiQ)V<@6JkLMrNAJ-!*@Yj{|2ztItU3cq%Uq2m+{@?Y=RYb?RYrwpzqcEh)2=pA& z1eST81e!f}@5|Az=U}{kyPws>7*u~a+`cefVwzQ-H-s#uU{)A+W9Mevtbbu`mjAHJ zcah}K*O14(Vpg#fA|~7-xeTc)5n`F*%4I1qdwT~7Nx^t5ljCi89HOIwAWeSE%*~8h z#3;no%e6YXRjv&8mP29VUfN*RnjuJh^8^e27>oGm^ZXt)K4p4h(Kq7}t#HH_-wt8= zKt1lXxVyB&nCa_aYh~EQGxCCzovQ^i`XVyoIAk)-c_4Fd2`kiPrtm77Zb>0+Il4a# zagIpc(_7P&(}VtUB3-IXd-SN+I!BQX2~%RtarVRb6-DY+sEwHRqq*(eVDtC}w%@y`DE)}UUs8+=sBWl;=Ts?H&4nBJV zNff!UOu(%RtV>;RlUy2l`$AGI(ph3%!Uy&h8pFlE6>O`vhgp@Du#J8Ji-7$&e|-270Z}(x@@dcPSe<*e?7>T&^{~$$GI9ww%py*^r z!4YW@DMj#ZSQph>RKj+s5YZ@ty1}70iuF4O9Jk%?p)PR~S*S>mxqo86vk$!{*<=z}!RI z@yF>+c>Hi1Vk1vOA$!d~Q(qKFio1r(&o<-7&6BZkT3=jU`wbEU?{J@0P*^ZSsJxSA z%7q3iCe$K6Ho41}12L@eAdRa~m)n8+@pS$7g(4E9X|pa6n>nSAsiw8Z1V1(Dre3CF ze(w)D9D+4XA1+waw9S3Ha{isrsYw@RnP&e{&jJ)!ed;6^&`w{gk-tBp+kdKlFqfPP z2vA3NFxqq+ji%iyBCokRJXqsiw?$W2u;uO98)s$m8A&K>GLV?HZD^Wh} z>}cGk1U{2A_O>E?rUY*de*@tj1 z*^z&RVW5Gz`$0SK%g)(YFnbU#ubF`4Cl~aKAu|w84kckONt-p&LY&4bA||zX{BqJ)7Us z3;J|w4X9d6|_M6I*nl6 zcLr4HPL93=o!W<(geTl~CC930+oLWd=^EbL;9$poP>2|ZZ(K)Y=q22Vx5vf*PT=Y}oVee6FxO9()$Z9IJCj_5u06F9l&EJ!|8eQ;^V zNxTTV%d7(hLL$vjseOO`QNxqW8iK2jcJeh7BeKSjHiKbPI^#(G`%IE74zIV*#;U!m z@F4II6VEqnui8HJ^zDjvZ7ZOZuQlx1bty}=g2bjgoLnly$+a?^U8}&^jTQFS$+detX^q{J7HMBbsir&krY$?l9eb{C}|0;$ePuO;K)zZ2dOAI#A#|I zDRBcv38!apyF}6)F%s-I5MMxjNV)P13*}8-q=y369i!WQXMP%o3{4;V-(Bg5} zdsF?nwMu`0z2{fMHJ<~)p%K6bZAReovn~9dED+hi+O86u9lg=e!wmxle~2!hG(lQ7 zRrdV#^}iUmat0S8N`d~9#%)AO_3W1^%PVJRkHGDhTlt!4v2$vKZ>Md7wJi;86hm$f z`MK5Dc>Xk=ygbTo4)waAy3o3r?b^41jkOYPNh)|M9pLO-1`-!nnA_OF(#{@Mj*hT& zw1>5;3+z3dkPsP<#NZGlg+*}rG34>_kR`+-AwCh8gI?iocr+psBaoEv5{gu66tX~M z4|k8I7*Me+yqb@IrIF&^6M_SJeU?@>eJ zA7k2ofl|G`D=?`hU-{2pw?ka~dEMv_5@`A{v{DF-;!}%6zFMsqrz5XIO;02nl}vbw zm!>tVDDem-5#}VRa`gdNJbDD$jHVRSS@Z0L0w4DoiQ7+TRdF%8HXev_BbMgb?LoNj zorhU_mV(wCbaZZs0Hi=$ze9)jqGq+cS?2<@3i;XHPcZQFL21%}(kj_is@8&ojR=0M z1LG&Lfv0Chv>ZDKWh%R}W6D1<7X-ahpjGpM2n{>L)mv8{imKoK!EGB7acVOD`ldJH zV@(^gEl{&ee+-(u4(3^2p6?F{FK^-D>1Eh^|2|UUGl>Ugy^oUKZ85w`DR`Bs2Crr< z;Z>uw!2~Gm$y>ZWdj|LJ-NvDZFA)@VH|q&dVhOiKV2^TTP>~6?MHy|kqL?*Up?{Q`6UoyXTQa%S-cOy9B$DG7J@J!-7&>R{6N<#6($iP8nbj^D=O;?uQ| zN;P8&v_X*q)DqDO($qSVWu2vmDX%_idL1b#ja-sroML6#vBIe#t=!VJv^`pVn!QL< z8*$>d^>`4ZzA$Z_>Z2Yzp!pGLBB^XsI5?|MOaikKZf&C2^?zd0mPHu8Yz)d)$(u+MUM0=YvAlK_^sB*7Ac-;jD(JMLO+vgn z6w=hF*rn2@lK*=z(sFihYx6v8y}UD1k+!m{iH{l$#QZ7K(R1EB)c;~AybUPQ zghxd$)EPMlA1|DPfB)Nrf7cwrpvDv6>e3D(vvl22rIgFv{@2BI_+|BETwOC3sm~0T z^^qb!tXX5AOHS*lwix#j#y9T@GYg7k(2|^R16%%@gJiv8D~v;nmZTXHT|=0@4aFdm zpPE8k$mq>hi}NDX^l2o>u<}2vz+svsOeV* z(H(LhH=UluYE4kuP2HWQg<>DyJO^1m^BmDJc6I=!t^ObTt4+muH#iiH+IpdC1v4%} zM3RqzO966|bnM=Z%a2XO$6SZ}*gxF1A$fM^JU1P=Ah5Qp3j5kU_}cr4G$|6d*N(-{ z`_>^Sa5rnc(j~-aRaTHT#TQ6@Vwl8UHWq1x+FX=#i{A66X&6$EH7wj*1TD`( z4&nLcU$S=s8xiNa9bsczU3Zou(UcYXqC%J+J(y*4lvkl9Iii+;K8SqOWIA&nVrFKE z2IXzhdFl|VX3Nn(4&ml2viw<-VqFRCE9O1YBRw5Tm1Mt;E-Dp{heqO6V7i+i50rFg zT?eNBi{{MLi0Jj88`w2#L)OlvJkoMF>)N`ieSnlf@Zc@Ok6_;jFSvZZ-cDs8DV4>F z9`87Gt;F9wjFt1pVDq(&kS1oF5o%>u1D`bi6sxzbL#;s_v%Z6kjD@8bjXoWOh1*tR z?cAmKs?8X9cywYHrN%B}sJMlh_uXEPpVv*p?Z3Z-?6rC@Kd;zZrX8>})8hb8)YOol zenso*9ofCFqa&;bu=&y+Bt;fXbdx!g*$QsuSp%Z^5hTh&-VY&6FIy8YFG5YPp`$<_ zAhfjGNl zGq;^zTv%tN!+>fS-TDJ|u{v=6>IDb>!{a+S1WD%-Cw3q7s6g(2^(lH8`2e=ThP%*g z?p;J!f?>aNZfIS`mkVEd-=MI&xcxt3{y#tCRq#G8+%7$y9-T4w^BEYmv9J_-E5UnAv{@{5-p-vtCt5h`xvomo{L*s&5dyZ5m|Jk^E!1!_h{a`d7eA z&)J3!uGSd&+1DuNm)1ooO}c|!fBuM+yeOUlXj`Wt>y@YRztD*lgqm8VY~3HZ{o2$d zkn)oX3vLezN8sK^#Qv2nTOLhEFcHgvUKg27j-OV zV(c|sxOa(5pK1u~Szlt@^2uoCPZxt2&x1~2cpG>`rG+)d7Lv?KSe z7Bf9u9|Gl@R>FiX-C^rUfoNLpy*`4_{gi6M^td!101HWJ-C2sHrBzrVp%(nqS$fj* zD%AAGG{QiRV9GzreY(WP16@5FP^&F{e%8>8y&W6=LP~;q;KaK`L&SB=IsIvFP@0*e zjgN5gC~!0=5+^s(vRj6sB>UxGfA=j)dUaujL>$)MzJn7RZ}5*AhBm`n!q1J?1SV8C z{nT(4bwYFyWGPvU>Khq*C-$5CGp4M3myt#v66&n@fm%fLyfRJL!LmBcB$@Xecj`0?O!to!{Fq@Mqqe;{X^oXG~% zVwKB|Mg30);Pd(|n8mNn8$v6?{C##G5+hA-pjcQt!@$;w;v#T8U@_uDNN=j&vV?)+^wA?LbiM8`!@z z%;K|`Bjed`D46^nK8~)ia7ue4zRM&=yvCjFUt{f+Q)xmwHHd7`q3TEYWy1V&2S^HfWB+%4#AU!2_GKqJZCkW)` zs-QQZ!$pcpZ%)m=Cj5a+`r5vpXg{$RzbA7jB=7k0KM@y8vEJ+(I@X1GJqo`zJc+D( zU8$tHi%kjEw>%3*z+>&o-}!|y@qM`Hb4+Mh3liJPIR5lH4*a?m3IkpR^*VKcm94sm zk`Q|z=GP4nY8W1bz&M%=RWLO6bb;A>;-5|oyoaj?XJOmLQ{05E^iW=xzI8sr%)ghw zCi`KXLXVxZHCoP?gx?n}!Ibu+;O5bR>nR9O=;7uo+p*%G*^nMy$R5l3Mse~f9l>O6+HR-QlQIoFH!7`Xo3;t;u zK%|Hdu2W(u@kN)i{;;>p{w?3WmSWTWC)`LZQPraqk}J_7<;I~$^Hv)F*~>Tgaca>9 zZre~;F=O}B)j#3W`VC=c?~VHpjv3Q2lWPH!iwMWm5-*d;ZfQdz2?ru zl3$l%XycKvanMN-pi0K;puPCv@IN@SW;A3kGItkR_>|?h)YwpPbsq4m;f?Qyj!qL_ zO*4L19p8tP$c#&S8I#JbnL#SDgBgXVs3A=yaiS0!L8vno(A)?$J(v36g1Jr>wZ?Pg z;ZYNfKmC}mWzF%0Tks}K9co6_M7tWs9aKtaH5g@m)zhyj&-LaTR}uWyu#3k!JDXwh z`nmY3K{Z&a*kLlDwP?#uUoCfP7u7tDiO=`qx8^ZH+;riBz}}%8l-~9E+Ph0iY9cP| z{Rt-i{`>o?b&+gC|k!HqyJcd-zLmKlgfkA_^;_++;?p&7H^z` zq=R!=X+zwkBFVD8`!iKJY5KQ+zj z79u~${gG+cAvZ!zZy@-mX&{jnuH`T{w_w(^FZ}Ce?{}nW*xSyY;$U+( ze^b6AW23Uq>0m_Mo!wyWPbqodT_|VA)wSQ^=Yj* zEh5w?(;HJ4FNBS4ZtSF@jg7rI+RXkIKTn*39<}>%i%bioraZx}Ydf)K%P&YdxrCEs zUHe(YWJjgWJeOyjoXjxkgQ1YvYA0DqlkVZ*jzxy-0Ov@>5`OIWq|ss0P!d5Q@*`6x zSYCvh)SrY}Bg^GIs-opkTDLK4QrVF|@%JXgMX5vN$X8jlnQ5jF%dH(SuudH=UYmFv za31@9SPZ2B2mOo*%?>j&Q(GZ2=^H3g@;6Z>D4}2m62)eoDChicXUdDewhOB+9ONP$ zGQ-ciJ7)d#Gh96BzI^}iZ&L$bEn9`@-ABU3tu4Qop-RTffL)k#cqd-`I|lNztN91m zbz^P~b5Z{NL-huCMmK+QyJ=fWT8SYxcvmk}aA1fkAg$>=N8 z^gLbvG$|g}ESOtZBxPC5^0iHrec(@!7uU|q^JRd`?WT(P5?{T(fDT#=YWoGFaA1gawh}|mjwRaC` zax|9hUZ-BOFcaK8I^ySVC&RmH#`XT*C&=B?ary+zo4E*0D}I&B?z~T}#*QD?0NpN&gFLy_f8+OyazfG10#I<1uW~BP)`l$)>Qmd@3|k^}sAAf*gXF93*R-|9 zSN-PADeRrQ7z$IJp*(QsQYd~MIt~jjU+2OL2|LHiP&lQ{5SAOPqJxo?6vNj{i@m)& zl;yPvt==W5bGC2U6huZIQ}0NJjeULmIN}qOZ&i!m^L|5eQLRN8%v|#)#&?vh;+H<}2L+9j zOPPdP@KdKjAE8d~_MqlZ6U|=Vw+xze)~4jl3=+c~M^7_LR&BwOpU9=#2yWYyB(@)p z>VA~8ilK_f-}fKl#5P*By>LNO-8cU5AEr;9%DT}fkQk$0qKw={AJ(XV__}#teo2%n zWtN|5!;%C_+So$kO3P}#dxW1{k1H<@^0l;(aWJuM57hdg@J)tQ_5WcK=8T*Q zuadNaiy%o_&oR0E%&kaz3`6(tCd1QRJ2RHnj9hnpI}bk@%UlOdK|m9M-w6Az`AQA8n%aI@H{9ad7l}fNJ>Gf zv|wC5)m&`#?!px%q~Rf0ePoNK*P{!CgmtUl30-GQ;`bDj9&$-E9^M6WCQnDxYW>qC zdSZ_=p%&Ycju{3AHye!a(k0F3ghic1z|Q%6&3MrDU#SEWYW7de^oWP_H2r{Lnp;Jm z`-YxFLQU#GM~jAQIkk6juMXdOnkc<2xw?KIq9gcJj$%u+Zc5WS3x??C1JSI4x|>D* z^1V-VZY4fr3%&z_A1r}yA>z#ev&Cv#SG&02#nsc{2X4_%P&srKQa zq1^kL9FaXLxa2U9QA9bv?FYofoaSq3Deu!2qksN|3sWm5dn&cAhABVJ!|=vESg%{1 zjEpcdw}ef)2{8Gk)%X#p?9-aBF_bcFzIKuuXD~LE{m6AG(w+E12s!=f2G5gFlNqk- zpC-pQ=R+y$<%d=yvL6tn>D0$=-2e^X5w^Bow}X8rd!0t0qKDG%owG+M}XVvA1b+~+Gq^!tJHv*(xBL23)#)JSan zU}2TkQhFDVUfqeKPpF~K802N=)ELtzd<9o;!}wR<2RzESV&uX__^w53xO-9DvxrN$ zU!tC2CrQy*-h|J_q?v>hAA14UHvY`ljEALXMP|)Ar}tCSpdDQ(B-9xO6mt@4dM90> zCX+~W)phVJ2`6Vxv$N#k{bXExtDUY~$EPY(+R&5&A+~W(xLONIUz9ld>?sb+HSJ2= zH2G)q{8Q-JVi+d>I}c9+c4`_kE4y0gQ?EaU^%;$n!3&XKz(qtOBMrZ#mp<6qc|v7p zz?ndW0Fg?8%_sLjE`7o8(Ly8pUw0mW%I#@U{$g^2xx^fuf0=`+Et{cgtpRYhuz|fx z4*u0VP@`KzG%u@TRkOzS@Z-x!c>A2M8wYXe>M*x(Pw%JJ&G|t>okk>c5^8#!UPMAo zW;yv$xHm8}w??^gb#sjNxUuOVBBRt9!)VHQqZS3{_RytldF`s+v^vGPD~I`-agoV^ zvj>ClLGv$Imv}sGz1RkYOr3U-d}h_l^~2W##=`6Kl}M|$@nE*~d# ze(-mNaZovV!P1gh)Z91FK)R}HQBz&>1bu~?{L>`VLULqgN&BNppDuhYYpz~B z54lX;LOD6sg|wdOVyCksCYVpN%ArDZf^oa$G`Z&K(`1bAG7Y_l4aTwieRLbQ50$q0SJZ zr>{`ci|I`$T!MRpDjua!tx@)o8*xk%j=xlQ=ZS_sB@MPFVt#NvvI37nwD~fe?3-cH z?iqZ|@R0v|@t5n+vPB=PKevcm8Ck&0!WSLueTAV5c0<+Ub1pK)n4}btP&-+RU|E9C z0{9M)a$*sl2W56Mi!r3*FgUu>EmMpkaN(AuGY(|3_WEor9Gv;ET}DFmW!%oiA_uvJ zR^E5ERtt5yi7(`_5GK9QfK(b4&kho0U5lCuN87TMxy1I_aqge92#Hbq?`ZZ>ld=VC zQHhiaIBcDPMaK^Cxq(ISE72WSuIz$~vvF>$`)8hDWV_EW`~*w3R5wi>;_23k||w_a8tQQ_#i5p*eh+cO04!r}tn7+Lbnx`?x zOE+krqksCz`74knKIFD-?ER5gTI&}!HIkSx#I7EP`KJydCj2D(b=GKH;R75$w*lUM z^z)2EqRaySJ-nDETb-qC6%49B6g}r|hRDUVxi4g?1z=ILV_h;2iQfw=3FnsJ)$8+o zO^b@zG+$2q`JKuw{l3t;|1fxXYR80PA}%1{X8srH$cmDl>hYR%1B#TIgT#n5S8t)F z$4Hn--AU!in&zHwVd;zt?Xyq9Bv$|zo|11`JxANk!4pZ<$rogLY#v;}nbqH6!{vQk z9$c}x2m00?jBTe^prp5P@2cv}%b>2Wc9Birvc1r?b4`@$H-X<{TI5R8ThwOe?ywQj zB~6Gv!npZ8XPX*Rz*Mj?zxlGbBEx z)N$eI1{CSTscWAri<%xIw}&9q8b^_pWeJq@(iqp7lAHoOh|=bisO#ZmbpLB+IGk9A zbsOg4>iuop%rTmkIHvg^tT?s^&Q8X+@~m0g_(`8GoE1$ZC8>o{WnM65mps+& zfy9E`Z$%r5ln%SG4zFI43o9M$2LEBCTJ?d8ht`;k3_pj+GukYEhQ!XZ z9J^*To@4Q%Q=Z#46(W@q zR{ze#f}JZ77IKK~QNqr-F{ZWajURT;fermC6VZEWA2cghliQ9AIffT6*#W(>o2#1| zmD2Q?^CS`nh^>qMtVHR}ec16LBGakj<-B^JdbM{uh3mUcm7cB8zBH{a$jhQC8C!4N zLW)Q|u4G8eJ$;!0qrP5r1{A5CC!rQBY7%O$?axlA=}9EiLd1tg{=FPZu$zysWWl2& zXOR>i#BJNyR)nmiTBu9`4H}yLGZ9NJ9M+_qbarop>8(3r{KjdpG_f%&VV^g0&_LKa zQPM#b?%q0%z|cEfV`6gBjxw7BGa4whFWSkNl`-S&$(Smms~oov#@Vk(DDXEcgLMt)E&~s7ZUtFy?O9tnONR0X<1qs5QQh zGVUc|si|eot49x^NL9y}y1IHH#fS^orpIpURII#ulZmu?nVk~eT`;|EYkaZ(8!iD% zcDxA#Vskm(mMRCLW7VFL=64tf%A#j&!H4Lj zbqP~woUT=!YoX?zO99&crE<=Ecah5}p;oRkPFCD1E2h9>?>sEKb{EO<6ss&ksdByW zU90B!Z0#hyjLDNrcH+;GKO!+IEt%;>dBgEdpT?sc2NLPDNiHFv^ZKo<6duNejsSVt?8RkocrM2|8y>H4X& z6>8c~FQO;Yi!`g&ip?dcShXTw%N$uM@HARGRk?-(X}$3w(cAgrF=8XBdn!VO%Kh+l zvnKfDPyJ^$c{i+we_uSqvY}riG&Ji~1*GgZo;&gye~cK5eJ@TRB{m8+IsD8f-78~w zjT-E$sUsksy*z}7%_ip#NZfhBNEf0<>P-)j_R`JD4Jc+O z)THKQev?p>InLeT78V|GuT+6Hvm`n)717D+CGtgLSGd{ded(y&V0B^*?mkqrDG z!WVU$V8pU9TwPykG%JOsPPSNf;{pB}Jq6+F@z6BTjLS{ax8j#yW?|#wP*jnbq2sLa zIl6<$2lnCrCc?{;Pg+ic6}L~r_SsE!-2{krmf<%QW@ch`F>=>i(L<%gv(zVe{w%Wv zCdBC9sv~z^iz&R2z{Uq&K|<^^Y`=XHcTNYWAIJhXH!}>XQi*97??Z+Rd!M|3RAkuY zpq#CEq2}ys4MG41=`Y=^+=Dq;)Pn0nlL$j%?Fqly*{|sG^wKj(lhl0-OY4$QnkX61 z-Wzw-iS|Vf7+S47`px~62?YP3p13#yZTpRYt*rxozj7OEKADOL9q+`W$EjE|Vg}|N z+K0;zpP;M16MB6;6Qz9h6TnKfTrsv;Q`R$4XOfd9-pBFHOSx@RAX2xtCPmB%buLdt zAwhoo5bnjBWx5Wcl}%0bm{dFqv9Kd&z26&&;ara>G4?XfZ<~k29IR^ZR^8#^t{qo< z9q|~-+ei4CVQ{d~&MnZnKLjJ03Yh?$MrS9~^h7ehNvO%H=CHAFgR@gsFH;lp_yrUS zwYphZyCTU;o1My-1VyW-^x8RAL6zE#a||KO3_4zIht$PqH7X%FDHwClp1@Co#$eCv zf3afNJWL!q8b6;ogRrpI7*W46Mos?-H5z&9-5}8O2K+Jx6@9f6hHkt(1Dn&@HR6nk zxBy%qW@0f^Mbx5B4m^p6_tnu$>8a;e4-%{F?}cIto>ylt<8Fws%$5qLLc;L;q_BQ# z=2UE14fVaXo&Z_WLp;58oUa)Mdt0qQ=?o~6k_ZB=*_qWETRbhD({lhxfLs5;%%m|OojokF zG0G?sBH+~<_6BmU?qO{OrOEuF>PQWFstj=v`CDnbR&87KZQ2=LrD&B2CFIfolrC2f zU$yFjA3psO<2L;QA3tr^CAajQ^)Z^1ugBMbz~GZm?OepyjEAMA>BHpY6u(#i@!R<& z_3Cz9dmPKIWf{LmO9_VtC|B#<$p-i?v-jE+q^4-o8Y@x*aWWkVuxbo@H8znro zgNk91w~+eiEMLn%4j#hdB^e<7)h(F{wII@hP?JTi5$e)zZrR>*wDi{NNbLbAWluV9 z8YB|+4^NRMA}%1~svEh1e&fK|eep?UKh0ve4pKApo&N{=ec1?B`TSXqcEGSc{b1`z zlVBLC1pIsPG|ZFZ)O90aXY0yunT}Msf?Yeg>~<6aV&7gtSa9YG1N3Oum)|O;uo3Yz z2zOWigWcbLhwq1u!pSEY-5if!$K&yq?A?u}>y<%UA97Eq1&?~ZPcI$dwhe)Wiw`J~ zS0>QSLLW*cN1+x(nnoZw$d6K`9EJI14NdxZo1hIS_OsW#+~_#l(DY9rA?5)>BcDK# zPfr4g@u2xL(6L5sc2ulzE9fYW{kDSZH5h{K6FZ@MC4Y9Z)b5C=m~)VApT}(*3kQ4C z3$;R?$}XzRF15moG&%zRMI>c9UED%a5g$w(!qEK!*wPab_6Sj7*)O?8##$}Es<5)tTGbbVf*|LlGyj-- zmWJ3|?MF@*`9Us-G-93E-9!86iGoO%WQ9W{wnK>=a!Qj$9i1$670uwphoG2(QZDMR zN;lW>2q{7|0|T3F_&I&~;6h%y^L!<8_HY$_&cCiE> z^yto4iWYRaUSGZ(tBLhoZ*QGxxCZ>(4Rw;2z95c@%fWKz!+F8{pNe>{sYnVLMB+X(o-l9%BJCVYnNys#obyh+xSk&|`nzU!u z)}HK(XCF;L?uJBJGPf-j+d?VHkjl?!sG9eHt(`g_a8%d@Jh^%qN&~qmMy&V}V;aC~)DLWS##|!N3>!JC_MN_B)??>YAYnKq87>Ur^_xZg=gQzGaj?G$#$#Z|i z(o=`={L%JISAWk9Vc}2l`dY^LcwI<7TU4pb1Xb-;g;ItS&z|L>S;!o*NbMRp zlKzsxOhTQl>w^@Xo~-d**h^fvL9px~*N1`+GZULzLusR#D`Rvhx}=gD@0w91Vbk3w zh|XW|hXxA4o4R!#zG%=C=9aG5e)}QzO`D0RSpEt5pzWvaQQkx2VM1{5RXCpghp!nH zH!qK@1~80Ec8a--@u56s_I?K|S5a|17}Ex3?fqO+doi z%zs~DgY2xkM;~I*gwHVZ;A*^iwJ*~}8}foI=@Fh>%b33vP^a}#lgjF4QPYEh`-7g$;bLu*V?GPAs8!tIWpBcST5f0Bu6FBs4cV_& z|M*wom#}yKE^gZpSX!B3#?JW|->eO+tekKvIRV;Wt49H?CY z*N`YAHmuPx?SP^}o&a%N(GUhA;mr-iMQ3z-h|Rpvvpqlkzld>X`*Ca!4&|&+d1@S_ zk%iy&k(vq||KB1^oj(o#-QL2lb81LbICk?g;wlh(E^huvl;=H`S|BPyju+u+V><7L81PH$pp;m{ z!pgJ*it^NO*1^dkZCN3Ky0ixZL)kr?F*L&6y%wquD0*&>lmsbmUfYC+53g}`MQSXP z6EjI-Q;3Lo~VVotN5>wEnO@#;pGuAAXd-l}n!7j>NAYkH_uT7x+C4ku}PdGS1q2WKrAL zs`HGP3Y8)jF^NS(sAU1S@H{*Vw}+QIEN#deXk6$XdVDDeXMb9RUq^h7K5hGMKi|**(eh^0W7=;C+XX4btb-4b|e|UWQ9wOf)XhcLImEz3Lv+&;w zDHrL$p^8U*igDxh^Mf==jtx^LVC$`Y+(a{DB2B)JTbnlWwXCVwwmq!vwC;~fp>ar6 zsK?^+gN1pPLM@0i30bB-YVHBPXk?juD9%i%&9ahq9+^}D6(0^knc7qZFxp?H{5 z*XFk6vgcT~aFx0NDMV0M(4Li7F>lHgth#i8n=M7q!U8S)d&6g_aTdQ-TH3(TF%L;l z^2v*s0$BY#FFZMpgqVy2Od_#6+SJo7Qfw?+%&Jn~fXP^*PnE!gUpo06nzjZxP} zn=|{&%bR#`+W@0}2I5sjIkf}NpX`7vDYMk7 zS)fYf7Rv|@+J_sLHeuVrJ=k#O0S;V^!m*=gGQ<_XN|6;h<3%9u?L#CbnXX}f5gUkr zOAq;4=8%dt@zJ_*lj0vBB{1iK$egguT&PK9=_57bnVIa;gCx*`pM-;EmbuyH-0f45 z70u5EQ7M^qt&I;fDQ)UEhqSyyYAF-bOi!M{sNrCGp$<=x@>_)$xhw&-|@#mhK(QmA)z ze$ii;zk4H&U)yPz?!GDoDG7HF8FEye`ZpaBG8GErDM?OdBB)G2QlcrWX*Ef45AgE8 zO?)kDS~hCL^{hBl$vFP%t;WTaSFAGkC6j=rdF%8-Em+bN=|O6talneqtYB%8=?G;S zxK^4EURJ3TOyCPfsAF0TM!gC`(jygiKe&psdwCyS0r2E*G`{XK31b&8z{Pu;H8G$x zK;h}#1>dyq1B>BvS;Lm;i8ClAY#q5Jq)bVIECmV^T1k09dHW!uVlt*QrEtd1wT+Xi zmqhyT%wI8c*Lnm7>|$TSH0qfoS0Xkz`=mq#!JB{tT#A=*dD=||>x~=>$~h{jLia9k zaH_)BfTvMWNDj?B1+KnGG8bx2Mft*sX?me1RTV^9JPuU zY9$kDRoXyqlR;hl_VwGs-lYjwkB_;8%}W*{$;7U?c&1IiefB3hb{mO5&#&WVHVV+H zj8$IxNlA-c!c>{w;i!7)4{TTe)AOq=;FB{?37V8gCEXJ`^%-9SoG#=H>LwpCEQ zS3kaHILN3u_}zE-_2f<@#$Ms~nue4V8R7#AUu^rE0~>MU5sla6*H+CPkE7xc{ycjt z-l2p8%Cnx8KuqKlm_N#2k|wjv^-&ONItZBxHPuO&sc%d|t(mQ2Zo!0FlCwokQZMG; zR;5y!OnfpUy5)!HRk<7~6RRn(|9K#G{kq>^vlX+0){Wf0>?&H+9)cO0W+5#6Fgx^W zYl*V))v5adCbw*hQlBnEVtMtzr>T&6_`}Z5v>|%Zlt>^pf7XXFp-bZUTXv&nV$u3K zm4;WO7m)Z)Z0D#AVxfn;tge;_pgjy(^U}q-O^esrJ$$Zw#L$t7TfVox9nP|+J zP+OR*EpKkxZ~O8hN;uW6A3Hlz(w@^9@8rq-L5qFN)pcCoTfRJI+D3zU^`0 z@j5Oro>wrwBu{`h-D4fq9#CCjTHD*&WUCE#ag+yToBs8aAC(6}+N`QpVX z)|Jip{md+V(4=EWzGfKi9=(TOwyj1|oO%LEL6Dl71Vy}o@+%mKfTKGR8Jcl59aEr? zB{K^-XX~v@>$-5a*RJ{!67v@FhiCbk!N^gl$#5W{7W~wjyV=6h3KE^g7_-8PX}LtO zsL4kiX_$HW`5~d(_vltzy{d3T=vi!g6N9;5e}wNo`2b@^^~dC;KVtg9J(zp;B7Q%8 z8H>(b#^Tc#xiaU}aZKI63FFqS#L%ffpjpe|=+a;$zU}rM{+YfLTernx&A&jfx(~sfP@czEl`2hrGYc+EO9O&({Vqv;IDKF#kb807Kfd~23|U0dU5w5e{qhl<(@2DAC&D)5Ni@@WoAMxSk)xfntr*Zr39AtId_+7 z5YvhubX24!!XlyIZ_ZR(M^yD|iptg7vm=v?$M?1}>ux8W2k%8h_+i9EoZ?m|CSPHy z^f4FLOiTBu%X3zqan=uoI?>4d*$12P$BD)G?6)uQ+2}SH_emLCx;C4ej6jIQZtyDE z4HG&}z{k_qLiy28g+`zSES<{0%2vH*qp5h47=_eAm_?87gwr8pU1sbm)$}Lqhk;GUEf{REaqE zDjzvWGFNI6YMOW;Sk;+Y)Ph(Gg+pR#sh3bwE)ZKwb>=38JR0Hx&ha2pDdBqHH~f3) zG#=g0>;@6T!m1+doEpKwwFR6#+Q7YJM|k>lg{xrxO^Pi^IP=ub`!>rnZZN!xd{U&9V@6sK1j@s$u4`LIL z8lAULM?t5BNrF5iD7qGP2B9W&#hU(dm|K{^$|>i7f0|-$E3E3mgiM}La6)bW@-7}N z{0wsr{EMegw`-=P(x9G`TPyfi8Gw;3hv0vmdg0eT{qV;pqp)_yWbFCtckEri5P#43 z3M)UKfW<>5VoHZe=v;j$yga*P8-pAfeibnaKO}i(zY^^@hcK3yZcveYQi3&|sYOi)Lah;Lg5Jp5Sr4IR7PY<3 zIz^!orVpwYDHV`y_z_DNeT}1cH*3~Rvyk|sZnc4!)v-T*8}c!>{QVo|?pcm6|DB0p zOFu`S8GX@VL^ITEQ3_QXd875P=IA%OKSrz=kLkPrhpiWv;qr|wSUGVXn)?riwOw8I zu4xY;E!VZ_>?$N~Gu||CL(#;IeAJm;0!Bh9!<~FCI&2J*W1d5rl(F0G>Fk4Y?Hcj5 z{1F$Y#EB;bH+YyCZ)EH~DI`KIGI6+QdT1pob8|hyk9lgA)b>V`;-4TXEN{zmDS3CB zF!zK(0xek7Sq>&;$<-Y#d5-2xJ=l2v&N;vz%T%uhM&J^6&S)sl#K)1)OxOgtZUs^-=&8=bWSCg;hO!J1?y!SK!dM=Ooy;FIY zM>>m|((-AnY1*OpRj5*^U}dL?*2@x3sC8T<5s4=BgWbA-KW7fZjThV47iKpf%u(LA z7v_zag{?=|VdRQ0P`R>Aj;UJpfi9^UjXjw~J`Ml=y@|81DXK((Li1Lg-vh}%Q~5oG zk0jqZuye?MRe3`bniLILOyLo0*{g?m7N5~KYAz{*Dqh+!to*Wf?_RFARhUSWsSxwZ zbU8J$!^*o1BogD~Qqc!9u{E3;w&80zQ@UMyNNltL0#Bo)PzL9ILYJJ+-Ji}!Er>K( z)48*#RjEo&gmZ#?)E@5IZcI!{{&TkK!>bL8v3&Um*2Uyw8b!9~P<0rVty_&z^9R7& zgSARVXs~HMPwNiO*mP+fMo`=KcuQ_*2VI!$V3%EI&ZmBdPG5!ri zFV66_f=B%P6e2T8#AIh*3NAJ3^R@hQ^@U+pt~Mme$#T|ZFB~hqIFlM$zP&sIUJz5xY;!&`dB9n|ok*w_mXG;370_Y^_%VlV8YvKXc1BSoPg3R4Ln+ z?NNUyEnUCr&I`EgoW<1(6O^T|o~vnNscD9y?5*jiOlO5OHUJ5k3?#bPyP$L%b)J~~ zLIZZmio%zuKP4p#@xda)bK2X@xp5_6ZbG&-t2u z$im)e^qKUPhjt0pwD52)gqqWxoUKB#Fm0S&U~ZufX-iThb0KoZLbZE2ERp0V0Br;GgF~u)ju23KcH=eJemDHr*PE zG&}U#>%x=sBUTlQl*Eh!oYkFdVQXiY7M?QXiA-TFd9d6kPjt3 zRKJyrU7XEeWv(`9Rmv#Dy*7LvV(X(DaOSBtEupP#Lu}Z;j7thtSom;8cYNESC+ChK zRLTgffAAQNmkhJiyrF2}ZQ2;t#Dv!n1?nv3TR@23-b8d%#>7N+HeT>;)r_y@9}9B- z>2DL64266!mtXZJ7d82-*8b}xy6!cjjxl|Pn{6^45}Bo z$Sa-_52-Yo{oXWkOJQ0OLdMjqW$azx;9xkPvc0<_%rakXx@-LG=M6jwe zb$!q)1d-O5-2}Nlq%r#WS8c2SPn~7;U*>Thd{)>TTZYhZZ757-zZMufe-K|QT$q~! zQ~sKRnq}2KFdFncd;cMv4=?5Fg@L46o#E(gx};Hb46cL~e7=kLln}@=nUPrA*M~cA zhQh|h7Opl07bBb%3MSMAoGv~C?jqasaF#X#}4E71HKTh9^O&vvhrsK$EFWN-kt?;!r9TkS6LU)YK*LanzYco@ls{ zWefIRyIurwE)_y#=7`~q>*wpnqSZENru*H;z;V_iKY9*0!+J1RZUGOU12l#z(;SK1 z477a(_{ST9qRb}&a| z?u4q9)#u5ORC3a+!BMEN&qD@lNu=q?ntoAoN;IU&`Xx@GA79CXT153> zNRmYuEZV{O#2UPKtF?k$oExC$j1hb-cf`dhuzun?^lLN*4Qus9+ZJunr%Mg=>ga~f zt=-V3nI9V0uZwmK24Y(8?{VX3_DjlVN1rL((SciRSA9&LJU;{T{|b*)-Kumcc4L^X zdmbGdj?~8m=clHKj~9^{=TBQ%mxW_x!!M0i&)e0c$5$V$ZQNj6R=ZehVSyqUzh3z} z8kHZ&ZWqJGR#T?T;q~ib;J3eUJ)QjG>g50@8*SuAoHUG`=QP5bC9f0IhKi2sgEj>J zH0iMBjj|GI5^0iqjZ<2h9159K{|r}d9v%>h)qYuN(i=#g+~l_N%Y*QTNQ%9|*F@;< z?+-8E+#T>__Urzdz7s7Q48@o0=HtlybqERn4~g-YAeS){nyf5VXz(vJk6KdK? z3Qm?Zl`LH!g4=^eA89#YuG60?3xh0G|A7Y=KOfDc7ZN<_)BLZpY#kbatKsz3xZVJ) z?5d+fkIs7g+30wLgaaS6_zGYCJ`PWUc0n#xTjy4GHR0vg9i=PuL79qu`CQaB*dyvU zr&#S9w{~FB^_!U2ZxUjna^G#Q-KHFd)o;pNE`0EzXOD%){)(cd9#4Yh2m4DC%$dg2t(#CgpHH_Q^j+F)S-TE4%6MUCMa5=SU}>$LyMH$z|Eo9H+&+xRNNs-W z(%$}PJW#(dD)K{LyB33Hb^74w-IbhB5SBKT(V^N1%oy|y#ti)hpN?9J(c{))(l=}H z?Sysc+hPW+tkhYxM63-9c)SD4?*wAe@QDa|k=DPd4-=M6K?&E^>I$>kw?7MlUC@1X zy#S%<>+XfxW&}5DR`oZa-gk|;1 zSAtGP;z9|zL0XRPi*=i~V0zby@b&GPW|B!xm|6Owaf5ysSGy{@{4$@_4L`}EbU9k) zPis}*f06SX0V%uwl9Drxp(gDWtWizZJ-twqg3}X(7m!$Us!k5?7qR+V)aC1!ft8td z^8CX*rUBGN6%&YrI5P4H5juF=!PzzE15YA-?8Fk=9F1oCJ3?`h=)aY7jDC8l-d z(Sj0wC4BgrVIg^L-nJWT9O|fd6fmwHEm5~mKfYG@adEdmx1YYlnzeso!LaYSY?Yqg zUEtu-oEsdZ_yEB`KEHnQPiQ&wd-WR`0jJ8fnNg~> zs$WFq9MNPRau2f1W2njcBkd&(&Q_=etD2suF`>yk4@pSn+z0wV6R|w3wJV53CxyT& zGIw*~(-WT}gIua!pG;)#2w!J!Sme-iBayy*Ee?YQ_GCBaV{RW=6O~H$!PrlJgmv`} z+`jCvVn?LI=kqYL-voAnx99dH#a+efn~(AL=oy^PTVKdc(|5*a@bhiK)hS27j=*qO zhZlVQi};4U;N(p19@9YUQNF#lpRbt~QDQjcsc9`58;8=c@iAVQV%6U5(ZJVmOMB%R zku3&Ssen?oOy}w{6pkLYXfkdX=5Jkv6^mEkryhOqP4mY1u6+-D-*GsmcNmQ6?fT=# zt^=`P@fvjbX&OAr8+YcdXYIp~pR%17;NZs!Y3c3ON zi<`Oaf<$3ui#i^rT}Scs^8hXa!{j7ILMcz{x^i;#fOAdtlz(GDNq;^ZJO&mL!w**G zhLS$5(0uS`e68qGvb-xAO#Bo*eqD^7bC;mU+(qa%_cwI;bsm~c8G};gY)q0l)0W-F zE>_w&`D9rrWZ@YzGU^f=_nKXQ5zJ822sJ4yErU-YN3M%(Eowqns0C%`h-Y2Jh%nl% zAIESFh5YS6;_zyQ&tJPN>qYC11;&7-leaY5M_^l7!hpW@zc(3mwNT zhlNevMtv=7cE(pdKGnoThrBv~RkyBT=ez@4T^}gsyVbbQ;OMNLfknN9y^jh$_XW|I z36*ArWfKq-8-$dQ0_aUh$(l~vLb%yT;9AzW4OP{SwJ@ko7xvNG-li!bH^HaXE5NTo z!8R7}5;Sk5q@DJB%2I-$3VXoUGUrAF%`H(vDWuO+L#DXk`XJra_*F9#YLa+*5-Bn% zyT&n{oDzxqNA#Z}S+-?8*g0yudbfkKZ)I{KUMIJ;mDnDRu35)eQfkS`!CX{5_=J-pSWYhf0yk8l<$Be<>>qSm!d;Wp1HHdf?0%Xj-`|=YlgO z=9cB~QT;mTIBN#Krx-&~-d$(Hm?{Y`pJzSiLavbFX`EUt6xszvNU;TLS`catSy|MA zNRu+AiKPy{p}$pJyKP03)XB{DHX#gRc7x=VM5#72mA%9++a(k*aQ!P$kgj*JaWlm6oC|DlqfHfOo9F|+hR z=VtZMYve?jNpfCNubA9X)sOCD?e+8|NCp(Ii&{7JsK^E--AxzgT)wUszMDFSThmjsNUD*b z6^}YKXpW(MK7~gO(@ko6pK$W^V?Atbi-Q zp5$v{w69nj){Teob;DuqTNzy{XlHiaV}f&hh5qgf2WH&&s#1Y%sau@L=x1;^yPn%F zNTf~r!NFPo6`Au(Qv7wiJ-mvqnFf`TY!B__Z)WZe%UldD8-mJ>eDJ^7bI_+=UoM{C z7?69edxN(4{Ijp%+uU$Dwu?!NWBH1(w9-O&a~7QptI-R%2o142Pt7h7S#REa&e?jcqGb_~@z)P%o>kOM-6)338`iA_oCDpgn?&9Dh^ zvg1wUO{Cs7-C(LpsbFHQ9h{a}IC3i!6$BLC)oRM;_-*cDeAD_fF2#KCkCA9UlBMJ-Hr z$y2413B|=7rSx;OXffhd6kgzw=LHTIN42QhM zhp=<1#n+4vtssB)tm#(1A&Xk6&c{}R1Ee97n?M;6B$P7E7^trr#?oEX)DdhHaR2r1o=@@9NP#!=!}GFHIY_gV@}33*W}Y zTt)1;HGIvuB&X87plx7Xtu0|$2&2|0L6j5gb$OQrDwYRo*8m#SVM1OVsN&DWp)62} z*&`lq>}D?3=K`gyE6q;iPl74|sc+RTSY47MULZ9!fN4Y;kr2EVx}kY#$WEw9aN z4ah|0UQEfE9!5nWCMLssWeJH8mGPAb;$pp1)pUwjqHuJa57iKOU)>w|PR!wry?j?uf& z8<6VJ8_;}5dJ~SQ=$E*%CwEET2lVNz>E5R#K7#aV?&GONI`O7Tr96Y{Ln&7vE>-5j&*FUUy}+xhx$B;{9@!cW4(jL&THN$_V8+#-bHdC_oA`uWFlm<> zV_!}thnZL%3PPU|7G?Yhiqu>CS?}VGx+1bhr-~(6gQBkL3yndPbJd3SB`B)_Fb`uYi?6N1>a ztr9Ug42v@Q4S(^K}Vz+C9t5l{9=m95A4EH$hE!UWTnnHh)RLGk!n{+I{!5JP02D7CcC8b6R62fsOfs9 zcci;T@Kb9NG$q8pz>}RjXj5;>S8+xKXKmt}YoXz}n%rTh(>;w%iONEcX(1QKM}~ZacS7-1X~NI=pHet+WG+g#m@c2JOqy zdVi)PB>WoW=Z%;5L@L+bEFzI5%xsH=x|sYC8UBh1we~)iDz))TvIl1nlMu*6HI09o zHU$4Pncrj>r%`c%oGfZVs0E8!Gk07ri^SPycafyAv2%u|eD|nPm3>w9>Y-5)w_tl^ z7q_hses0>BUa2AmZ=ZzmH7#ww?1SpQG!v4cO2oRe`yr3a_`Ljb^S|E_9(sb?7Kz<4 z#J?0eeW6~aE4L)Fw)9biw!!kYFPN#!+2Pl$AD49ARM5nzL)R}tZUomASs+qMSk5C7 zTSDTX?THmrOkN1243H{q4_={Sx+_!Hlb)Z0G^2l-ENI<&`6EjbA_DL%cJ)Sm3u zG=A!{uR?J3sQzhBi3a_;!p2^0@hatEIDbd~S&Ee_YO|ZAN+S?_HDib%1^y4HRTt(G zwFQ_Ee;y|`&EmEVh2rQ!?0s~Di!323S8Rp;)4pbw8CT6U*EStOT*58B#(Ex}rd#S~M-NQlL`(6qi-g;Ivp2z7F} zqb~wDz0k3y>B;1wAWbEI2?;eB#%YvXASa=w7YPH3B-G?&(b&)tVb^hgXYN+#XxOz1 ze4YIG8Z*&DUc)R!JDV^&tjg8mR*EJRsUZk@e2@Kbu9}7>V}_%mmv*AY@kd8+W%YRW zqO=Dxw{Vu&;otDf+2cq_P&Y6(4)yRw<0h!yC~x9Q;|iO0?$u0t=kg*q>a5-_WOS`r zlYKwa4Z0=A-@=OvTQa|L^9fDd5+IA(vRJ5#$s;r)^!2Ipc>U%TzekNiE`vBB0`hw& z@F-lJe_F0%ATY=ZA)yu`J~DNC5c&u;y|b=R)0=ajCX3;>GEQeQ^)BofFdX)tEIdg!n0N?A{Y3rX5)2s9o06C0Z8z8BuU|(% z7M!~tfPgGE5>b19z|SZ4ASRs8buF^S$90-x@b90h>v`bTl`x!nCgjcrd_7ttwR$7I zR=7~K`4~|cuDD`~$qODXz=JC{kr1Qp6%z`D z6w0J9#5~@ExaeEy-5w;$6xT~GWpaIFIgm)?B-8}GAxSa48Qn27-BIKIxEUOXD~ECy zz0vK9k6~%UyD?O8ID6@=dOHV{<`Otrx$`yPVR!%n0y4Lf2aT?T?}mH^pHe+pjoq58 z8}i`Q4*a}mBto`NflSW@RaFnqB5v0=_-EZ$Sa5PXlH!ClBgLpwx-Y)l_#?NHOII#}%mVS66}E8&)paliS~58y?ZcJ=bIek~wxJCd&z zPO3NR1anDQzGi&LCA8<(Eu`kr|DPH3R=U;=3AHNKIH7>W7zZgkUGeI1x&oynvm5W7 zdx^)985cxRDw7dn2L!zi&dlv`jukNzbzt{BG+X8kr!10w^kPw^J?LnxN zDct(4aZwrE9(2=_sF2}H%?()zS*VyAeT142+#lqprdEJvx@b(`XJHRI5-Xw#z<)*M@bxqUxEy&3~yX|0VO zA^&x9{0-JKID_!F{~Sc1lt>mjo9SarP+qY{bHy-G#)olUS@H&3ecF|ME8p0&A=2XylRt5gcs zUaA|+VhTUcZ@opptHVf0xQKtRn|=j*f+D<1GMh@uE}Lz)A8;C$9;G54*Ka1W$KtqU z40*`Feas4lg9rnObn#_-dFw0Gg8PHsl-`^^gi=FsR&{d1Bb?s4ja~3m(~okM9N^`s z4$~v>>Lz~z+ zQ_nky%FViW2E@%t-D5ww@hYB0rH%H`z{LB_%e|aYpd-}u6KJ4|3|{h3kd;Y4!XQFT zZ<5~qq3Mgt<*C?p?;hUfVqOVZy8YW~^J1hV-$BYH^#qcfs9Cp#W@hV*QzA`1h~HSn$O-%pEeGS=&?azfK?Fr>;XV_ruSy>iZvXXye2ujnac41m=udo2KS;Ez*LB9g$?fs!;$axGej+Ap{Q+Y(e2bC4 z4@J)}nxIT2M=nOv5JWOv{tx)w=Evip%5f99haM9ND=QiMi-OdbpdRl7}`N?P;if zLa#J4$ADUOm{154XO!4-{S@w=e#X}e7lChqVO^(V_nA5DD5^tv?3^0mvzmSg?=zjN z7d2v=4MGXG+I-D4yotC1>5<=azN+;=q%)0_7JpE&Ug7)7!6_4QXT5$#lD^zowGM+i z4#Jp$Un5kV1vuZ(AmYQ*8xbC?4(Bzq^g$ng(?uW=G!KLp`XCO^--s6>*@q^lv!+Qa z$v-Vb&XILZ2Cz|tnv{kr6K(x1J7dSFVH@(8gP zj&a*L5#4P(YL(JX3`vZ;jHzGEW7ZMBqYyw{qw6=4@o|SwapLA8&MG7*Y1ELq<&oGc zcfEkZ1o_jum8XU>pZ=g+K+!jAFI`4*VlI4aSzu+M%^R##$e_wB72|tAz}Z_^b}JCy zuiuQw$ei7W`fz{yf0(`hFakpM;o76+7}2*oE**cvML^_-n@g5s%OhrtsmN9Y{Ja~W z%77uY&jbmAUPmRj9eeVn zkex5MKgg=q*w9f?7jb;aA566Q9eU7jd`H;Z)!}O@oZqK^9^!T%PttLn04E=S4XL7u7tqtIL}NCaT-+Ttwo_ z{H@e(M^nmpR4OSG>i5(2A^dDEKm4_mS!tdL@!uWwB zasMRs{`Kd<-m}=UcO~vW*w1S03yLk#yY5G5{Aqvn6HEa4q{%l;#3cZm4<1Bv!cEp2 zQ>M=^%?UK^OBa$y9tc{0kp9vj6&bjC3uRt}n%)4m~^OF3nH-_Uo#CUiMMcY>k`QFVO6_W zYuC0@shCCmp8BY*J-lFTtM>Dr50Al<1E;y|+!D>yVEXXSa5s>wa_!AeDS&AvFNZ1~ zcY-$K^Y1^x!ck-K>@ZEv%bH}SG8g8p!kRC#zUxyhxEPnMW!YsE37DBUXe>s=Ogl#X7wdh8e?V;>?pNsiJ?46AiiCo|KR z$Veq74;zIW0b5v2Fv-R6E71*~HLVGek3WJ#lAx4RC!CD2aNK))2Zt^{z^Oy0AiaDX zvDfY)@@WuWU%iPp7p^1l%q2WLxCMXSe~3G`_cDuL9qHlY-UZ(;Tnle+Bi+!MK!GK` zo}APmp0ySKn>-f}Umb=_8`4Q4O)g=I8}wl%eJBc9(8pP!kw?n$K|dzP*@(L2MW_Yn zNYNLf=90vloRwUglPn8?OMDqL?V_E~k{#x(Wqf?%C2qe|`=1o@Pun(ZyZH(S*B?W|oiNChwy^hf;|3h54alo_llA=I zb}aVIUXQ>3Sc8Sz58&vn9Y{%`^N_BQ6xpLq?arvvdkPZ0OuI;|DNtHk!|T}v+8;U1yDc!O9+82pk(6Q=^| zdfe!XHoOe527zhkWZ6;HV26~`D|!L zA6cSg#gRpwtNy-7=fl{S(%Uv6i6eogG0^g?&}iTwR;W+UeMvvhF1T=WA3XI;T@m;q z3Ds-YXRWO|4XLAjGkmo8KWytjw7-sxh3DCe+Md00tY;Ar6hPkSen za`ixkk}mLZbAg$S1lIPpkT}@E#>o!0j@C$wibX<13{s-vkP^pAVgh9Bf=f(^!haD7 zc>LlO;-ar=qB%0d!KE2S)vf_ix33^|(ymbRE@6ND2o|oJmF)<)aS)+;#eNt#YAz)9 zxyz9feQghZUh^Se14c9*hR>G$!Pnj|UOf!MH{VV~K#)4YnWs+|Ol{i=4JVD^>REC3 z&^3HLX9i+oweAvH1!!dLRv5Hk9<0*oX~^AYIJ53=Y|HN*Sy6U8A~aNvPkN8W(Hk4n$3~L~3&Gt@SkcX|K(3HwtdNjDWQEdi zm`teYiDXUFPaspjDk~H#H;NVRNLpIjW7^=~F?IDY?#VepR&J9jpWtpFMMzM4Yk}cI zH^Qz}?z&-w^yFUbJNP$3qi%2mj5#8*f|-RI%q^VQ_jY6#m>sOFt=YCY#9|RQkU+;K zAzq5;m=GkzJ?92r(nJ0@OY0IyO^F2M5YfP_YIpah=vTEOY`T33sf&8?gLjG3KMup) z=iB+32@%<#Z{2>VKWaJ5w3l*@#J+xjAAfGcE;I^Y0-Dwsh~G9CXMN40z{ORYF?ro@ zkS5ZQ6NQjg$AIe1(B;!{aQ8383>?-2Wj$XSe2fWC!qq?j!2DzTker~M`{3l>8ei6_ zhW7va1(Iy^G$|?(_x|31Be!qj(whjxCj~Pjh#8ywxgaQH*V?`cJUz=|c!knvJ$)u5 zHoAtY@o{zlym{M-+|8>-{)%ab_8=xgv+59uEk!<%g+klZG&N;KaDC7|N?=D8HFY`3 zq9z|T4J78`YGs#s9)+49tD2StC*L#;{nL^v{aDesKzuwq;q2wTDCMJ{*ZDt5n4L_pv@$dbry#lso|FR%6(-mo6xx=rK~9K9>p@0yEW4ldTNRAWLYnr1@V zkKx&4D!(UZ;zX(Vd0aIpWaKggm-l7U_P^OVdUwuAZrS(U|1kIDZpf0=V?896&aO>R z-q9O1ef{9!XoH}zFq{m0f#A@~8Y75|sxNJ>=T`OyoAx_<>X z0s?S9Jf3xaMX2Lr32#elRBX}>{yk0alA_)~Ic9-ksqNJtZJI4qia=v!U}Iy+hKB#8JM~G zE6oqi4r;X;R33^)uX)Q%_Zg3bQgqmgK9ZPFtef*WBEydGdrXUseSJ(BG8xu2^jpR( zk=YKvPHN69LiM_v?p~d+?)L?7^EYh{lfuS-zbwPgd;W$rNxvj%G&``Be{&2O`4uX+ zuB=z@xv)b$hjOKue(`b}9SaO^KOBc{Z)5kidS)?$#P%>N6l+NC4JTG8d66Ji^!Qm; z1Z$f7JY<2=8|2EGCh{ZHgy5?ttD1b(WKH*Bg;J|=KeMD~M;tx86_u;$w;cb@m6!4H zSN$~BZx!DGX!C<{x}>yNfn{6-RN-$CcIP@?zqpD(c?tqwoMXqr91`o2u(olAox~2_ zHWHL$7DZJbKlrukfVy34aI=)M%LPJ5Hac2?o!>6S@~b!T_{C8ji&G32)>Rzds5xZ4 zC%qHab@=ixvE_>4S7Xr^PtPtG`|5Sr5e$AoWDsdq=N z{yy{U#5K&GzXVT$_c8k=^E?(}Gf!0VX@cP$x}f1FA7tz5C~Ek5sk3bo6yiI5&~I3L za4w`$jT@Cjn8KWBRu9GC(#=aLJ<0V;f=!S$y_6NQ1jwHxSkrlm1I@2c(+fzDb**X| zE~Z5UxuFD0i7h6yn}DBo|B$Vy2~fCO{mO&!?5%oWgIesbrffx0nHpT(n7Ew$8>>(M zhlKbTjILJ}6)HDJ#nyFEqjg1Y)HnBsM-3g~+h>EY@u!7Ycl#!i;&iNQNon-1*&M!u ze}v4TXfLp8f9*f~wr&=ri3O7*nC_RlmHVORz}c`ak^O`s>OXB?@CmLyQ;&(z!VSL< z8IO8jj^OI=I}~|#?aUJ#Sic$%UcW$O;#*i+*}=iW5v815F}QnY)cvTZUiWlSg4RFu z;BP~^ZtuzJ+haT{qZI}{ z-g<}R>5`xDJY7h_=#bI zeg5OeTD|I94d{HBE`8)l4II%^joZ|5uBogf4R~*Z<~4NWut93=siQ^f^R#^@zR_&u z=Iqph4J(ziYNgsYZl&xh92t_`sEv}RPtw*zCb#zo^0aaL8bu{dE@>!8jzZB<7DPCHh^FBqzeF1tkMUv!>^oj+8AhMug( z-P-R{o2ZOnQA_=RwOgM(K32EB_Lvb?^NWV!ePRqXf=Xt{0XY*`K_I@lbI!TXjjc3B zvTEz{C=*G8^5_rw1;nZla>501@yLC86$d-EPSV>CKkfK#OM_vz4^i`a1&a@6Y?!TT zbN9T8XIE%b@r|P6y6DVPFHy%X@#U@-qD?n8?5@H3@ZBdhyw~XgJKUsb z+QKjN-W!jo#?*ow2~`oNbnf6xuJ9O`oR*U9$;E*va$=uuxUj(u5ooP3e0#rO1|&DHw# z-|P9$UsUWj`)?n(eMR=s7pYe5irxLCD45^)YUa;c{Qk{*lrWyQ$0({=!Q|oML|>k8 z7ylg~#+o&fY6WPEaUago&7)pW;*Rk}e{DKTsk3T{WjDA#g>~w%1>&-3SVt&=0?ldE#^&35!wSfMrWW<>-ObzN5Okkz#n5?JAeki^BKPSYns)0p41DgAH-`S5KCF*|CIBY|{>hVkd(f7{~(NZ!RGz{0J zy?f*jqf-;7YU<=&vvTbVYJK~JKAttlH0sUHq~#sMFH(=*yGEysd*m&BdDAE@UFI#; zeY0Urnw~i8ew}jcC7SsbuBp<2rSqqb?5i)oeosAG4L7CX^Qn3=m*|x#W0dsf4GPbo zL#|qA)B8k4#qR&G!yM0gz53VZT2Fkp|6Cv!Ki^lKr-_SasOE%{ zH?l1a@Oi&FbG9;5W|>cp)X5zO>WU|atGEf0otsu_)Z}RzcH)Ja@XeC^tbCbSrU(9E zxgNay|Mb*ff9R+t)phEfBYb}>CHT5--Q)E6_}6v%A(!O4d%Rhz^!~K5TKnEjPK5DYuo2orW;G$&UT5V;Nw2hMn ze~#a#hwr^t<7bRfjyJWaUz+ebqBocvM1b(=r{+yh*3&OOpeDPCO7TQC(x>15sP&tE*4+8qR5Pc!k>d^Z z?}Rma>dA-n!}QSZhz1NhPDwLEHEZ=e<>jWC9z9iyHf&V< z_SI_Iq@z65Yx`ea`O#(jV*Rz0IR8S!$Nhp_?{-aJyHVXjJ&I}7%c-0e)tWZdUlYjA z#->GiN=~bz!DpZ1`=g5k3b;BNH!!1h|DJ~yzyGjaci-|qO-N}Zls8dZ&=%|u!2^VBA%rE=>)ELl+rxGG zS*I#4j!2f>19h9w?qK~gZjq9beQ}K2leg-KkTB)6FW81_Z&B;pC-up+G0MwHQnebr z^zhK(I{uQ~r?_m~HbGm*&Cs+pi?wyfBF$g6O-ujYsL}5}uSKhVQgn11J@eq>I{f$+ z`xGfcR7n3*`^vk%iKZ=>Zpg!Q%6aKpzImW#seY-J;9D+u1BT%gmd!G>u zqUORdet=cg^ek-S;xKb^+BVf+7ok3<^)0bMBs8d|@LB8i`?6KOe6|qR_6T)7(x~e2 zf@RyKMbyqs`ta)yv@>y}sxH`!)`{vcgXKW3Vbb`mYhTx$AH1ja8-FnEET`xu0B{y0 zjmv{xoUC_{uwArbKaJnuDr#U=HI8l}iC zu(xD1Q_a|=N~qC6p6Zo<6(cL2{$4Q85HK#g;!(3sKV3cWIQ1HGt=2E!s>EbEzj7HZ z77LQr|GR3H4$AYWdfPsZa)`;^uBnTbD96indX=&+M3-KFf-~8hWi(O2oRW*G0)$t? z1XBm=`@~Nmc5e6Th4b&!BftKt#BINuwv}6yq&XlSn5=lLK4iWPcLoH^-vcP$sjR4N z2;361^;?%)&Dn>6`X0f>bHNM~kV$Kdv7|D;1#a9tK_A`uw9Gv!5w&WC>HHz5t45t8 zon!Jpe>P)+B9n0kl^(I{7wf|Xi_PszcGUFgE$ge}A-*lZNOU91`j#D7RW2 zr8e)Zp;z3i-W^Xf1?*%W2pT+@OZD>DSG4;3=M-X^Qs%+Ot3ktp$m&94%L~di26y^*GxX)!rCK!iNBX3W=M^ytuIvJw2 ztN)5Ybac2G9r!xGw8rgp)cJSmuuenFdrBVV>Xd(PjI zljrKkQ6D=Sz2f$&WBT%TEquv(VuO2GF3Jd89H{4mOvl>5bg)HJtuiY5{^dz}{GogF z=G4EGkvdCW58<{B9RW+ygq1ODW>O572K)sPTavcmwVbnL&7)P)Z#iv(05ZNDKCdfW>fK^TI#|J9#T%Tsr&OO|0$&k*Zbw8`;1ER zZLqm*d!%aD$W@1~hpJ}X((S^_WMNIyNLzNSqi!ekP!~fewbpIcg2Wn1HJwgE_EL4} z)J@gv6*K1A3%q{WlX`yARAp!QlN*J^YQ&M3D{4@|)=^aj&ZOev$y&6r_({d)ExW2= zpP|Z)U@F50Z?$+e?a*7Xo7QW^X1r#MCW8aQAm^^0qp+A*g~!*{R{uQ6rX8DAZCRpv zpL*;rZV!CdtY#yUAkthmBdLnq@&3OK)G`T_*l?EiSkwm{h&5}ydh^n|^uoWhG}nwy zb8~hmE4xsAqU4yI2=qy_@(_|{4an|DxeDYg z)oB2G0=;EImmZq?_bjd6Kx}oM64N%SPiCg_I+r}Ln6hxKwr)pJd*#X6s5xs=G;_*g z#irCy+g^JYTF152ZyK-u!v|^jdFSZNvo6;u=M7Q4X8T6c6F#1<|GoRU61TB~j}Lv@ z4pqx*2+^#XXcCgEsWyV82--I5tc2cd3{)5pe4~zstLEmlTE5kYn_L7>kYE(_%8hH( zyhVF$+O)+XZ=#FlY|m56%$n-lzxYgXoI}NtC!&T?c2pnI^s=p-i}D8}8e*WE)R$tb znftwm_R1viS5N(>#~yl2AN)H(JGTC1$~7x6cL#u^S#8KpB{sSPlE&S!Cna6ss3@Wa z+*YULf%RC_g3ry~m{&US~|36b*5O4O8f8OlsE!|A*%E!r8T!&-He zr-Z!-TOK|@?YF0C!R8$fX|Tv)tx-@D=WkZ@!kubycrT{{>inB+uN zQ~l0IDt_&1E#E?lg<#6oENxi7Tv64UJ5e^kV?@NPH6FE#Ypm9XG%YmMUlfRpIKRq< zyqP42v|a2wXjhHu`Zk*7d2yYl1@j8Crfkux*W9Pa$BfbZ6=US}Y;#;40jmuGrSrQn zmV6T@&14yKX@NfJN6KruroMAb0$L|X`4ie*Hjvk1{Forwsld>A!`VlUW;v89EZn_v_v)C8m~i6 z>b^(i0`M8;q}9~Sm5JK1-M4w{=AEn6KH00NgG)~Qp53Cedben+oWwM(+e+@JY^Q-v zUAtb1e=ky(BaTw7+NG!&<%n;d{Z03L`o6N#eJe28(fo>|M#wwxT<5r|BO)|L^Ovqr zW;$)ckC1pB-MX_pUG}`wd~QUPnwxS>UARhHcKSA6tw^M#EYa`Nx2jEaQ?=<{GFe+d z(u~k*8^R`J48=`;3Eoe~>`p`$j~a14|8qjE(uGAbsr%~(GxYea_v)nyKWoz_Vx!ZG zn=qui#Kpmm+14Ko0T4n=C@}=i>ndHzRJ5W75Hq(QRn4TL(H;u*dQ-G$dxTCu^<>4@ zD7kdnbZ@4RDN8hQO`=}~d9+~TCLP|TpK@vzj&&{$xJg=e?4#P7H)z=wX8O||SIE50 ztF&<9G97jN09CJ1F=V}X_I6!!$D`V`nPh|1l5h>`e46T9_Ly^gAiz_vi6R#Mu9ce# zw&@CsXrQB7cTi?$_5|HCaQ(FF-cK85FIQ43?WRs)eb4CU6yBad#qW6hVcPWDY|Y+eR9bGPGo!nF&T4fTaGLT$OD<_ABBA?0 zwcD~*^S5ksw*BIBR&8CW^^-Sf;8`aqDyoDF*+-_@${&q+dKm{jYw^ zTwm#;QK||$ZJejS)@=2!05rrE(@gzabx?Z8qn+cDfX}*Xk0Z47p9MMCv8=5v9hWvo=x(k-fg#GZ%K7I|S2itc%WdM0kr%#AG7 z#!cnX;;kE%X@z8SVzpP^7TI+~59hG*_Va{=BKhg@fu#w^_LdJM8AFa=ep9U=#T$+OPja< zW5{`@qoRxCk47C+K~T|bjL8H?po+Gx4olLN8vBE&?cJpBG)3wh=P!cE%vdYW(o`Kc z;=~dc!GQWr;#4y=PLmdHS9+>%mE)S7t8{Qqtin6kZLUg2$iV~DVbfC0*|^p;QrsYE zhPbyYZdIOooY<>W*Rr?xW>ia#=gI5Wh5s|l}YN6K6yZc=@I=)yiDh?hbmH7QD(z<9#$ z@I_NutcqqdUuj((MX^7Lnre+!MOA~=#zJ#V16jHwQ-`)bT&+4Iv`fZ8hqhAK^tGC} zVxyc`-P}ygU%OHLI`)>QR_Q(iM0Gn>4c9EyqD>IKsQ}&`n!0MITIM!Vhr?Qzn{oo1 zHhFZ-)%R${+Jf!H4sAO`jjwug?<7VQLxa_eG~YC%w3KD4S*Me_b?BwcrhCo@QXF#Q z>!?Tjo|-vrk|}c{s`eFm3XAHg(1@ny{)a0px{o5_h8ZKqJ50IRyDr$t&Rna1*Ur;V zzb{qV9}Cs!;O?qjx9ska@&Hu_qpl(VmOyF0E>P(ZJS2(XQh_uvR5`8|h_sp^>3lQC zoU?8iUgx~WyqEW5_Vf5`z9(2!cJ_qjy6yZ&blv?=YvSCGl%7G`^q3Abueg|Ke9i3q zFwPj0A-FkM5^KZm$ZlsR46D?P!2Td=09jMCsA`ln##kTX4$aQmqJRF`tPvLvEmi7J zDoK|U4%fQhW@+vQe3#iy%FNXZH>%T-CzU=OYDlPRbvs&7D^_UL*2U%mbLGiiuRrJS z&=Eb3RFf7)CX@qkqm91mX?-&J6LTks1~TfUW69A*4DPhr*SsZpD{K_jmN9ck&(x6l5n>#u%PkG}bW=C1z9v_saR z6o_9!?)+lAew`h_nyrLw!cL*`N&aeyJz@V2FLJJfGd zO|>#>X~LY1{v<$oTCrojdgqvX-+|rSO9!G0I=5(}#S7LsY6+}QTBqt8tE=ar9;NCy z_Jon=8GCZ9A!9a;O%s zV*NsX1Kw-kkB*GL&^+6nmXOs20|m8H)g_(XT#`J$eC{~i52{d4U|o@3PVo<~Rl+hyU( zATNZC=wJd7HwOwD)_{Of0tv?OgR*?t8poU`paIf`u;C&2h)P*$N^5@-HLBVwYi=mR z4|>4v{^zD3ll9l^)jF>KU^O%(TpG0R(?a31Hf!RF)us_8n#Px|x$AeTUz0ZS)Nk&8 zRmouD>#(qNOHEk7F<_4FPoT;m>ztB`feyTx8v>Cp4Q;j~y z`;Pwu$gSI4Nvp=In&H%_P7vO{qsGC9s>YgRE#ElZw63zcA3a7f4|K@5B=}q+eH9UB z343W_!Zx7Z>@cIdNh=oV*O{v|>GR*TbMi7p)NQ4NhH>RO=TTKay!f{5Px8L}* z9(n#{eK+}it=aakv%=UXfQv(@!lDjUWX&s;lQBM!s988i4;@I-40RxkTE-w_d}}R? zu{9ENVLWmSA+Wy%qzz&7diyEt)m^W&e>uNW9MMF%@{PThFv$2QV-Ryu5~gQoD29IrMzzX|QB| zvd$ZNt>&#HF%}3_|Bl1e^`_B#HPjCEWa!^#uGQp)gk$EK%2G@BUG$VLe{`6WjHxVO zk;@TX&e!7gU;5AZ8t}00Lsj?6QOXNB@TFFrAD(vdXn4+k#eb&!@XKB2Ysvbr{6~8S zeAH33AM_vXJ($$=rhcUCG3wtTZYAk!gX$p2eo_)n4fd7+qdYw0@bgRcxiLhI<9 zUZ<({6;B`VWF5PHi9#m6sMNEc_n#?0gnF~|`Tx$*#_hiH+8cy~HC44nM0o8R@{FqX zrhX}J`WWSA6UJLQ&|ybLHBq&gda4&)L#^v1s8^@<>T!I3weQzKwG0K>Rg0AgB%Vq! z1BoTev-SDA6EyYLF^{tM(lKP+LRuE!hWvmtG}EtieMt}wE$3`7mA3j`k# z{-ln^AnS{a1(Wl?Op^jZjmb3h{H*Wh(x4!wfvi%VjQL&wL;~Cc}<>(R4 zacPnF?zMV((pSpMNiwt=t_SWKrz@`S?F99g6|3fN(V1u7q=lY)cNTJZ9m~!|55pZ{WRaadxVmb|MZ_JJ*qVxZ-|1bSU$-0tdKY3XG7d$ zP5ogbguMqMBdS^x#YWUs{n#36Sfi$z*Q=|J2enbNuAS7VTYJ^46Xh&~VUY>@FhJ(z z*`jGrsXH^YWyMx)TDeVYmTb_3$#XP!#VV~!+@R#lb;{0KZN77Ne&*s48r9Vh^azE= z4CKgnayQ8JELHlZvy_#UpZuQ?ufL3;rtwWvaO5O_LkcPAu&^-4;5q(T7^*fSp}fG5 zKMM$(Uglk6cHs=_+J^j9m>ZV#ZpJvh_uX6Cu=;!FxLxC+ z`sMGpot0qwf@QPTYs9&?Yu<{l&5@t1!y4*>J|`;fk^`Hp!_yMwjV(VyEQ-i=OEmiN zOO=u8Tlra9L?=9}u$VIJa9;{Q+PR)ZMrAK^B1TZ#c{z-#%MSJt4Ub4rM0jmONSqpTt8 zLhK$4R2HHJd^tB7e>EHE;hdrb2N`4g@%$SPS=Xs`j2^!EUY&DouM%@{kf-*-0as|; z{Ey9%uVNk2_AK?k{^?Q?MbyqsO8x#deYb3(Hm>mzb)8xR^zWSSO6@b>6BbThsf(|? zPqUYOV{QWV!u0-;O?5?|lNEaLlg_b=vLR&lpL%8V_3~uVzDkFP_=}vp9~A|%Hs*Q^ zaU0S`F*@d5W>~#+E`ySgXLL?jSY1U#*D-CUrlMow6dN0(xVT88zC)Fnou`cS9A#v9 zl$DvKtn3U!glY16QkCOLQjT}0`H%=%q-l4dBGp~dG90oN1dChIj%#^-=Zmgd9^j*zgHb&W5g*qrm z`88t<(O3;fVGw14!#t>kQrUBqwd!c zgPJ)7usi(t?rdFj+s#T%rV}2 z3*|Aqu_0mz*${I$k#ND`lJpHH$eX=BP*L-v2}H;z=~@ZBblstc>gv(=E1^-@!XAHl zXPRz(_9ZP@{Dm1|gno?&57qT8=j7b6? zS(|^6QHHF|KXPTjE*^L?-L5`3Dd^R(;W{#ttoTHnCtNM=GAK*uKyc+wssuN zsyP>B#fDe!)mMMNT&nVoY4DLDo$@W);3LNP^&kZR5&;iwKFheF0aV)jk6Ryg?JQ;G@n5{ME9_DSGO<%Qg7M zLHm4#Z(TK75B)Y?8`l_RUEsHdtZja4eAX+C;jgAr1x-~~Mxq9mtnoY1C0ep(f|12A z?Ti%+bE2Xgvc|7_YL{bm)k6=cZ~x+b&mv|;A-6SBv@q4 z+ni7_7nKK|P zQ+cws2ksh!dLC_z6_M5jLPHIjs;&&VK6VFwFzkuZi*XY$h8~bNtB#ylo}8QwgQ=auK(cWjRA!Gvg?v6>FCFz~;%ZFY)A z_Uxd3&6=uz%XV6rT1P1$hWZ8>bJx|rAey+HAzbba&M8z)pQ&C&i(hJHPQ$DI^ zuVE@e%fpY;#=jRRDXnOk7J!}LBdQ-58w(!@ATlO9@d=*!P6VJu!@+uS^t0-7BWDQYcM7uN8;3nY) z0ikxM2WQn$t|7J^^YzoTgvZS!7Az__CeyT=nIZjuhaUe&zVQ**lwLQapG2OODenrc0_n94LWnMv)e^!uroz^^}-vqVWmgC zk7EJmp3$shL&fJd(V9)$wSMC~XXDP;nhjK|$0`0-R6ay+U88Dap3&EzzoFkJeyNEI zr)m6xsrr5KpZasvO8vWhi552tIePMN7pCl|ng3cVCtKGWI6BIsYt)^`bRYqFDrmDFi z^>x;{XDiC^3@(*DfkaX7T>5}MU$aqXUU{8bx5{6`vs+LnrhPX{AB-F0kTf0niEVl+ z@8Hts*RDuZANQs{`r-}!IP-fY?wq0Q%yr7~Y&GSJh)W-E%UHe(+oR$3By(M_)!(L^;H6QNl7P<{F@dS}u&ty}Z0xwpk9doPN-+^yQU z@dv#){&{`PSdvp$RDnfyH?^#)&oKI4GQ2RfrlCC2N+ATCyg23b`#= z+UbxraL76!s)dipaV8K0*)&8)A2d|=Ju|}DBW`!7-n6OdN%KG2L0G`@?!R{~(~I}q zqHmrV=eSlZ`1*Hmd0#J2pQh~eY#o2aP0q%mB_ZAX%D3;hUyD|~f5DqE-ZZ4b zS@%d@bxDsyN|p1cJaE)mrzkwCg)>TL0y6-N=5cjWK zS$|@TC7uOjot@<2Tg!R*A8saZpS;9gJ3T)fDYI^&(@w)x3 zxAo-EJM_(?-|B~#f7g>|+@d$8PSC1VGj&a$UK(&p@q|zmpWXejKAQcH`Nh7SE^5_1 zMkfrpq*CVoh)7A)_w%QkDjd*58IZ!WPJ?4rx6vR~i)SywJy*BP^jUiC)7!M^mAe#@ z7Zg`tv4}kWY>gN=(x|0fC!@S+-zq0#qW{Rn{(v{NU;}82xVTR0b7{p*_8u~*vueZ= zA#7?f6J0()01!13i}V;cK!egis~S;T(28LNn3D-bWHK|bY!yu)I?^tmH3(KG?s8Xz z>X1&YRO5(~i*;E9V$7zCw`=r zi@#LE`u%j#1Wcz#T?uWH!s`5mT3N%L_&PQY9|adHGVhu!;B(+n)8jOzjK$9 zuC$_1>yrC)R*zxMu(}X(GnKLVTGNTxxO*3+1(^b~cRuCBKbHmG40Kmf6&0#e`uBHy z#}x%cbb|!n?ohZt0>D*3(pF8oL|qJE2Z$J&WipZ4z>E}xjXMOwF6^(c2&t|kJ2qBq zud8MF*2&|$J1qQnixzGznu;=1M|SHZJGT&Fim2ZaF?#&77j?@KebunpVCOvfuWvc? zJe@RxjV4OQSEGK_$8&Lc_uz$@G&2Xj8MfE_?SuMa8kO zv;aiLoN!)OJ@(@$ef!a9-8}Fb)k!$k6k;KtpAUJdnlR@(&3WT?^Buc(vcW?As~*>o zuBV&&QRIH-B`R~v)rN3fvMvd}O%uK_o!dFe*mSlLIGau9SGMeB-n3oa9x<`qb?!YC zALHJ%37f|F5a9j*C}~I6?s2Nu_yk>l^jSLl z?vqQ^`>$K;(Npigr;HTb?dB>Yo9KcL&6L!)d^Ug$2BMbER!RzknF1WvD#Z~)9_vwD>9~-j?6UpU*~g8KXwF?YB@>j2aObgZ&1oa=> z_HcOz|IdF^IT5pQp7M%B!-PgxFIC6HScNmP_85M;T8EP4&pSA5~|0=nPQ~H$^c2nPsO{bd?$3;c~e{XJ} zU7rxDs5%cQB(!X!L2oLXX%q(H<+3Z!-=nY!7FzZ^*!*8CWd{9f4iusWPy<5{9G^%b zLS{&jTBxPfsdubyA2wXWZaHeVG1GyV|Ia2(+f>x;5u)Q;H&yIzT&%H%nET)HqAq*q zKGiGbs@CyeEYfRJ|H$`)lQHka-UE~!U;fKt%M)RVYfNh`T#aVUC8=2H!F?Qh>k0bc z^Vc=J=S5<%rISj#ZaSAJvhaPcg;4Ye9QbKQ=@AidHvy5i8WjnvQ7&)pGuQLriKdq;?6Cu@gcBc;7dE`01<4TO| z>_juXEqpUqZQtnZw7xe$fp_g%O{2aar3bElPEpaQmjZY)mumFSAFKL|asD&G2c2Y} zbMI2GZn!~qoy5q^p0D&x!U&!GtPrHUU%T;5 zOK%9qgsJ&IyO}rhTLA z_SYE21iXtMb*>wC*7h4KEBbJ$SN&< z`*@B<+;WRjb`;*px?b%;`g8i1YFO9wboBE5c(>R#fe~&9f2`zmySqxhcx?tTK;@h7hyO zi7aCtbId$C$Jyq*d7kTkZp;_KYC6q4ayQd?8gu3pj1mqp=bY!}F~@vn4)ZW&=bEsX z!xUNbR)vN)_n#>nyy;_=wSCvQnUfE`K<`g_S&@6uhRYLI4!lF}PkzpMo|%iy7i5{BWe1tsh@wT zltdOf7RHtxKkJarN9&GZPig6*G-XrQ%YY11#E)Eik0I#^{xcyu@}TZjhNJ@K5t?>B#Y%<&UvC%q_7((6WzzU%nV zy=h-MzGQFOPlo))`7_W>GKzVMv4545Im=PVx!H@%HM6*=Y$P2L-o)9tJv!l~eI{v8 z+_L@fs8%}T#DV*bq`~4Xz8p41Ryh;609oS$qQ(bBRabSQMorsYVDL+`dnRM9{Nn7= z<}}^?(i5sx%2cElDd|Q;EnIF0JHuIm&TbMOkH;~drx=y@{ErW6z;Wm5mJ@H*cdt%U z@?P|#M0t$9^&|Z}|7UaLEBNZQ`>O9D3Yq`!>*=3=)L(!2cA(!Aa;OZg% zqlyQM-qt4iiMp`c14p{uH~^xCxXT??+4Vsu14-kTW&;8CR6Nuek?@Z6sad&OGN7WoP>Rt)Y=Rz0VnHbouMP<0=GpY=7a=OO=sQkl&jLrJIktQ4f4` zum4Qhk!z~xm~U6>ybA}*o6V*gKCnysRX4n;Tr&#U8$|8gq_7QZ6p^%Exrv*!e$!@c zNKDk0)Ku+EPgPP@sVTq{$6&+TUKnbX-xj?%$^gwcQ~vFI_q5KgyH#o#_~; zKS6Vi?gI8q46k;glm0a{qOJdI`5|-LjgIm!1amKMKkgdc@x=rC>}o74R;`t~cRNg3 z8M7Us7O)FCez{MLk;RkH7ubB)8P*3ZLctjDA1#89v)`W&A7O`>dssuY>-N&&jT`CcZoSm`oZ&jC zW2`Si#Jmf4=luCe8g}|nC8ZSjzDZnk^)V-C`+!UQXQ~#aJb0<5uP8hL)TY^~`gy|p zYS>`kTNkNX-hFABZn*0_<#-BK2S&#nqVpd8UQsn_I=jfnu3x6mjVlzMv{s=RJLUBx zYg2NjmZxNBV_LQnGc%N)k)o`u9m>wwB40|@LJFxwF!ZI}kUz#I{9<<@a-g$fEpUl( zMEoU+tTEC&+uK@LImCqW?ISn@!4_z56|Tgb@&75uFWJ9;~paJ_?I2Ik{F@ zk&`u3nOiRQ9~B_J_7VE>rKfb*kk0-y6^~CxP0+>no^9@Tl9QFw4>;i+W0Q;#5<-GF zR!En-s;;a=Z7+lxQPlVww8vN*zcfy+4l}ec%uQZ7Kd#s*${o?#Ma1b8ZTF+f}yDoh4Z2y^k10v?>JHOR~ zqi;~Iw;;AXBC4%gH|Z*GM2H!!b&O!B0G9<@R^fln&HUFqPAhh0-=2VC4vXySC+2R3r`d_UP!$gn@|3ap z{9@f6{X3qmcYk?9bxX)_87x4NXC69IKhJ%q=nm*qtmlmpSIY`$g6eUAmSa!pLuDaq zz8HguE&1WTu#fgRf3tt5)-#L6@<1XzU;}4aTU_;4dgJCtbk<#Gak$rb_x6u9@`c-! zq7mU_R-#?^0FXAP*a0uEAu>$HrAn3elK>U$kSZKD< z?v4*aBiblB;TdP*ts;?^yWJ4>EN5X3Q5|g7Kt?*6TS$0Qg@!j%NCb~fo#T*V;>9Zh z-t?c8wY~TW;``5fKv#~wX^;7Mf{B%LH|X#aE>+TwKZ?3N@WZ`f44)tQg$SG3%M}Er z!~P~}zR;2{6&;lf*%EHYBgZ_q*{Ns<_O;cGkMD1wdy_U7#N1 z*MWSO7-b()L(z3d8Kv6O|5q0d^0Jk_afs<>IQ;!)l4 z&9g>X<1#D63GW(%7v>nVZV!jW%NzTPsQDtRj3GSwN`hh`Uwq8=IjWWCkTqt>n)h%X zqU1Gf%k8L{dJPZPSC2ocz9SCjaIdj^L8AH|bB0oOP6<>|u)$l#=s8y!V}}{8-L8UU z2UHp&s)dCsE<93kMy1C_S5sVU4aL>0 zV}{cW6NBZa8>2Cj)~QnAT0ePdY`5-$Aq`XOTkzY6Yyl~UuT7<`) zsVGC(ds#IOIFL6aeZwi{i5-~iiY5(D*7!*usc|E;UB%*+>)+KaZ#`&)$cCb78Xq6Y zNZ8#2@kXv+Ua+fDDF;+EQB!dtThuWG4KZ7SgVaCV&V}dru~FPKVq4RK z+BzW`c)VzNI`keSvUh0D5~2C$e0u)(gz4fVuF~^kpD@S%H(lhzTaT+J$^JwQ3)u31i<^+fLk;^2Ov|*6552u26DP;k^xu z@h^kG#zYxHwDu8oOlx%F3n13rd`5BLbNL(yorY`E>UA|Z$7S`11jb|A=+*`>3Y5L=SPMM(u%5SQ~}w+$kt}n zwB>}LprVL6kfc$acBwZZZaC`-O~6=`W+E;SeY*|U4}ZK_s`RDB;g(bG*NfvG3#>3S zTAZM);}49Jde12Z8OT7+BKgdx-{cW>SkIY6l_J z6kX?Gg~c4@KXKqA&%0Uan}(Vv$>v7o6(vIM9knFCo<>Di*U_EM)Th6VR$TQmHtwUSSB8b}FaO=-SP2#L1di{K3oX;;1 z{CF%JAm%h8s(~URYAGfxUa^tYlsNy=_yAW`^OlCLPCVyO&IX47-UIJvIcQmLgu-(K$69ZBWY`p zFs~{48JPWy;uJY%VLzYGfaFp4JaYf(IQT4kEqqqs=ULdw!ei0r+!s1AI|O9pKxfS7 zIjX2Z-4itFuTRyuiF2x4!6KzsZ}~*GzxAj*nQ&tvqQ-4SYa@J$KSH2%wse{#zec8U zKd6(8f}9K7EwKi`8K|2!#NrqK!l>QipmxKxYW0uih8CwB&B&w1)ryF(I(c5oj$CDK zxx$I}=Ke9M9T1yNxsW#&L02e@#S4By6M8f>N=pSS{_3Bjsg%qmzt8Jc@q|E^^8SIUc1)Z&@D>y?lj}p9NquTEB;sR z9e)30hK5{uuF_KAV+Rz>n*Hvg;P+#J?gGm`lr{c&;@gpkbO1h89ad8h zpZkEWf90|=b*3f3Yp&$Mb4KauA4ePF+!RREJYja8a60B0P(fTXBNOX-BXWp3%Dr$# zb3|(F4J>JUJzi%hUlgaGd9l9z@*U+A`5WKmh!gXVCFfDK?>Beexorm|JSlH0d#5kT zqevtLZ4LrN5b>N&l(KQk1Vlw8=!7mO>AIV**O6x)T(0#fEybgkE`CtYOqr&n9TSyf zDpWw{%M&&3`ERvg+0V9vSO>a$lKCsddYsQ2Dd(|^xK#(VD zF~95IOW2!h3Dpv(eY92-_BGbHXP6~ZY;QluzOU%#ShJsHAB(~gHOBqGY-L5IGMP$Q z4>Fdoicr;U8lS3Pr+%b*=%pZl80#Ab-KzKgeqWiH2xkXe8|vo|W5m4TL*|-sB_SM6 zHkKcU4?z>9k?(E`ja0R`uBuU^z7nEh)G9tk^{T}xC$zed54y4-J(s&>y(jU zI{G}S$*u^CXr{DO`ikOV7s#+0FuD$lV=7+^{l0=^Rq30~H^0*BbZkXj9<;e{jjc6C zM+$G)RmTVk2NB^>YSeU)uIEYr>*G<;`B>jF3ED zF2MoUMi2pNnkWzEs^9}$X{8Zv`;1@`H6KRBXLwDGV|YoneHQZ*1>rOb)I@khLtS#@ zxw`LzdsM4-etA?FaC@|GbE;Oa%g>HR!BAny4r9Y_@M~5`p%epKFhvQ}J_cHGu;+O_ zuw-nFV_svA?epDzo(0k{kgR#VRn)k*hG8nX^tDZAu7aXi!k*48;FVjDZKR1mMPIf@+vuOahUOE&=+NpWeemE9{ zM;G_i{TgI9*`QIN%)DRQCPOgEq0{IHXx&gVB(Y0?^ zcz!MCTMzC>hbRss&1Cq}F5y3<-iJF_q1Pzl)41-w!aGf#8+g}-D zbaMA$dTq=rs#%lHa{u5pb|ZQ34l@Lz?KlO);s%1*nM9-qWtbDx@S6h!R3OS7m58g0 zB+``omByTM_sv7J(5nxBqTwUE?b0KZC;t6oy+&Moy_T&iw3{^q;NP6mxwia&M zHiLCR+q$~=g$I<-5S}PIRxH@Ak>~wSla~INpO++nsHtZhpzz@nls)WRh=fqr5r(Lv zYTa(`ZdLVL=lIi?PISnymEPD>d^o8&dY&TAZWr{(3_RdwKnzulRCcZ@x>|kp>2Z=S?$%xBJad(!z{&&3$zI)d+D&@@pD!G; zE@tu!^=Ol9<1RvB=aZ0KejXOtPEmD6IZ2eN8nSlWD{tC&4j~qa@Zk4D&bS$wpyNI; zk!Zu+xDU*EK-MU0xC9sK0Ap-Lp6?2=Kmd}xu%xhFOU^8?32<{*@ZFFxB+d3(*4=^o z6E_G2SfN}%qY4JNJE()eo6IDlb5k8(Wkhq`cJc}8d(!?T>2yQCn^P&5d?0;LS&)ql ztK+(@AJ@g6K?SDf<0>NAGj1c?M|6M=m`$Iv_E(Mnab3`B`|;;?*E82&=WHV32W3gQ z=7$bO$28TceFo^x3op{S&z2uagZTJxJ^sxzIabJ5E~+J zDka2*(#y$S;%qR>B9W?u?48f;iKKBO{A3K50&11|LcOuSpE_gf5r1df92TCD`21yK zxJnRE`3;zk5aQ*Y;R9y`Q=IWPUQ>jm`5aDgJ$$}(cRuZ;21li znNSHFJD{@mY}Hr89=_D{dj6C930uwWZ&#;~tYZ{_yT&d9r=#I_aQ!Y>mkcT~6`o-| zogQu@LK|_`It_}DryTEQJ^8@B&IDYran>y-=z+5@Q;k~8Nfky!WNlq^_%XWek!LjE zLR{(PkNE1Ldj8X=ba1Ov9EFEMM)~;xS>u1DUZLW+SL_s=Pe3R?h$T0N9nWu3cH$HE zPn8;;l=qFj<0EO$E+h@{kv|9WW%Vy1Iw)yK8ovnjh)0N#`@-w#^ofJV6+x)nH^vwW z69quT2*l23@6FAzujjptoSBW-F14J;UB3>H?h{t}xsn9EYNO64N)SZG(a8)|+Y)JW$7a9^XSXmS$_} z`ZVR`Y;ja=e4V};c|?C*^w21E?HRRN z5ApJwIY&F=rUbSxOW2&J4MM0WTZohQK%x*h5g8CT3K;hZ^^Lm5d-+b@YmLr;&!NFX z#tc)4@Zyp8b4@6CK8Nq1)8u{og``2SeAJK}iWHw46E-a9r_wpI>T&VCHSULR)U9Wb z9h9X+k}394mprRSzqrk)m;BC{C-|u8t?{GLaEkW&E_M$p6D|e{8Q&stW=Pg^jM0Fc zy3o4eX__?iqrKjIxGaDxo;&9uJ^cGvL)0<4yhl5oa?j)H(Id9P1jDD#Ox4wo{+}{a z=Qt!*B#!tuW7IY384Z)(gNb-n2Nd$h`Xn$8)kRcRUrtLqDDcXO7Xw3kbuPgP9;Ohw5fNvZoThpCxoUlfIPR| z1|BNUtPK6Vf`9<0Oi(dMJqnaUqfm<~YZoPh#${b$G%RNBX*{7Q%9PB_Y7}vZdi7`E z6h^uqojQQ1drgF2!nh(kGZ$`i;?oZ?@JMr;+cLzS;03it3@tfEP7LrD_ zGNDWOMM)%W!QU}H3j)P>FYVJ3Eef0aMrX(O@f{d`X51JkWegV+#~7W39m#ON`7BG) zMHT*jK_Nw5UNFj>-i(kMd~c{IRw72A*rOt&H1yEx^!E5i&8f>e-&1dXaDTmeL9xDrC-t@HA#biFnvW zU57_^a}qmM^j2R5@F;8h-SVc7E&7`P?F2%^Crw9WN!qp}2(c_gWJLhR{lKVKfyO!h zZhJAlhn~^8Qmk3!j5*{C%wwg1(>n(N_6FJt<=iAEPqIPHdcpNcmKF69q?=zIrIUM| z>p$aw!e+RR8wavM^`H#-ZMN`T6b~vomEKMU(KsO>+(=F*N2uERZ4>m`?ev8egNUdw zb!@kPNE#e=R6U*D^JsH(B1{GHO)mODeInBySH`_#f((MgkILj0u93Hmp&0W;0{1X0 zZMxDnoh45yn+yu{W^<1dFHvmCh2j4}MyUZ^BxL%854Zs_FWhZ`UDRhWgJqpd}46 zhOZUhJN_^xD}s6d4iwOkXt@k08Hz$Y5Dw=ZH{{0a zRI3giK~uR3h#FCYyAY2(^^1E1i9l=+9I?jao*_RvJB)B&9m2*Hl9jnrncE*y#^%eF z=Y^w!kGx#-om(zbPS(`?=#&6(ZICm)2anb#jl09T7Rr+l`7C6#XH)UtaIsrZ>NFf02Mv^liqDmaT8Cs%gxhxhq35oB)|v48 zFQ8|iI2}?Sj|QDap(+GxGl__*rhajcP}VRnqY)Nh5K9bE??zouQQF2coGf*092+d; zdKN2V(|O9tp6~eCv$A&~Y2uv8HdmCSAxTKU&j>C`LNJM%CPz<|ubgb^$8^%&RdS4Y zWVlXl-@&LIgpC6l&6&m+4&eTvA>kHrQ8Fla7z0(!kQ7yoGR$X@98ad+o%o{`&WFzr ze8ibs1$}d(Sfh+>cPf43 zP$%|!pOKfTtQ~h5dq8=)Sxzi?kxX;6cM>d-QkKbgmW2nBG_P?e7P}r%s>@Ar$SAMcCODB}J=Q8Ue5_g53l(pG9jdapw0H4r)|RjL#T5J03y z(9l}~(t8PnCPgABgd!w>pfmwNKq4TZM0yA5O{pp(e0ksR-v8n5PkYYJ*>m=pojJ3! zQ{3Kw-=B{nk&V^!)n3Z37t?l~ZB&d%gPDj5zfN7QXR-*Sjl}gp4gAob&q|(W)Is1* zk!5}P;nBXRN9ktHsrxR>+2)DB;DEGy4!O+T!k+zw0n&LWnzdZ<&YC^=ry_^?JU-Ji z+TAIA*X7^%iARdUVy>@~lM-eEQrm(7!rXWo$oMb0VuFd-bIf6;Q}hxzTk=@juogq? zK8Y~?o+iu?S5k{1h%wuJtN}>k>VO7gO8ZpJnZ2?d*Ii$}Jg+!5E8x6&e}Q&xt|BNk z^lP-dutrbTNnmpMy?)CF#;R*)&%ri;KpBT8jsgTktMEGp0Q-Q2I4iwp>fnQ~choiN z#lrKVA|B>knywkNC}coSFg2t7)aZU3UqRJRdPQraq%hZl3u4LRea1UTWIjQ2jfjb>$SaR@`l`KnLeN&2wHZ^@zzpSk zDfJ2pHmbnL>YYsM^?ZDh-krD4-3@#$-B^!*ug@h84hR^sQd)bDcsST0({R{(KO96H z%}GE~NQ#lo7Mhl4CL*6;C(Kr-yefWjr4BFFQ$!teY_ZhXlKX$K_H z8N7)4W9EaM=6_$6Z(&=SUv(Ykx5Hj;ywsIcU$`4zpXvqDn__Gj z{+51O=x-0eMDubS7=K%ESs{wrzQ)tRk5Uv1%0Dr63wml-6F*tONlh7MSGocvsq)+w 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 L&GqVZUE=-^HyYM< literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear5.png b/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear5.png new file mode 100644 index 0000000000000000000000000000000000000000..417af6c61166c727b9ab50cdd14a498c68744e0d GIT binary patch literal 77056 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 NziqdsJ&+yE=yzftZ#5;QXZo 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 S-AwW`#1E{fu22Os|L{NMEB7e? literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear6.png b/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear6.png new file mode 100644 index 0000000000000000000000000000000000000000..5cec7a105d569aa2f9d4b37f709496b29eb96e2a GIT binary patch literal 78392 zcmXVXWl$W?*L8xs!{V@u1b0jD#Vu%HgD)D~6I^$3cXtTxB)BZ@8axCG?tuWoUVhL2 z{V>(lT~jsFGxwZ6_w?-ujrR(;Sd>_=UcJIqQk2zt_3Cxn%TAp2o*U zsGcNx0P*FPb}9On_V#wb7Z%Q0tsSQV&XP~q1CiQm%Gji9tf!}^c$h(q{eFMC`5POT zQB)9)4_EC!bCwwy`Twl9`p0Z-ZAp<+P$<|Q5VFAECMJy`@HxM4>8tBS<(@F4pCzb~jkk#v@a)Qq_w z{6~4y>-UE{L$9f`IVJ#Hr0^F|GXhrJ_h;E6$y?(zt&sejmZmk{{NgEuQdZT)>}ksF9NA2k%I6CxPF44YttK<%hc?jjSl zrS{n1P|!`rrO=T!;qUBA)$P z850F-7FB|pfKnJ&M!Z<^z3j{fob^&hmj1#ug?03wdTg&pJC*ERD5;ls5@YO()mgIe zgozSSl?J3SFb9mn!{VtkZ#-Z-B;ZQ!NM80;{KEGdVikq}9qMbxm6 zSy!+i{qM1o&b*1_EeXy^!15LhV1Xyr1(BAnq9E{r62v5%sN;;hE#HfKP!qom_Jw(3 z0}nb#3JGEwA})Mp`)QIU3*sZW3Z@&11xN2y3!0TS-wvE8m4NaH^oxC&JsrGaSQ&}e zLsr3_kEC`);Z#esKzz6u;iW1DW|%In zFt4NQd|6{PIR#cZ%S~!A7zt=I?RUTi>n~9J0BlnBic6uol|T)?fp0YL`VbAj1u{~b z03(T{#tDMX=7tS!yPti+sH z3%=NRYgZ%e5rmXQHwrl)`E?`KnPAQS7wwap=Dn;=QtL@RAE!zrZs z>UKWJ!o|ae+NAR-B2mK?bA+KdI-U}oEcHmmLl_xSUrtkHkJU56Bs@OB@%z4j?1teT zktVb^93udpuR$j(8!x<$wa$Q*68Hy9i>+Curq3U#qOJ65!|z|2Ps;ycd?h4Gy&j^- z!APXf#sv3eh6Nd(44T{8GCPRaG+VV=mu=67M;A9#q6%|4GOxA7VgN_PhzcRR?nSIC z7Ya^>6t70bOPaxUu$z=(gpZ0!GH+ujc-`6Qay`?)yLX}Bm0`}B=K2gZsra9sdQ+vP zmbZQDKQU{(vNsZ@P+i!FWij4i0On~}jp=B#y~J%he8_c zoOY&KDY0NRx}LIaOhTXHf7m|hoq(f&^~eQR3c+PF{%o>GXxo&6!bQ!6CO3M@1rWX& zuzOETR2Fe{x+wS*d#2f}jK=yiyL>&^OWmfLClss&9j~e*XcP95cwG*Gv>|?UV%d7N z7AnVP4{Mi#D*oEPvc|V7yCqI~Wjsyb0{4S)x_;iKpk8W1p{g;F4W#Si#_YN8znA*5 zObYB!#~PV7lD7=h#p#Z%X|`ac(M}(-UK?11;8{Y@>P-;i&&65>gQ<_)p-7+k19NgTP)sbIcd#F){T15Lw25K za;;u$%Gox1!fnd5pZfU`X(fEc!3Fpe&f~*kL>$G%$j&Up?6P69+#-arIVcxgke7D~ z_!-UTZ)AmW`^PvDk~|MZJ1plQAx5-NM98a9k)^(!Zr4Y5e{iK|6Xe99svtsp_1#mS zZ}m;>VDO27%byXbtSpb0D)#Sx+JquN7==}bp1Td(>t_pM)&Ia;8HC>kMl;+eal~k% zy;a_P3m3rfkGu2}tM$#h?2J%izPKSAt>p5I{WatOUzWi+PX%bnVFY{*r6)r@t2b+5 z%~=TFV%DxAmlM@h45iYNXQWtnGctpw<`D@OHT3+M6=z~2vk%iuvd;flx#nH3l^6*K zUJ!o28jec!gsA@N1LNx4|JL5>C-<__;X@;LVKVdUu7~3-sGZj^< zjYN9+aD-CLe+FJnuq-RPcWa35aw#Mh92CW?%D;9Z$U|LMunL($vP?Cx0d~U9`6Iv_ z6F+&)&gu3=t6qzdM<_AGTj)+Lg?a^y4>FuRW;POeo_OKwVn8`Ub#Z=LFp@J(=N0=T z{Req_4O$C01_^i*NiQ6h^~$7NjSUe^+cm3KLx9t50wjlL-wHCm_7{gsssJhcC(ktf z!-~6zUV_*kkAt&{@3_r3j#OsaY?)d1;;iv67)bH6sv(*nZWDLwQHunH2%DHM9d|(? z{1OK-)=X{1u^O^4K@A=M%1W~_dq*%%{ZH^oqMdd|Qvk$tE-{m6$hy-IPX+$uz=o1k ze4qH=3*v0_RYGtKha&~0`DU0%;Ifdr$ye{>Y+9v-(fqc3r_Meo{&t8L!j=gZz!+szLM8kM&H1#bC?v3byKdK5$gta4c;MN}kylkD*HsZ2-A?k` z-VY5|@K?R8{E}30AAR(W&>p;gdOfCLL(K-&fK3ABug=9VcRT-&XJpTzA28&A#QOLo zT<&t8;4EaJvq})ck`ydVh2;U<_mp%<+w6nP%$a0d2LWo7S>C<#YlK$2pMwdiO>NBZ zQkJwIWP%B3?~pV%;|RXuB`Gqqy(6;qKuYTbQ&)W#VV148NM0eYn2ltY2Nqw+^7%fM zb!aF-XW01>x2iK{z$nEjZJS!}^k#3GLml;i{=&cv%c!n<npy~%#vhx@-Jnc{Vu1sJ5kL2RU#_99()NRDKI#BChpEY@SV{@elUq9d}W zVwo)j5vQv)e1LTXgq07?Hhuu<369_UP}%CKbiaHPopXa{FY@Lm zVH9+}c~Hcn2+w1+!EE z3gCz{&8lo+DI#9M#!$GW@zPt*-icm{lH-XDn{5mqJG=gjj}b9*c=f5V;q^q4>ocS? ze;vaczDQ@^8v8+qcGf*Tt&?_H{DR0Lv$S32r}p3oODgb(3c_*4E~T|yn_SqvSbxdM zpZc41ITW;Pc?-tIWa7Xq(&`jWaQcVALzj|QNvGugAvKj8)%jv+zE9*md;Ge}ipaoY zd}eX$(@8m=FYBM1&ido{(LVL69`KQM9~%Y_-@028JyQbhY1}?wjN}zR8{a_wo2IyQ zng+=c%-;^K_$gz5P>n?glN1c)=Hj}xRxnpmoF}Rg&qi0 zWj;*|ITsgJN3KLnI2RX}ZP(t-rFEyr4L>h0p&2^Wed4+MHTWm>w{!QAkZG|G1iB6H zwq`{<9sRCFN~a!44%1Nv9BhI`dJq1-Pnlsc@lUQ^Aha4dVq69*;mv~&{QOK&Q_ogI zzjpZ$5X%U6k*!Uakv7)FQ8qf(@(n~PVGC2_@v@2QIPc?`zv>Q-_XpqFD^LAeO6}uu zeVo^+hR(dn^%*KOdi*TnmZjuJWz&cz>>Vi}7;W{+R}Ml7A2*?aGF2S@$dQ8>Nup-8 zqZ5*D#%+W{P-JC6ELt(~`{XdWazZlVC~_NJEQ6)W0|3q4Ex=_ATUsXXW-CyYX6ZGNB4 zygtwIxpE#vZVU8%Px^)2sFo8pPYM!`x*ii4I;5f6*p3>Cclj8UQUjQAM zyvKsh=Zyfyk9{hy|Ko8*To!x)fLTu1zYxO0NHXxQpgkx`FJT4TbfV#IOdR;*xYVkq z&VwZsjQ3WPKfn<}Rvt0UcYSrb%NL6QKUsEY@qI#>6bnG>&i4tAAX4_A-kvNkR}2u_ z>gRNEuYu<>MfFynnx?nc+bJC~JdAkQ-`WvzNl1jB^H8$r$BVH%W3@8FKB}l1mF}v_ zm!6rpBvBCg#@?-6Qv8-H6pbEz9{@}g9Um#DCyO?T#zTHP#c3XLkye|9KNRf8FKAut z(=&;s0?#G|FU;VczL9!CR-eWOvYh6cQ5y_Ap+0i#O;s@c^MeXTHQ9j4HJYfROoM6_ z)Y1Uw?a80~3TwO__K%pozq3GtiJ@^%&_6q9u{F8y%Gz9z$Ufzk*+IcbOI;IZSSnn( zzZlVvOCP8dke21}2@RW-m4MK5HC8Bg- z5QC{>o^OXxREb|IQ+qp~8O~2uB7C!0&u=vuEA;Hp&H!No*2Q+J?1?vlR?JGdQ5!My zJNB+4p%VE75k#U&sw{2NUQFcbV4xQZ3;~jl{rn;L!bwoj;;0`N?;DebkbwzrSb?~+5EgI;XF&G@Tq>nZ-)n*uszLs3x4~RHng`LI7Lgvq`R?( zVYOdB%TUja{XSCXvp&HNxoX{|EFqqq<{onY>b6e(O;blSYe4v%9|DD=Q;Jn zSW%M@DydmbGak&Iy~GTo-PF6y0}Y%Xk;(Gw-qnQJX%1Oh=)1Kh8>44M_0Ru+W#%oh z7UbpGrh5<~uN*^-p*c#f)L5S8$sqDQr0_?7fKX7PJ=SYlpnM zJ5Nmq)?i5*(34^-YPv@>T)Xk}bUaURr4NJehe`>KqX1T+R}%;B7<{l_rFpzJ{f@WY z7LCT7EfEQ-B*GLSj~4XCVocMuPQjavBYVlZf_bZvU=gw)X}^QkoK@5$JO_F_Iq1`Gow(wph2XG)P_qBz|Hjh!M3bQH+T0%{Q&v(Qo98B#GDnmJ76k zx|4|VR?%z+l>)=329^b{SM!cn62&w+ya`LsDglN8BqmFrPBc(|DyIbLRr~YY6eCpz zl$j$4{p^|EuZhMbsXuOHo z^LRglK?R{hc`}7tQvo5W7{tWd=>|KYjRs02BPY@S-fykaT+%PBkj@C5hP#agVWb&q zWW~2@(Lu))On!jxjBiib4{ck=)+rOkwuQ}24 z3EVA2!#yZ)P9DUw*?G)`wn3wN!1u@GVbyzofgmwyAqruEZ?Vw%k2Z8gDm?lQ?@%}X zjG@QNBEE554J{DNeh9xZ6z2Ma`7PHt4{E2M7|A5**q?9W1CnQvL}CQ2qYN4=9*=LB z@1_eG@x7$dw4Oz*Yqz9hFj?@Wh9bCLOcCV!LH6yGT&l^RT*9$Gv)8L3gZ<<{zF7?2dRw*lLF&}f*zm>+sbo7s+27nDis*ef19cM3$Zopo z1X*v5{>iiGv~afRIiW=<=QIU;vIQN^2+Eomhp4IhIiE~=_1AGRW7gHNg#aI|dP;aw zPNL(|r!~ZW@=-@kWrL@WPy~4y(*_s&gN=c98!JB`^J@O%(94jN2a9} zIv;115RQ=1NhYlM6^$56TgSlnq8E~9OZ|tunhMbe$Z*~JmT43ct*u_Y*X)d%dgz4& zoN`};DfCUYl7%i-p+*h`*2;DMBuFD) zzQ#T=P2YR#bo;vTJ+*8P`(SeOix2P)$MLf=j{FEng{#;3r^elEvwkSn=1Ri9FivtY z(!BSZ`>9ny*k6&S>*N?Z{_g!wk>bmE1LLSg`I|vRo2JH$A4O3ga=g z*==&>Ne~t0mH_V~$Nri2!C={FH5yY(ZF1d($-0Jy;Ft{7HI5JG4FYm_cEsuB90QSf zf#21reN8Hyw@YiM`6$wNDb;DpqQy5(H7Kvgbc5{1)CnzzB<~)bMgDPHRo&wf_5x6q z+VV|)h7Ql23a3++OijGq`uBRR?N(UGuq)==Gcq|F*ZwhJt&@+GGg|+Xgm;&nr zBOg;;AZI6W_c!FQQc)pJD2iKUBFk@091lBTY0rtzX6D}fu4dE}p>y51gXMPAMz41! zSF!TGQ(#mg=+bQMKmBKdN{G;nO=TlOhJ5;Kvg&w}78dg_4CKbyDA(j?uNH}_ec4Q|*ma36QwVCP(z8CAc}AUG%)P6Q zVo{;jo&H~e@aW%cb?_bts?SrM zGzh+>=)O6DnVA8Hi%GCrNx*P~+@^jJDmZbPwtx3YZf%Jl-^Xz+q3QVwje*!lWK~ag zY)fuaw_6n4q5MgLu&Uh#iTQLndOv(M@dsa_{~M8SImLGZj|^*5fbZYb23J%R2x!V3 zgjjk9>)FusIB>&+wE38N#cu6*$g-E<=NjId!4l}9D&ZhKBK+$*?YHWEUe6A+9pe~huG zoVY#iw|GqJ$QCwwv@+};Ld|`odFWFf|>}&9O)tc zntXJ+{(F#J6eEI>%u3GHop-L65{nmVSnpCkRsVZDn1gUwJvxv+UuI%m#PdXbg5p5G zAzy1`-dr?y`el9)4M!q68zPul$$nc`3q8pqpqj+iP?bivO98u_XnmgW?`KPm^-7LW z_rs`k!;vYDLeYccaj3|(sYzS9LZV^%M^c$}-vO7iN!EUO@syP|-w)p@I##9@edPva zEL6$=jz6P?IXMwh)$Vk~n>uP^$yCe-tK2JP#F}oInL8iVN<&wWn^no3^jP1Nmk4oHYusr6}e5dcPLcBx~#@nb_EX;KtZ zk!9-@Z_JcSI$R3wa@-)Trea7)%|_b%V#nFGTdP(%rp7tqnEYIY16^J=ci$mjY*d2B z#rP`=$qkzc*WRfPyQ2`ZljIjEX zC~5MBHjI4*7cW^DPo=i_+VNMlA~mussINyF6Qk3ERJEAB{GJIBYJrP-lp)!z*|iz? zFMc-|n4sZib`0YTd^?*~!zJWz5y{AzJPGb1nbi%~N-Ja{!&b508J)oqKlG0a(*9C( zIs8i^5`tKq8tTX}v0yU;x{9(hS?O-RZ+sWy zQuu24h@&T}H7X}OlR4KYnJ=#28zNU}Z@z;fVRL#9f|T$dRBzZ=>|*m0O!xJ7DU-TF zS#LbBNG50d!8)Jxh+|__3+IAgf7JKU@%7q_d0~XdAbj34Y$pOb=%9uoiNMUk`W9C& z8GAO9bbGcwDJFd6?-edPwmgz{*a@|AzFEQ~`HUrB+xu0NA$0|YmCPe%OVg?9thq4L z-=`x^D6#y=hr8|}5z6hnmY>e0N9Uj0N?3tIExkN@nP zZXS^G+;wFBVQkYQ*qg4T31p;XvuQdmZ#j^E-||H6RKWL@gpmJ8Mqj&h*KvQoob{KL z&Cd8)xEAeIhss7XaPBsZz z-{1nT*J+JUoFp)OF?X+C!2flX71U;~$55=97ne)w^mv6P6muH$xcDq(i1TwH1Dko* zD}f!qRrF6_4fozZDwePQ{CcsXu^)@Zx&H(cGk|` zVM1TF2Z4)#6k!{F67YG-4wy4L7s)?Hch0$v8%`;HWE0!KHQxVTd)Y{Z3+0A`2)ouu zsI{1wr5KTgpz24@Tp`+Kjy#GYi!dwUp-FUOY_Yw)W+aKwNjI~#%mK(@HT8W_3)}j0 zM%HJ(VF*i}Q(q_DIU@03Jg{rd|P zVlsC!{x42aJ0%X8{2AzEAAC$Tn&s;n3VXSOj$n4B83b)Z`^HusiF+i$oBG9eVa0t@ zExU|qc7(nN%g_zvv9KJg)=PSA(tP=nmahFF(*lMQ zXMNTxyi%WNVGR%oVt$?)Z<18wySv`QlApjzv_Z)Er1{mmoQr`(m-KEIK7`DA71EUI zu)`ikqd+DwkCKPo5Lc!<9SJ?`mf}qMwF0kQAC~Nz-xzEH2X9YEve0iC%`9fxoFV8a zv>ik$R7&9{u35lPaL!-DX}jT`;i9HBlC+RnA3?LSP{W)oLTlMKyRKOgHW#P|N8u#I zj(drCk6x-CyUDD4v7Yfutu0o8MGqqr<-lCII7WJj9eu6(I(NBKG0vIqp9662T=Cd^ zpGA=l|Ay*M`4!Vn-P0baa|XQ=lFs_08u9S?+br(rc-<98^s}E3+UHoEQOn!Tm8gM$ zDe$cu^9M`XX(mYhjj9g!ihjef;096wVvt^7k!G9Q&}5CZ{CFl&lSHDLZh5j^pyID> z`P)G~Sj9Uo#v{}W9&;p)=T@hhpujYYJ9bos$FdF?#_9Yex6?cvRD741 zg8*$KCR;W0QOqBf>8lKnp^hUbs@TTt2i}CsJ7y4;;>&NISZZ!o1uP#;n&HmxQ53$; z=B4ypTS9HHzh~o4SAe-6rrkIwt4@*!Ctq1` zG1q7kVZ-gT^+N{Pi03aFrV>f-&VrM^Vsng_-770famWg_lB0DD`?CB3sBeahfr!xn z%LKJW@xl#V9Mx+HZ=CFKDt^+LT-~GIg>iwUV%|GBbd+(wSl2EAlLYqN4H8ELQGxXl z8phS0l?`|tS$IPm%9c>a34LjB%-oIec7Nn%2A5@D2iV|>%>=#F$}o|rWip+o?^-cQ zkhk}fpfItUddsq<0cV@@#8JhOtuONOPN8JHhNpOnCsy9Z&46|~c>L{}s#+mM4HL;i zDP)yIY6L-ZVg5&Y|KcZ7{tXF?G@@3Uhh%B=_vBjdl9z{eDu@g*4*C!37{QE~*$Gd$cRnw|{kZ67_IwLcwL~_TWy94V5$xk-#QbPV*aG?@`I@Eyd z&0vCO5mYgTGL*+sxMt3W65fcEi5 zLM36F&PfQ+&tE=cQ)NT1J8r$PO9D28-PM;{x!S;3Pe4Yd%j2R%15$V5al4TXIegUU zVCihS{6;|JOf>dv5XNKj7dS3f#|EJ=(XGU1w{xhF08mzw#{RK#$S~_vv~5jLMpaN? z7XheiEGGOW+@S*g#hU0fh*#qpL8lXuh;A!J&_oo2C&Hw?|J9PK+W=)FzMez<7;+Q2 zov3gso|*pPX5lY#?}Cc$S9G2!tM5ryYfgt7`oZax8Q>J9m9AoxL3m}p!hrm6LKX_c zc5hOSNV6Ace%5(t965~aSD}Bscf@_1%g@vX@B7`$W}bBop9*V@4V!xH-oV))yyg1Ph$ zQa2H^0z*x~6C)0g&y3K$wJHod;_chQP!6o53Zsh;tqpZ9$=bWSj>K$F{ZWHyLw3~vb(*z(Thd=<5bF`t{>|X7LuL9Jg*bD+vf) zmE4$*f_D-->HFnZu0*f6^>CQ2t_Y-AXr}7VuLxx=8pwDT9F1=aZQdv?&jlW(utHZx zrBH(q`8VxZt!p84*xh04;gP;K8a>C&SD#-%=kST2T`q3?!K?%Jt=@5lVJzFG#CaWA z&m2MD*~(-newc5U@pOW+<}JgV_9tNg4RCQIM=($X2V+qbtl6cuLDjof|2&jPtFPh;fDklu6)6 zsHQZ8--G8=>=$FsktOPxOcS);fh*ArsL{W^ph>Gcffa|JJCm!B;}k$ISv#f$%WF-`^(>i z#au_0HMu^2tPJF1<#7^0%1dU&U@&iemouS~mMsx^Zs$|7J&u(0&!phP!7Ptp^8(w7 zucbO~mwZIC>*KOR2VSDY=w*xZ8vamc-ltebo}75M5(e1xr8ZR@mGJuxr0t=&G~-5$ z+bbS^Syta-I3fq$k1&dKAw@e<;<67JeUecXENwJa8Q!(p%^$J1F(SepbC;PTR58`- zq?osQNH_opr^X_h6$}IwD3Hk(ET@}%ovv;jQL%JBp_rpnCeh)i*hwsf9!qH$66)9q z5HK&JB)yVnE_D|27>t@;^gK|3lY~clMp}0zuzai#QXMB2;2N(pr6T@PV88YkS6X&S zCPHV4aRFjk17J!P8O7;0FK3kF7&AYp&ut) zH5K8hSbC^GcryWvB>HSPJ2P5PCh;&=UPbjpC6eOrfhBNKj;hE4|JDL>o;@Bc+|2mh z3=^fGzL@}*bN|7N5i^V9zzWoM)Bc-)qR+K-P&OZzYc#IFhndscDraekr)^_sLjm%8 zkK@em1k^^`c6Fy>Pxyev*a@A-p>gGTg<3@Jg@ZHE=0Tr%OUECReyf_qJPQUElG{H^ zgtGGSGEE{N*hv$`=GQ0=q00EEnvz0`0M=sgC4D(dh6(lP4{Z!{4n`nnIgg^cR)Ns= z@%pPiNt!UTkbuyYos(sS1KdHvf+b&zFrEu1+sR@l$VWYdo034Y;2eyG7#P; z0Be2@X>YgB>F6&)vI-hDg|sDBpoL)ou*op}ZK_Qa3#k@RI$p;;OQ}IR{uBe+)~(2< zwNfCDl2B&q=!i_U3aV?a4PA4bfLdxR`AV6GK1*@XiZ4Z@whQ;!47nc|dWTLtMnOCt zYe?eNrt0cgLtcAyN$s7b_JI6Hjf7@jt6ru^?>#UV1n6_&AkGa9tyJZ7 z4NaAmdB3m_p^w08xQK8Xdz0zKyN&Hirr*^!bHf{QnWk%O)kvxgs9M>{;3cj3+u@(= z^S)6Zz0dAx{yJ0;n=oOHXF#dbN*UZ<{EO`O3_BWhq&{XV_3N*7=pG6RvO?ovFK1&w+BRK=CJo_E{R5PV{p7T zbkSCmN8Q|4zZ`Al;hZ!w`7_LgNFagSnH82rCuolb9Y$2SB_v|-yXL%52?B@5l7kf!&e@m(Qq*U&-<2DK*YKSX zYfM-2`rAul@IPn5OY;`sm0?UeN;{BD1gT^>S{%T3&D$YT&)+Oz0Ch9-yRfTdJ9hh} z8bR9ow=zYv__zu; z0nWr>v|{}+K8qWFcgXDrT(w)raC!JCRjchaI1X83C4FO;_VqFwF$syUk>1eyY>)mO zV}X?6thudrZ=MYA=RekOWx*k>0R^*%@O<^;9mKr0t~T2#kHcqgc_zT=%1c5}T1X<5 zsFRL5_-(mKM??XRnt-V|uwKB?<;0KExcC%0__LyC<=t;ajRt8YPgwd^TO1{~NrEZZ zdG3Z+j8-zxuY@@mX)e`t{4vk`EMTl3q-b)Gblh?;_ak(8)v}_Fq%lw(m=tvXHH}1<^tCU@afWeu5Y)>H*!?{~R^7i$>A8c?$?LK-2vlsF}>i8{I#N&5lo$-#BLMd76^mnrlYgi4^7O>e+$@ie;ZG0!w z?NV&dZ#NZ7whr-)WnW3+M#{wcqFXZj-k5DiSiX=+eVw=K%NYSqoE{z()gwIZc+Fw> zjr<~N>3rq%54o%?Swt%vhrfj(oJ>uuJpE(O_&kp8x8J<}!`%CK4jwL5cN*#}hDAJU zkI~L*Vga=ej!`BeL|=?0`z~4cleV;S_2C-h8iLo0_m5hr+q~3&jr3wx58p7 z@i%5e8P2;NPli>)aP{KZ&(Q>4H8zz*{0#%bGvsN!Z;7&(!rGBp~p~#XyV1r?qm@NCPqiJDs3! zA7Wcc-Vn8|&;)a~^Xf|Mv!NKA6I~rTLDydA_JeC}$(3koQi4=+1S=PH?knj~@Q72p zZ&>3OA=^nsUF`Osw1glbk?J3y4?EaOfmhj_r4aV$w0$CMbM`8AVCyvMV4z0+p6&?F=vcKU)kavjTQ3|WK*itFV7ZM9y)W#R6-*SrXKTImR|NRus>mXWa zm{-$Me+F_q{zqd+_iy7aNjqG72z@9<^*CX!nb}*n3gdoIX4c^$#)upceEKHcNj+^)+&!ZnV6oN;ekmljw0cwKQAlbZ{vDt_V3 zlq@~z4|K`HR*8uWLTT$t#eg32BJ(f;@IpYX9h+f_aXSbT*+qR#{mn*PuDM8xrmyVL z6s9=4Z#*fND+X3-#16^0IPSb;O!9&wyo?`aAJK8re+d4-%1sU6MKAhxCguFfdM{kE zr4#tVKA zoxzBUB#RP7($w4xkHC|2x8N}|56;)^CbL_>VKX<7<|AQG$0+0#Y$gD4d`@A_j#1N` zUPiTyN`b9ZqK%7VQ;wTOw=|%;32{bqzY}Qa{eJCTaN=It6Vsy+=fbYnQsL4)?qupV`wje~dBkq}~Xb&Zjct*IHxu4ot|= zXo*Dpn56mr^H_t|$)!K_F#pDE$oi;jaE)O#KQ;mRSFYJywzw0k*q=ekGu!5+VtnJF z-<)F?LlZup*IRWEii=b4*aEKXM&`EWL*%i<3#0To?5lkAq9aC7TkFvCi=wS&g_{PNOW`(VmPpl)Ol(qOWh zoZOGdg;LLX>tb-fnp1B^O#-h=c>@7Y+sZw;-(NNp<^SDY@qbbWkR$)ag~e+$6@vlk za1Uu6ZVlWInKyEfi&1Bh`qzJiYx|GV!iryACfAs3{aMaaepa+a?Aefqy3+;c?0pX( zqXTkg2MMl#PPO}PK>V>deF!JpHyo}i$YrW)7e;#*&J~j=UH1DRW)6~d(XU&-sDwA~ zw*bnJnv+- zKFf`P`fVfa+awYF@Bsd8asb}CbaznE#no>-mxOVi`g?Wq2GjiA%30a~)s^rkNk7&q zO9vIdwoZS5t3C$uU6q}eU1&PjY^LFD92*x8Qzv8*b#T;MpVa|29XXST)jODYdU}1~ zRV0MrIBnpjyVgT$0v)($-kOS)9RSgs@8K1Qw+3YE6HqeSOS({;Bz~0jw;wXXd3t)Y z=0&e}r!T708~?5Ge|LedOT8ZB zqyAYXQUQtlW6xk0$8l9Q)EbKRhTXhHpR#uZfpQ2T1;Ia16Pq*UDlotlhCl*BXtfN5 zgya

3s~@flweDA35(#PV^FB#0ww8-D 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

qC|Je@A{CPaWgAH#&us)?C)p`xW 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Ɛ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 H9i#sbzl81d literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear7.png b/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear7.png new file mode 100644 index 0000000000000000000000000000000000000000..417af6c61166c727b9ab50cdd14a498c68744e0d GIT binary patch literal 77056 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 NziqdsJ&+yE=yzftZ#5;QXZo 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 S-AwW`#1E{fu22Os|L{NMEB7e? literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear8.png b/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear8.png new file mode 100644 index 0000000000000000000000000000000000000000..de64264914cc8330093550eab1f0ffaa01e91be9 GIT binary patch literal 71177 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%0Dr63wml-6F*tONlh7MSGocvsq)+w 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 L&GqVZUE=-^HyYM< literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonfail0.png b/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonfail0.png new file mode 100644 index 0000000000000000000000000000000000000000..a25325815275dfe5e34358f3509f93fa273ba40f GIT binary patch literal 67970 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 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonfail1.png b/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonfail1.png new file mode 100644 index 0000000000000000000000000000000000000000..2dcaeb92e3bf4650be65948ebacd9a7ae8bc24ed GIT binary patch literal 69118 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

  • ATKFj*pA9VSx9n&4rRut6B zXy}#IP&Ze5)a}~?UU`$ZM6+REJPmt>c~jbon_vi3`?=J@+h1*l%4#u{eu#MziSK57 zir~0qLZ*ZvE}k-20!{fPqOsT_7YL%pP65mhq~%Ivc}S9wlZdEPvynV#?NA;j_7++G zCb#95m_$f^v{tw+TP>DW4BxkzIAkO~sq}!{S?nAJkC>LhfO%ulm~mxtTdi1s60i42 znLJ`d*SynyN&@o8;aV53ea_%<%4B%*t0jom{3dPFb@k=M<6X$)A7S^pigPmpFqt1(>M{bT+oc=FKu7p&XnLc+l+-qXO4jsF2s71_6M`oj^1r@#ARFlmS{gNnza_>$y)%5ublCU%x6%{mjPNd=2Sd7!hf-Am-kv5B zq1bHaN+I)(#))q#Awhd|!U$=h;XU{Cj56 zFt$O$pxcV>Dw#BR3sOZKGSZVkNCoU{GPz}8M7pe)u4^KX)tgSCTiXgLk_T&BZCZQ~ z^VWSTzMPhrfEc7*`pgtBhQXQ=4*#;+G?A!)XJd?+I}mOr8z}AY6Nad}yAQgKsD?pr zEQ3ms7x$u$LigCdYhpZls>R@zQ-u|_&3HtpN6NgYKJqK$o*f}Ne#e>xaK?4AIqA>a zr6>B=Ux1;XPenj7^L?_Be#qhChKhw-825y9i#dh|*PVA=n82Fl(YxLUP`f3_+N4I> zUbMtB)OkE*S4YZ_iG;$PLY~5&RsvmRDK$B+A;&aGM3ye9G?PVANG}3m=dzP&^3jLD z&ZnBsS(nUkFp6$gFnYp!(CZDsE((vOH_pQWV+k3ZX8I6>&YP66oA-F;wEgwKwj3y%ta7&2BPhfl65o9V^bpsLyAjNQZ<)^Rkh# zV8TGH{$s?JAuB~8WZS_t-%8ugic%e#p=Ig!;NxvdwxDWB<(Km{=sls9N)v?-S$Znt zPJ{HiX#Ng6nSmjBq9D_CnTlbOa8su4!RwvMSViT}LiHHZV>IG*@><9W)G9C(xnJ*U z+)o*ocI<>gV@N+O?%N1W+E=%VxKaaVkd-W&1H;Bt!^&-I(6VVOL{djc1MWYK$2+}$ zz`*{U@i>-M7YuMwzJeYtr^Bs!>dK)c2i3(}*m-@e=xtit&VyX)c6jH56{y;;15}CE zE6#>fAeT=N@_9>@IwV|pb^x(4jOjWr>I{ApC4D=O z{1;+gh`(K@&d5P}b1*#I6sTP_2xDje559(?xi%v_QjY~+|ASUlUdQ@fADY%Qp#_%5 z?M?h|{1SM!p=Z$k_>rZnEWYMQrssx*l-Pcm&$06Zwgv{H5J+dj>)`={N)gC$E-2#Wjsh8 z^-GOL>kmJMhvKDsb~7bP)o%lLrFqSr!`JpWZ7^Mif8JbZP>R*QO&TTz6)g}>E|jeh z#zI~?E5wmc9XnEvdyp*T)wbwAG|e*ianFavn`a@XA2}vv+`JQs58s|(o>!sBjZW{* zfZp9?Vp@&5_3KD8Pl;nU7okss;W%=F6F8jWpRi@k#eL*Ip)-9v-fa1=bXK!xIl zI&DOh_6Y7AvZ$2pq-Zd7EONLr=f?!fH?H2sWFQ7f7z=sntPn>N2s@M=OO=@U({x>N z^zgWZ&p%&@>c+LlQ$kpT7L)pYhexr8rG2h*+_2v13=W z3tsgHLNv}($EbR~H_`B|F;HidN2US=PPP85T5c$#&0zZ9dpAkj&J4QWT6CCz96qLb zL{xxfmpm(J*gGRDQ@~LK!VaW4#|~x3%C0L$xJ~5IzwJB>7|OXhDTA)7t*eh?$7%C? zj{w(JDE9VH<6i1ebXVBmVR zYtR`x&&)Kfq9{RG6%C6_!RucxLqLC)4RbIw8vJo=i@2%G5X^W4y46PG@sr`6O&*yN zIoqJWnusR5vE*1tHi4uO{9enfEBXpK)0|`? zxvt9RZH4#e^n{z6b;YLcK8(hv6Q`IoaGjzk+6@raL!xD9Q-l2P_C}j>pNQ`?_-1wb z2l#&7I~dV!0q#9yigo(o;geW=JNs{RsXqwIw~kCuQCO!ahKl~9G2nwAQEco0=sj$A z+ocAN7jHwPX1j4OW1&>!Lg)A9!^e=VoQ+Hf5`WP`EIMI8obEL4?Ky7Tb6(K2XgBaH zVTG8IH7Lud98++}g%)3`ScoGCM7BOG1LyQnnF=JjtNrl7?9Wl6i0MWo3(j@;xc4kP zi#Pa689Z%VXdo07?K5MX2pZl+2<^{AkFcr=5sg32Oh>Q!<8b+!J)iU#B(6}4fqk&KOGBgzVs^ssE8E(aDJOOmyA!hq}#4#{SS~g zkV7gPnHd$Dvb2do#xf=R_@SNBwzH#fu_`F)&n#XO$SbU(!5FVx?wxWjn?OjOsQPRIHYIN-NQdF_c>If`V7AmDV_IW<+0yHcw;pE`hYd?Gje7#MT zE=U#(!ZMi7gS0~Aa_<=h3NOlAo&%M6%f@4T-!24aZvx>-@g&K@G%x5IWapBdCF>)1 zj!KxkbP(LFtQQg+2aNAG%{|?pUlLEDJc7v;fGmiA$9;i~G7L;!0Nl#Zoe!&}! z-nLF9Y{Gi||Q^Z{8Q=t#W@xPsaSpb4oNXvIVhmW^uz7>Txg%TBJ%amS6Ss^lM z@HZH9;qQ?cXaVsYXbn;J(!`_NLVjgqJepo1`hn>oBEe8zlX&=jBonKN#GVAgA7w}K zM0ip>K_*U{0_Vj78sMYZy|`%=Lc?|VV&Y7(WpL@Zs+ywwX#2aaED^i;Z~Qvu8>~Aq z8TuIWfOLD$pLABGCT_V@6r0O^982@ zMSYr~Ol|A3^|Fz9P^?ZS=aB?R81(d%6pgU~o(C%jbIu)O&()2uRgK3gipEk>oK`_{VG;nphZYLuw``T9 zc<%Nh5dM&CeaII@mTNAWL>_KxPmKHM1LW|vE^~-!yI+5}3HPH98urC?wXOFC^r@}? zkpkj(oyDK;&Bf|HAK^*tXHdkmw7vmqWpPX#y%az0nVRCNh_+(Dv|&&va*7VrK^OWF zR&U=1%^}CFAZRV_EZZWi4<`t0Dtyr9tqdSVwd#pU}{NQ-3 zUAt%E+376wMcHTR#|(cTd4O=BA*8eJ+K zMPtzaRncmS#v4Gwp|a2Klh2>O!{1BRwPjBWgg?ekBa6}cAj{FuCZSq^Zus8_b2;af zaOYtxeppA>jv=sC*rOeCbSEF#8K-aH$oR!r`OAB_6Y~WW8m`S^fJ%`MuT>d|zt0@N zu=kq?5;VSHN4#3q8*OS1kt`4ew9sqjVeL<|pg3n(y)7f4(CD!53g>;WvUJ9xN`cPE z703`;HnJL6L7<2`m0$yu`n$M!l&i*O4pi*eT_}|F2$?`v5(PUwFx;lLoK`oMrE*TT zqTh@b1Brt&mevKyfSEs|MSDlIA)@sWjmK$q&>G=qxlJKVLU~IxmPaP3kZ?#o47QR; z?VfBeRLL}|IWmask)h0vrQ zuh)K{i{{oBTd;ij3|xqqD1_!|<2&L#MK07YIS_xHIE-a~e1N<`N~wO8BfvhHzi}jL z75<;`2(SMato%-lgRACZkLigAD>flUd%(Dtad1)OL94f?827S~6;YvZH2|qVR==S< znPu#Si_W=HVNzffgmSqWLhWkOEGc9uh$-Y{Ht3b-vH5A9^f_6_`Upo{tkc7k_ex>4tt12eko5_p7+5Z z4ox{;w1kFb`l4o?1WcVQ&Ylm(&Xe*Y{DL}jbQ2@ZekrWcs}EuICyTJ_?#Ix_Y!nw@ zuyPa%KUB}(2Y>F_gLQ{KMd5<(4kHdrc!-`_x%V>^&(Yd=1c=ui!gOQHVW8MfX**XGKKC6$Oz4M@Q zWjA=cn+aP$CKM80JJI;P{C>K%7$2cai{zE0C8E$X*pLVc+ZM$j!?L63wj!I+2RBSK zDOlm_ogcdo{Ee!W48h`*!1c}hHkyq6mko=}=vB2baMS{5i;|bp2G#ZZ*!Rs|{1?9# ziuk{cM+py<3uuX{i{8hp^^2qnX(WZa_oLCGdQUuwJ8C>4I#E>@-M?4?y%)!b;}N<2 z5Z3+LTa1#7P2Kf&DTz^E?az*VBmq+Uh0U9B^2}B|i3}IYM!c|^Wh>oD>=0T8{Y0a1r;s+0hOeg^`p^9urP{TDS5Ag7l7>De z60Wy)Amsiw9NM@K`_~8K?vn)1I?t0&9es&^ipGE}*Jr0D%cXW%-^t|LPAh@P3`urm z765&1OxvY7C0$&Dk^}MQxrJ7jE8kx>wCG;;_WO|2{;=7QIaFJ0i%fDO$}kTV#&J&M_l#H+r3M zFXPau-C|U3#KkJJVTnlb9BrD?A6K8HT-cK7&rlRakZTo`39O3J`DON>W=SDTJOi`MTM_naU5m(0a4$38?%tW>Rm0+^(+STvT-_)j!WVq+?) zt?CeEC@mRTIUg^YsggNSty(?|*!U;B3Ybge+XB_CLwI<04(1Nqja&B;)c)gjaN(a{ zMPp!?>kpGeI2n!~l&&SMB9nhl{}@?PPTi#iB3+=jR{%y&9bq+j#E8?H{q7PIc|chm z6$V+q!eCNRh%fzj!fdSBKN?!i3W+=zu+HsP7t=mkfg{&ephHi`lgGm+T({h~lME_X z4#L7^-$1Q2yZj!;?!w^-H0m4+2JUai95C)>90JreQ8Al5UWlTVn+QvzoKW=g7)as; zU<$TEe;JSB)?xp>53q38+xT?ihuApfdtBdm2vLu@gm%V)b1I4lC9I}>a_<&sE!OIB zcGT=M5bkaX=7iAQB4;znZmiTu7%J$Lp0bQ(imvH~&-?l_4A_1o)#O2F3)Mwn>vj0( zuhWNcp;l`bKG_2kfG1|-0e$6}|qTDBp5v2~w;4o~S{2`vjZ!{heC#Ecn*E_wB_ougk zpRetj*!4T&f_=rvFlFzhjAtRhm0M4t6i-WyiYZT`d1qHgbU5@gN9guOCg+TEdCqzZ7=_G-{SAgM7`a!aENi<@Cpm1OH;#=Jw*w%dQj!8s`55jYm6PX|fj< zfATs`-8Ge4qV`Uo+#jNy5{)U9%ulw}FH8Z&xH9D{Go-!}O}a^y0s}DS@P6ouGGEDl zh`D?bL(7vlOyD`M5{+wU&=R3`PnVozQKd!imI;K+$crTFGAlrWo7xR;_UmUgd1y3( zQ9oE|A`d7lpu+GpOd5Rr4C^KiHIWCcj$*#8@#FgS_~N@xsUi>J(D5+5-snRd*niWi zS?O1AhGF-iXE^b%KfK(n>u&OQ??JUOYS5Qro3Ske_MDu9xD#}%+8?S%&u}L6kZ~{L zki(@C%4U;Cir`36it!h6{UQL(V!vu~u3ZM%82Qy(PH!N9LHBPSkDMAHLout&(z)vkc{6Iund|%sOFc z2K5Biou&}ya;c0HCi#`p zLLmHGIlYW(HKW|@Fv8;1fom*&$0gS95iWIND|PgMa_!w2*@R@m~MUX{oLa(Tr(s(MuR^OKE%|4t1x~* z4_w~A8O5xuK1|T<_s*o2=+fjviTot!gRybhCsGQsy>Vm3b|`cizcO6Idi~QhC|?FJ zOX=phtJpZ{3oQQeJsb-j1;o--oe-RS>+4b*CHy*~q-aHb+rrPas`NZqLZN>m#L~|= zA3hC>cl5>daRYE*?g|87Gt_2tG}LM}ye*cwB^3I*Sh90ALe4RM>-;FuxGB8U2@1qg z^J5w;g;;9cSY&b%27QgRU>K9&dKMCiEZVXe57$3`-_r`8T!EN#`-*W-0yB6xF_9Tx z@+3pajg?v#7C%7p;7DYCEpvx^h{oBF5`nz=+7x{M+eh3?5iG*_)%#m8dntqEdgxtB zqhJ4JP*h3i8?_e4ri{nghd)Z&T=KPl$8Ry_%ZA7mASH&S8M_ZX#F$>Aa6kGShV-3> zwuhFJFC%DFc>b@A(ag*WEBpw}Kc^j95Hye)t<^21h z=BHm6_w0jpBL^c&v(>nlG4N0o$Jnn9W)sK@pb<~q-3wQ-Z}qph8uf<|vd=FstWe}d zUiV6VV4tx!Ti*a0vXG%O{eAG311wx}$ID7CW4z6E?iw_SXR*ccakS4aO>!O5f zV>GT`3;9}96TfGC)6QT@&+3Vv;A#Zp&I#eFtc#&TzD7U^&TVsUY@Iy;f1djiTAh@n zL?O>I8gzlMN+jJ_jDp(Y#**bMNr*8B&bp+m<-8BZMI>4K{ozA!eZ~F-R}an<3d;MR z8~3CPtU01FeU)w^Y8MfyB@nU?Cz#OcAnh`6PU%mkg}+ZxT)1`~xpG?PCVD1Ltzo%N zcoh4Wv|rGD6q?Q)CvB$&KfYDP6g$YJu7%xaRw8!*PuD)sYIRuh!yQa|zk_(t;xMr1 z9CRI85pHfuteCb3tG0fFXzfL~C<>xW<3X6ZY%p@9fxC{MevHn|yO`X2tjpE0?PB=1 zGIYvZ_(7RMbewXHG z-M9(zHLLCrg5NWD8TQ@$(72ZnZp!KyH0pZ<=5y|f1dkuw#;nh4B0R!G>|6t4zGxJ> zOoye=wIv&if|#6|E4{h<&B-YY6nWKF09t>Z5oVYnYe8;9kAjoW!1b6!GB< zegk-?EM|(@1uB&iojV1fW8?S50~d_%*S>|hW4ED4qv7~|^Sg-A-ADC;{qghey_mH+ zZR9~zt&j_&-kvP3#qdR3+-c|bIa0|Hd*ImapEI01Torjxt5@37wX+%?KYM`HQ-|TZ zSQkf~(44`&m|0k~V>rJ1@Dp6$aa6K~?1_R!%ShLh6k>J9vE=K4 zco_QBsf#W8=Pd|7H#%WV-~-F-RLV)Ywv;VbK=DO$hJJEd0QW_^DjEg2dEp>|0adHx z(vNHG8WZ7sl1imbu0@_ViA#>@NY|p8G-;A}O^C^pCmXkt1iy%Pn!YgEtm-D3q~iN{ z1>)Ofi-hUSeoGnAF~Il%U*d73p$3(^O9xaP*^hw>X+Jf{U$iVv|HZisafsADM4+k) zUa4=tne2q8hZ_0I<;Gt>-^R0;)3|(h7a}yb;GrssPrh1*&%W-4BKg&JNe`w$Sgu+j zoZ23RTTcy>4CD3pkmIr+{9mO-n|dgNBkbEz0tUB4Fve`aZLU5LMRkUsMTJm+OQhh_i2an zWt-vl$!B;P#lXB_-k3tjut@C*oVxxy&hPmT{&Al0DOK2lYEq9Fy6ru2hCK6OyNF4Kms!?pXdxcJ!Mx}x8;Ucpy{$(**~;ZnR9ig+8` zI||Xy-z->&C&5VvyVHUy<#S`q?1>5HY;55omPnFDqE zZfu6?`aR=b>TpeHEed^tfLZ!O;fDtOSO+c}L}YX{Hct5jzZ`f^2qHO+TOllt`gmvf zPnfy+3$$rl8UemG#Kt*hc$X{Thdv!R55zeB zbSH51`?Sp(KXEPNQzeUfHG!{B>N4)K8gBi47V{MY#L`3PaZ`T&%9#Gu9CUp4|rWmFcE@euiu$OFw z=&NuoNHno4xf00J5fw+<^$x9>J`nwZ+oD@ z8zY5)GH5kV1MCVZy?0XjE#L7(Q{ByX&BA7;UZfiOqru4!a>7SVVVmpfJgx^5s6l-#pjJ*!_xLG5dO$IMU!N6 zZpkW)D3Bm-%`>fv{Gz@ zN&8B-X7FjtD)9D$i!i?nc*rg>Jyx$@iu(`kZ-o$fb9>{9H6u_ipsO_ZV`R6f;^rCO zv?C}-It{1=KUdaNGvLtun@~jCc3q0tIQ;#T_0cjG74yHEYIQ6tK%>`T`)3QW@A?cW zwjd+WqZC$e`yGW#neVgnK#8C{`7m|G$Cy2TGm81P5l@%-!B+^`_B4Jq7Hs$*e)-}H z#6?O)29pIX5Fc-I3?x?k)9B1HR-tsoXB$37o&1BjCmp*Tu?xRX>W`;V2-$g1s(Bl@ zx|tMdb_}OGP}!E_AXZAr7sAq=MG~PF$e0Mrrfe5&k!TFw^D`2W96x#yZ|5(LL4u?g zRxXR9!+PV<_O2xLCu5NG<0FnC(St# z3q@NY8neGlZmoEY4u74$gKrma#klr^&_$5Ofd0cVed!vU6l9VJ{5x73>qXln8dHcV z)Od`)t+Rzd_*s$#34|R%zn)3r2YFOQ*$T|+wGP|1oD%}wP}fPPD1?e-tY2`-7PwZe zfFhn;Xp7KeYi{EkFsBYb}rEyDKRoqEj{x8wfmIAJy z6PBpODvTKcA5~=p)ZQWcBX8;9zyTL87 ze(xHzt@x237Oq2NM!fD{tll#MYbH-Z=>3F~AT8mU+bloyx(H|@O>)l!mC6Itf1ZfK zUMv!8z{R_pv2*?y#HQ9MRhg@HmAdEeyIRHi$Muw4I{yJIOF4B+fsvIR+wUU$Fz& z5v(&Jb%Kk^1r4h-fQt>LZ~&`qmjx>D3lcyU|f2 zfr>Y$iObnoX^n$ef+Rv`IR!rh+;nL%tyQ*07-Zfn+Dy^rind%d#!G0ikW?6~c$W_Oe+j#55ct&ta^!%r&~Y=dp!9R!U{g{sGZQBC|!~+6`>8L|Y)*HqjXCU}`XlgMY{RN&FsaIx>0KoW{!w zmEBiz{KG`AK^|35zN&@xkOD6J7lLQ;=4_Ly#ko?WV^MeTTjJq0xJ&LwAI6%+M;&T~ zI0EzuFZ}KlhIe}dVHz_5RQ9U@mzvfSNa*2f(8p%%{11g&Jp|UOXxw{waAVKk`0Xmm z!(70ygl~I%v7XOxR)P}E2RFd7EnlH(0OOD*aUn#{W9+~B1y+7C4B_#H06QN=y}Q|M zrx0>5E(sTwC5jcxgAZnY4G)zeZI)Kfh7IFz|N3*j{B(y(XphCbDhWl>4mk*Hn3^(;OdzqI+B?fIKukK)7m_*!BNl`&5z1C z4SrL)&Q^Z+jZ{KDGk_Uu=dQ${eT}jB?0xD~k> z-+t5ycWz!uqPCVEnkdS_(1f{lV+CKCF)>3+H^8Z{RN!$}adF672ztQbtPVa+XmoDan}Z4LM-7P5p2E_3U2*GZ zf(dI9;v-Wg5Q?y7EefDVj~U{J3|3B*b|-!kmPvXqjFlLL8`p=68%tH0K-oz*mgRg9 zM}i%|p+{4WrXI0qgf)_f3@H-`1L*7o)09|G zbum<}Wql^io!fHYfc3Fl#nqu8O5UE*sIm-*ZQ32(+FE1xlAl5NL8Zsgp+z!LjkXXF;zZH0`VN0BTNNv?r?q3KUf0Xd)hc`difZYAN8jsqJTUYLj zfz8-j#zEmy6don5pEH!15gQYW)oZ7SJ~tE$<&5qA_1;2_*X1$z@rj#i+wHsdRZg2@%i!&F^>u$RV0?`&v|A}O^%gYlBfy==Mard4% zu8T_sTsUKEt8;5uu<5&i8wzi*38+7mZBkE220=oR>YN)Bf#juPe4??9Y z0FMGDFKCtp-6ubPHAPt52aHF6SMu~i->(K5_g-#%x@tD+7wa#^c+z4>=VH5ZUNBD} z-kUiCYNa9epwaEe`uQ^uXI`HpUEx==G~8TS3d;m0BFXEdQ>IF>)yi>O6S0o%N}h5s|TY5M}ni=W)-NKx*E;ouE`2CNAoQHvZj5tDv*m>t{dEU3KYN zF#0w93Zq7L#l5IKJZgATK&7mKR+VO;)9mlz)hz9G!W5Cw2yIdRoXnp7V7_@dKXV1xT?cm|%#c-_ZU_m}V%C&BX!B|h?728ckcm01mh)Ar z<{FLvO<99HLwZ1;hSeD@p}hSVdQ3r#fANMzQwg2=Iqb(0tIZD!WH3R zPUj`44e}wNHIklzbi2bE0^x|4Q+RpNykH5X@>hb|)%u%!XbWeboBjHEUP)lur7OzR zWElj*x!;RAfjhTeT%*UHpw)5g)MIq1JqlmW>xXcQdQ@BhE6A-E-gt90Do+|~_u`T< zw}PRIW4d?7BEZj}8D>>*PQ%a3W{P`l$ed$l(C|T15LD2#zH~McK#7U#-$FIN(W0-+ zf>!Tk5;Zo1b*SE)H3Hr$gXON!KfvlgXCw5MxpYi=A$PU1P)Z)dmMzJJy-QC<#yOC|RjQ-wZUc89wS@^svmmx@h__TUeXoKJDS$!ezp=N#A&gPqpI00%heV6UdB~76@Hf)@$NU1ZxO{7f1r3I?Mn% z2?cIyf3zP|pPN?W>Qzp3G{8-n4+>Sr5ii$W&+TTOi}3H+Gh%=lcN_q^$Og8aiA5X6 zC!EvaQX16*KSrM~m%^{NIk@i#JbrLZj8a1eTt>pXm}4q0I|CT3{dNC$qMr?xwo;K3 zAIxQi+HBWVO2}Qf07eX{47K-Sh0%N3Iz*TzmBy=qsM~~&ZIZM&o$%c z0TeE5t^oWX;trw>?rOV(gYV=3p5-;C;kJRGT9%ay{d?@M`X@*kysJEsKY8N*QoihY( zs=Cs?QvVRY-1;4%7Y%9E=>fGKC|#%=+}&t!m_Rd&GF5hArOn*RAlO16yn2(AE1a_& zygjYgsvbX!LU0^QtQZjBoALf&553uy5}~~OkX=(#}UCZFDF9(RNOsQOE2vjOJbevKS zRfaT&yF0~B+EKhnZuDt2OqxqY=;Jrxx7EonaAi-Z1c3xrE|EA6!t(Bvsk}(pjg{$; z9D#7u^P+fhyhxf`CdMdPvYarysOGYDPirYQJQQ?&BMvj8AuIn~L0}*p;}k#kN&E(*o~mMd9L_K<1In zEL~Vx&H^DlkTB#J2-#Ai46c7(?v(^Z!cxSUzo`A1C3H~rSBzWf57o^F_-V@z(8nw> z9uX#H;HxO?fawXG3zbuyaI&PYYZ1|#3C!7aL7Xn*ClR_R?4wmW8}3}bh;vVVHtume zwZv;ZGA_$7vx7l(d&1R4jgepUheBzX2d&Ut#M;9<5dDnBY8?+(VNH4Hxiq>7^ybLc z2jy(0G*go!5WXi{AS4iz&&k!r9rfCAg+}XfbH@y`iyKIyIx9R%Sf{;RLl`fBaBv z83N}jg+E4q^$}d{s6l5V?I>NX2-;R;Ej)wRixx8A?`2!1ZAZgR2jp@|P&kvML7^bK zudw7T3Op+k|f`STJu24uMCXh@A zYY0RRqMLjVfgbt9jj*09g67kzX9J;koP`p8axhYHF+aH#ocCGZO z%T(vp0(f`uR3T;z#7phxz@x>8`sskQ?`T9m^KOH_ zKw!YjwOCeWL*pJbQ6X1*;~sE3b_Fi3{?oYU7*Lq{70w5hDuD}^O1T^Zp=pvOEf8Ly z%#;#x=S&a`N)hq#LV!nx8}|TT@ANIEr@a3Jn|FLAo^V4UL8T%9<0mgbt5)f^s1ZGO zgU2^St19n`J;OLrXf9#hq1DhGcYJw~n8=JPX>AFemh0JM4QK^rY~S~zNhv9oqd8i& zF9C%jQ@XQX8Yq>n82Qz^;yxL|x||!b;or>&zwg*`MlL=caCI>y&rqJSvIk|W90y5G z9y=vobi$NyGJ)_yNt@i3P^gI66=Mxp!Xql$oPp)*ZyN!n2|69NP5%NK9oLRGXo(%0 ze1Ks?3=8q6CwzU}@X?~)DB;)6ctk+E8CzCtgd)tb@rkG?Cm|5M5WAU9&q^9+*ZhJo zjiEpvtI4+NHyVDv+15v9K(R7~P_Ix=;~t>VZo#4D>y3MkflBQPl}iE(gsvDK$h2;( z3Xo1AUS`bLy^ebg?^{ZvLAG^-D2UH5`fzKAvM{PWi zMuZEwf8&nzseq-51z_^hN$_zomv|0|{|+a;bN~z2oE- zp=lYeCzo3dG;N!8XV_;&4EyqRAqNZ#+9`xR`Pb7e2)^yOVv8!Zi?DiVY?#163=fb% zvXl!;CJ$a1FVHk~SncY8nr&^<-eX808nck~E1f=*2xEUcj{WymnjUgL*Xo$Hc`|b5 zNN^KV1;*U2-H64fBUa+#6|;NJ8Z@bw2XDVO3rdB-mB+eCCquu((?#2*eMcZB#+<~E zk)Q>V6>xRyE<|Z}8~1<)4ST@P+xpTz*~oYV6%Ind2rr5k#|$j`tVkyCQ0GPAe8vn0D-kBRBi2M93b>cD&4Nh{ln_u>bR;f)zXNfamBu}wO|22=-K)HD?|CqdFFID2;?xY*%Ub*GA@u3d zMQEU5_;Sfd2(&S!nQ(J+!6$P&qe)rw@}>&yer#H|49egz;}QEICN_Q6J4p#Ty&#ZG z9s{{^;v62varH<8sug<;dHl1jkIaa`{JGGyNE_pxfbIzX`Ei4B$G%WYGnFY;O`x2W zEssq3R3t|ryf6|7rM+pEou7LUTwe57FF=G4x^a4g1)^3sK806x;{gsnqK0TFubbDS zAwK(IFkBPdyM)+t;2xS+7=~sQ-okgE@5KE_&j+<*57hVS#9++@;KoeOs9hZWzxBg%A2n$X??- zGh^_VU8Ty{G7jGV1L5KJFO28zB}8e$vi_8$a?6w?@**nWaT17Zg_y3|&Ba|jG{zk( z5g8-IhLJ!#RgNc+LyNwJLU&BsRw{B~`ohl;6qpQq2$V0EUsxC6IQM7+CeC^T^(%G4 zpw{1E?_Upb>&{bLzx@mohOEL@-;EMlbPUD~{S4jzm-ccjIdcF%{Qe!hR0iva!Mux+ z-{HYr`)WZk@eZgInFj|Xgpdh@&2-gVD$-U&#R##XH)!-83G2bw z9jeQ>aUskcjPCI25Y%s0!npTh1O)=W|GOGJ8hs*2CrDaq_LsjW;q?w3(WCwtw68q~ z%eIac*Aam}jYnblgf>tm7z?RKsp1~^a`rrl>MqGnj#aV$)g9ZqNDpNlD*|5OS>0N|yHx$3*yq>+ce@9f5dHHR7K*7{c z{c{9j*)knoL?H47$(D$jK-_Gr8O4}FWGvkl1`EW^&Q&%OxKW{~e9BrhGiFn<0TnpchN3n-Qhv-nRNN-H~VL04u3j-w!j;r?F z%As)u^U7%o?OE(uI?uRg2ROJ?gSAi~9uZf}3yWnYa1W@74deS z&Ok!?V&z*2e(!qpFNoM}c9*0DdMAoJMBFv6!;+bBXZuk^=?)wBfJP0wO7o(!ku@;% z%g#cXt14~NmAvcP?}&*vuOMmZ#5aCEUaTM+T&k#Q^Yfd243hxUkjV#ttlRpvvVv7M#u zY-A1i=Fg3~`Rg0^n6AGY=T?{%QhPvYcIL>CCj!oiT2u70yE-;i1lv5oQY_Lqc&q?znLesF=SQ{Cyl7&&x*o zW5k!Q3yGIU+NQg8&!t}xZ%6T-h(P3TPW+l_keme~LnaXkzFZ;62C#Ns#4~dl4Yz~~ z%iA4QYOukhV? zC9-JZCLF(;F#F0~8ED)}8=T376&_{(c`lB|_3eknU^ciD^>9b)*Lz9Z*~nVRRkRQ) z1Q^x}A;L9#aO04n+Oj>MOek_Gs4QEiLvjRSc~R2mm~G-4m_s^XEhGyh#7rP+cgF_N zgL-y>x2l104_Liv8SXx`j~~;=ONAbrSWA{I4uQPS?!hKBq zX0qsTb8v-OSsmY*YurmCoQFRU78{wVCKJYqb3Ji>m+9Vksw(2u*D`iZ|7|J<}v5YA7I?G9+4I<3pY1r;L z_Vb?$aqd6+SECK|cN0QB2;JMKzKlggcs%Nq>w)0-6UHM3RLCwW(ddWu3<{QnTU*bF10rzLW5V!_P~=^ z%T!$jD&~0u)jpnP+_x8;=i0TZDCp_i%p|yUe6Mj!K#Zx@!^}SNY-COJp3p%MiFv`f ze@@wF#X?mUTWl;jIwXm(nWmnrlrHeiX`>5D$gNn7*?;7!uzehSdPMM*z%M(5qw69~6MmEI&=SeeKce;zL)5X+0=XQX-8t}ZSJ zxo_KpPrcWYb@l032dUP`flw&x7cp4IC*v~+OgvO&m+1_{%V_40O(2Fjv zRp@P0`f!r*h<)KqSy&{H_(z!|l{fhET}0_F825lG#VZ-NvXRx%zK6wHA^KZ5`|~fx zz0@PCRlP~h0+C^oJdAbVoO53}CioS^~S zry=oZTIqkdC(cC%Gd}7S8;x?~-!~qyKNL>ndsXP4A?B%Vi*RKWuI}OFLBp75Vnh2k zUz4`8k@eBMPeZ7c`Hg$P-&ceG55e$oV|J0xWzd+rBJ$#;}=8AhI7Eh#S3Nf)->)3Ay1K)0se6sjyAk|3RlHDDB4T zuHgM%GZB~0_wmBHr)XKDH*Q9lgK>I=FPfJ77)3`8Gahjq+|-UwpSB))JPUqo+;(2X zN5|q${59hqP|UBau)^(FMKc>tiTHT4b=-bX7vq)?CHmX=_xGK~y;LHLEa@yGISYi1 zsWPT?8Ldu>&}TNzBBljmQTJREpMEYZ8R}+LP|>%$aSu3rZ5@8!l70d8eLJtBO_T0Y z9W4_SfoN5IDhiG0Z#>{wsMU^7pSA_h!tI|{ZErk1c}0x=e~o*PUGKomg>vaeZv2kG{g)jv((1j(V_VE8f zuQbe1*Xu(t<-M;E66RQKfU%Z!OOB#XpWcYno--a5P~=DZM$?g}znRn=4YgBQAV5fr z^SiLFo;hkNo(N$rwjS2Pu$@H_9rFyav6;iQ=qNmStig^GGqK?7!8rZTWq7#zp>&Bx z#&^ZI)&7mpJE2e%TqJBykT?ds_9h0m9*X;SGiJROojwjBArEnI&wrTx?spg_&ZT$b9_Zife;D%G z5WL%dBtGc=K0fO=1uG^l#qsS&5gr~2oz8wT<*)5m#sA^Z>hEL5`xC`;tVQEqmTs(j zxVdk?anE+>UJ?uBg<>@_!mu!uwNTOmaWPvU@fLGJ(i^4UYypK!S81DdjTSDNkNbA2 zlVu5#$CP0!F!AfR#Y18C^(&O+(Y@z4@Odr$T_J8llX516>zreW-4}r~nPQvBj&3G8I z54R$J$Cc16IP>@?{C)ce?7s93e)!`Nb z=A#G+euTLAaES!0K}>82Zk;!K3hqW7!M}fBMg3Z3;i@!OUOsolT$R~YSbHv+1vXnC zQPH+rAeIE8$v^_p<_kpAybXkKFqC5njz5kWqgEg>Cvp|V*myyf;!;C}oO>D~J}WGH zel9l7@cxTH@)+Jun7|u85_@JLsa96U|3)l?XT8eCBk2pZhiyeL9gQfxa}!9cRwGEx z9H*&T?N#Ge*2M9DPZ_s>XK^PmrQcWLSv6OXNDuhu^u*B{T#nj$w*WKxG`cI|Sw4)b zVViO2#LU1YqXJg z_Us{!?25$Ee=X;FL}SgYRZyxF$n9CicuZKPk=IgPisz+wVa3GQMgSxbPC%3q7n^Ye zLKJzk9+a;AjC;U_T}!cL-6_oYU@J!VT7*80K1G|VqtK+xn`l_34;q*2i6#}hp+ULM zs9mz7IMxpxU-(cGDBmh;r|&+9ykp!l;N*jKSUqQNx@yeN zZi+FXOIw<30_a1;_g%n~xP7=7ydFC*&c-*J-o}6tP0+qsW3+AD9IYF^hPDmb;`Mf~ zW6GzE5T;3JUHp6J5Bz&R0D*qy`5>{n^9Vj`;g+@u&-JJEZ0Ursi9o1b$Hdy^@{tI{ z)l49W|PS)r#uVMQ4gRpAn1pIk+K2F?SBTVLPxE;Amn6Z2D zAo>6v#U93EVd7s8+kvAumto_+Ntp0y7qon}IoehojPY+QL;X_yap?Mw(tHycIb2(y z=fp+O7qYE}n7!e2i5`*!}4WExeSeSXND)kR> z{jbz5HLcUXJXu&WEErOcSR1pbp0hygyvA^95LhlZl-_R{_tFN97=+itwqebmAK*#| z&2KY2UAm$3XWu~=knzIX!`I1H&(l6dtQ~3#ISRLKn>FDaE&=fI%DM#d=bT+am_A=x zCSG?73&($nkkIs5g7}})X-koH6wW?cBNRuAf|>%B6HIME5-zMPZGj}XI9eca8ry0~ zNd%&{ut045dwW84^)4<>*nu^NPC*y3#CS9z6bgS7@oj~M6+b|e${(S5)sN7!#>b+4 zg2olcp}1drc)L^-pG;YxM5!L&!6N%q@k}e+d~7en?@;KUA% zLClqlSUbD1anCu>q5QX~`lg}Gj59$e?&H)CK7>Bbuo7MUGH+wjGA2c29xx>~HZ}^6 z9!KNKx$C(8-wiyta|@5}-@ucHHxU+fA5j{*Wm0yj{xrdPO&f5$T~{4r0O= zh78Hf0G%Qa-s-Xnxos$IqY*M>`j|?%`}Dl=2$;U}jc6>%&4Uz7>6^KbMkHr}$c!jA z=yW=vx!7)jxVxDLU7Sv;ijpo+==2C%@+X#0`4YSCj)6XAP6F~Eag_9Hi_fQS!QOv2 zVac|U=-46;%9qNC+yQFj5JrZtXg*%T-taWEoH>AkK?>BVMq7WB#o#yy}wdFNcd zmdYP9r7j=5gr7hE4b%QN3vai67rh(xLHl}b(Y8q=^zU9BpHJ?LHLFKs&;GA*_P@<| z5Oz|K2d~dMfBfUv!#K4558S%txC+X@uTNu%kaUJp{{UBxndb>v1NL?z3xqoqhZh(;F&?Q>Sxi$MZWt1Sp5oq|Z30~fG;%mQpU`4P05W#$>hMtG^pVbaL& z@yFT47&xpt^5%Afg{RGG@Ne?wQeyOkrr2{~8MbZTkA|g&OY51-Pza^s^wW9RGkzEp z8MKg+!qZEb&_TvM=Rk0TbCxp+xp)V9jX9`Xx?#qba3@iOg+Ic%zpr8KvX)) z_-(Xq*cU^_b;taz1F-Y*S2!QM77t^0AzCMdn-FsL2}26M zosqck&|K!<8fb(pOjOE*0bw?hh=E@+8LS}?jzAM-#0%3nPGbXu2)KKwja#CZ^p3Yc zo-Evql|O%q2eF??7Jwxb3O}@{FdQfT+l}F$w1TG{*SB(a1FF}^gS7`IV9Bx#DCFH- zbfDbDdfbfIj9h171WRG86KG z*i6i*(Z?b>#x^U$!-7EK9ZVqCW`B#Vr{5F2ZZ4TFBacU2Y+kZoy!6V*Mz5fLY5;D92O-kRztlbwK6yS zyrfNsf`q{e#p@Pq#po`h(4$FL^laY*b5{1oftxdMJ9-o1wMPY6q)$dvVz6?bl5ec*)Vq$E&+1_5} z5)fLcuN;F%^EYGP)dga(C!AW&SVz6${jvA#M$~RpL=y1n0Us|{Ok4Uo*8Q>(IbCZQ zkBU(faUQ3>cHH7oFM_{I`fJk3AyOBS&?D?5co1bSmgnUX2rqZD0%T9nb@SJ+Co%PZ z)A2_0H_@X_V=P=b2>;$&gOK=>&>Kn2S#TrVG4b6CC(^!9Q$|3czk|?o=5qfjfdmp8 zXX--=QMyND0^xp^A`qE8_>7yLbDgk2Vr^VZBUizk#x3!s!rWFw+n+>Yem;ud{##?C0my>oquc^c+6y`5`(t?ud^*Ylpp;XG%4nbjFnrofYm1 z595}7@hsFiU70CCA;jYC|EymYf&>y3lYoS2fl%Acaux_7lLs%%^qlL(wZ_ESrZ<+a zZLTAuAc5F<0vXeEH|<>yh31&?hyk9eviN51MvR}`Q_7&qNYtra5DS-m1GTbYI@)_cvIoD)D4)kE1`J8h4uCLzxBneH>$ng(h_CI6dgmka73Hb>mj@pkfjpo?sC*S{w|9(gL9n za7JT1MWp1y;zjX7O+G7~xYn>}zHL2%iUx`=G&qv^zyx2sEN$Bhf6rWoSj`&ao&kYw zweZW{?P%An4BQ+u?rlbk)@AU)xY?pF3>vLgkm-Mm=1SY?0so*pru*Ow&}*I|;H3qll{y1+kgu3Kb}o`U04LZ8(CSO@`pN zGoOp+CS%svuosw3*tDNnaM}~)@?}hgYFnU(8Us2`z&q3bhXQ$v8IJ;H3tc#RD z_tk^{62y9_Ine@7{KjVZhonq}&m-LJObuPsOL@vOHH63N zQeQhOcy4W~k3{eBeW6mhq3kQ>F%hr7jmQTnThSzt>wj%E?nw&yCDF313oEe&Vp0=1 z`}OWc+a7vXl`#HXEeIscJ{3OWwx7h)c%}dvz<~LuQx>67i-OW`+6qpPy!S8`|NQ$1 zTejZ9`jzK!@Yq9~zVHOWp;~e6#t)_u9p9*dd>(a-d%*sa>0^cHd_3W;4K(gK2cAB) zpE6+&L_P_T;&g;U5r}}iwg=CmqMzZ*86SyPq?t(kece#Ix_O=DDD6?i z9=2^(Yig0t!<-T440symT&|s`PaaBEHlbDo!7CMsN8f+?Bf>SAZ(>|>s8g?=am)U2 zQ7SOx-BDsIgEL){K!sf0F=@qkSguByE0-@^g>peK8RpJaQ*EmkA}*n%rA%e&ne4)% zrzcaIsn%K`%#GzX;x+Mj^5ijCB+*)ss{<^`iHFB0$VNyNRF`h!TJ-P6J+8Y^1*4~R zw>jG?Slq_9+I@*8wHxD?qf_xT?jLA{Rv~A{D8&8c`p6Gr4q(-usd%N@E9g?|ecZm2 zww(C?eZa|ta$-RgZX8NjF4|Ej5ac{AEFvWOfm5ko!Qtkrvu;X1xVzaNL<^5_-ZJ&6 zfl84dUEXPJ+;a>XcdUjAL5_*Bq!z_|TjTS!6XEKba4mc{Hy`+@^BVVrDe)-fMX-XO zCGfg1fX;ztIf*b!5eOlZ2nodWRK*Db2@QXWc)nu;^5iy8VvE(Koj@*b_(ND9X4CQY z&TrT>yPQ_U@#9a>tYU9$IW`*_qq~8@GL@I z)tY0%;FXAtOWl1hUm>3`+ga?`fd3w(kH(t2Tm^BB&XZXX5&sZcgEzn_@XTVxO|{Zp z2oKv+_`Cx$Xu^?$P`^}Hcz8H=JsDQ{eS5|jDTe6`$m`J%U#$8ZUY^e{gTS=z+&RoC z@sFeKCtFd0?yGY@Eimp$V33U#2(?fWh>gh0u_tb zH0}Y9w3nd_PdJ0#5%8!}MyQm|Q(ZPv`^aesgc?(b#k?F3l?PlzvlV4)RyS^Cez+(K zVeqv6#y!WPM2Y+u@a8Nbpc88R=?MnRi~6+2r^~)ZVBm{a5zUvsuyIQ$LE3vpe?`)W z6;k%(iIv7ZNuK^I8kKTB@EmZ?WQ5y<AA41oXaM@KzX>^K}Mj^^Xwq&^cycGgBtSqFS_vn&gq(Ba$>4G_E16 z7bjc5E0t(it_N}lrB?|YB|7!1h7Qdpi{~^ISukmYLJ@%0HK$_6hG{5L*zBT83dPEq zm9SXd(_~Y5<2bIOO$ilL%BgY6@ej(^EN6ib6o)c_$QLRhS{sUoA-0ib!BTmIpf(rt z3q6+l%ASw69fm@~EI1N^D_$SdM%uOp_aDZiZ<`^A&=@QjA7Q<``^m4U(6KcXWS3+> zQmNLr3x@x18N7uxBI8QP7L4dL9KZf?*QVG|({9XvH)yzwxm-T=m$dC@6!S66hIa-? zUxO3t5va+aWC`2Raq2o&Fm`#Yx)Uu7nLUljBkNg z%MCIfNgw#RR>$b^>+tTZp78Tcx#UlkCgu|VI)q83+Fyjw#|g6WIKUVrvm}sAi{uD| ztVnT4UllLZ_Lv?fy9ucdi!*2o1!Cm1p(vgAHRBP-L8-`xnni|T*4iJ?s(m>@VAjowFI+UQc!CU{ z)$2o`4N2N1&)CT6bxVzV5}1N2$3G}nDQ8nqU7Z)n5eP4eqL2ha@-S5?jMF^9<43l| zKv+&Bw^u&n9`parlmBeR-6-?l`nu#nzMzyJRK`7i{rN0*otJ%)-ss)+OL%jM!Bjx$ z>WVI(e1=99nSN*x#^IXNcz^Uj96Nqn+D{3do+{*YGxtdt>x5|>?+*2E_6l?g=dnU= z2Rlb9Z_G3COzX^r{k*+XPawJca-n=sbK_p-L!(l|&|_>X<6inBSFSvmzH%noHTx7Q ziz*iOf}64=8kQT2nag)z%8K_;I3L%&w_d4ob#)WpVNi%UA@|VX@I+Rx(FHE(${NSrH#BrPf_v6kvMHEpR5iM?l*#hxRWBKM0pS>gg zx8sUs>4f5b?J;V>falUz(;LiydS`N9Oq=}+@_W){Y`uU_8lK3hegz$y%*3LfH{+AV z@1j!4+;DZVT~T0ig|FHiWRG}3MTJNa|$mGGFl%b5N86G{U^C+$@L$*t%pSegxR*T2mMLxA66dAAkMOf8=akz6MRTOS#*@9F z(_O&bv*|ODGC61?VxZTW2_#2g+LmxFS*|qdSMF=v%N+1?t%3I!O@)uQbF3Z134GOR zmBhDO=c7-{X$Vv|hu)0AlVY6Zb9YD2RA6-b=JNDpDg$gx!ui+i8(XelqK-~=d)0(pmVJoL(m#|bJG9t)4mm${=s+X zS@Bzx_Wlrg-Fl*sXJ?f2?}M882cub;_t3T8r+Dj)g;>4y7&h(u4Rg25#h?kjP`-2? zcvw`#NGoy&m}9z;Ay1M_M?bc4iE&E;-5(?nIsQQpPnIPRUYMMPMYbesnsSUDh221K z(s82~qCin$E-K`pu^u<~?J#azk7w5tYN{%il(mTSS3+>%u_17-R8~dpL9a{O_Jpg@ zNNU#XXWSE-NBqCove;npB!^nf$_Z`8tm!_mW1-395YVat^okefxu-WS-uyemZFx;} zYyt};SK+kxk4oi%PZ!LVg0;?!Ky__=Id3IismB!Ww7QBMi|Vb*VDO@L_-5xjSh@FW zEZg-B=53paFE&iU2g}A{*z|$uG^_)H0&^uOL1J&@D`KAX7!mQzEF6s>q5ONzRI872 zl&POGflyn?)bS4@B@2YlG+kJ778Z#_LU8O&Tze#iM68Cpy8=NTX4lE>8@A7ThzL&P z`qCwT4;VcEWzk=V*BJzJ#T?Dx=I$83blpxvpfc6S(P7(Hddh8VH2hb%90G=E9Ouyj)52Z8}DLr}M=_=n*LG z-wj{>xDXYq(VBN|@^B^;uWFWM5z!&frSisz{by08r7NhA^Wo+Iqb#NH%+&D@A|(Q$ zbe0K(_6Ac8B-V(Gy^C89Z6go{jms4)Z`=d!huiM1(LF~P$rH%4&{*u>`LndGhX<O2Bk4^K2KH5LGHNaK%+ z3zsIMTH?l@1Re-l9EsryC#e(T0F+>AIXZM!JTAgD<-Y1>h7QJGEV@Ys|WOHzyR=j&^r~^oIdJysUm2>BMMat8A4;KYxVzf-`JS-+V>GWuOT=+*6>H(5ERMdf z&BXiPzlos0Z1PAAyu4I!F?t+gG~v&wvM2T>P>3iDEZtX`KK?;iOCYiZ!VZx7Nh>UY ze|K&dM8ZAmQNMF_C@p5B-`u*vxMdv_iiEdGpF;hRjbHx;7CEZHFx!4i2KjBa`~x6V4D`uTilC`#-q41LWuME${0k(n2Fm(;R%(> zzSaVZ@Qs`I7J9d3y{C*RtIst~Y8DuVg)7#h--PzU0?t~6AJY@AN)d`1tET15g^shISm7sEUg^#O&Ks5T;1TL)T*jR){o9g{h>d@6<8UM)i)(4Ri zf$$$^7OeAs@zEHf~u5rC@zaq(mT(pP45e`UW|+ zBsI$xJc~6?C@Npcx>Q+DkA%V1m@wO``=%CU^EppCFLCU`-VAeNMa3K9AB3CALyR@Y zSgRD=9s1VB!c}WfA*ic3V2**=9SUCrxi`kh*B9g4ZC{~oi?SI$K`^U8<>Cf^FGI?+ zRv(vuK+bPoZ`_hVrCTnbD9``Ms9|nbSo4HX)u79! zWqY&lU8lc`sK2bw#`5w>IKe6Saq{aChsBuv+HM6MJ2~pn`D+N*8dfJ`YHo+o))mzA z@=ECHicLoXDO|51bc*CFkESOc#_hv{E9p}cEHR=qi3mjD7y>asfx<-=zOy< zziz{P5Z}THQ%~>SPhQxlUCi+Xg_|P<0&wEjO+qZNo49mrbyO{F-DE{?--MrwiA*{8 zZZEjhDFq*u6HO}AhvLY_h0;Tq-iX(k3B=vi6N*IEt|P$WeRW?ehEIO_AC_Pn^zUA@k&bs1zMN)?~@}Tfy73$o6`?f^O{#&bOxMzyc3a* z4pt=rjouu;Zg8AxYGm?jj)Y3^g~+>|HGS;Ex? zXj0caA0tHjAM~fp!S3Xs$emmKXU-f6jY~cT6BKAJ%W&nYNh`2B{`+kYVzq{K&6SD( zyz$X%7BrVU`~woY7NTR)HxsFJx7JSO#?tC<~&w? z+z1TFjly{XQMO=CR4bMP^-6i8MR_l@t>A`cWjs*5h!2Y7_Jfb7pEy^;e1vSIFN#+# zBm|_Zc-CW~2sX?GIkt6~@m&cd4f@&X#^N@W@t2A~2%20py<`I6v~G3;V&h`*+s2*T zv>b3Q;=gwCxH_bR4!E zWUVcO`>Lop!Q~Av*mG00}z1r3*_Ch{Ac+ zj?ad3!oyt$mC+rq4Spu2@1DG7a>-NJP&1}ELIR=UO=Ubw#6JjI2!vuTYbEX*K8&*n>s1)98=%Cd`ixvyLE?)F`=2phlY6ZMi29HS0-AA~%C4o1F zGLqJZ9RHvUeJKfqpy_2vAf{YXjV1;^e!0cCXC0W%+y9g9V#_#*F8&{!KA!ZdUWrk( zT}R`V0cVbxDy$@l&wd;arLp9IPX7o$P8*1*$g~HlIr!JET`q=&q2i{ivJmEMd>`&= z+l0^c3kk2=%b3}bp5P)=dDM#6?Fq|Cs*adM__9H#)nWRODd^w4 zH@Y-vg;sTIqiLPeXkM=jUaMadZ5uSj>rH!L{(Ilx!M(fC=}bClrh$vALMUDafvT!i6rg9Ta5o7@BUFJ6`Q zQ;S23mf%6GdA;MlE#E}(VzyUsy?e)e&3c6o+{-v{Vp%e%T-IEt%sCK;e-?fIT#-7* z!NP@=O(1E))nlj7zF`yWIQ@h|ylggXsPE`S29H-nA(v zzA+8q;i+4xG^4oO){#@ zlFFvh#N%G{ahy4ATPbpPH=t9i_QpN&WzjqEU^7eRr;L2Q=9T{CA`+Lj9l(zt4#dwJ`-u-3+$#mV8e`|#AK{XUq#Y(7+z&RdgYKcs80&-3d$^&L zH`kta9%%K!IJRVFx;%N7m7cRhAhv_#aqdDWrhPP02vy_rsPGlaRZBE3GX(GUU4Wm~ zAH%l&S21tiZj2th6m4oxfS1Y~GYpPDj&c3Q2r^H`rQk#e4-bdNxQ0x8)Nj(O5j<#_ zeMQ-srK}G^kyH>%L6tpea7rsv4!DvEXP~cs|`w(ys-^eDNMiRx#JUOA172m6ESSsFlpPF@ym}tW6rmR^9+lKKwLbzLLv)JI-@`9q-b&~ugN^g zDzH`0qU5A0M#;-K2s=kYOl*v>1Ww`C)pYw=4|3Jmuiq4}hS-5V=675?obseXg*f$M zzV(cI;zX|eg(uHac6GQ3E8)A%Z(-T;?Wj@kO}Hux8{Z*f@i_V``?(f_vmxQRSy+Lv$JMcU3NxVL^qScEN& zd(Huk{s|6#KP7=x=m=P*@n+B*!c2`rzg)$s2W#P`EQuk5HemiQvr;4vLZx)Y$Qc7s z$g8&T2=LcG`;A-936&ThzTUvj&4!v+RK$(sL2w{3Ej7!g@5)FGfsjS1CXhfV`AoeQ zuZhF=GuImTtVfmV1yS1H5WrP(4mr1FlrJn-IE&O9;`N#+tc*NJ2R!khEI2 zDS`EeCu8NWThX=o*C>{w9fE{4;pMSWXiK(8OL zY0pns`p0eZFm4|;x`xZ=7X&+EWSZM0^L{)T+ofxRWwfMkR)j?{|Y#L=`Z9CNO>_OORQad z1n<1xRs4WPXqw(=S8fghhnojfk`QdMa%4wnbpJ@Zf$G}W{?BR@%5U3m6AvCo;_A%^ z+z)X@WV}jf6bfM(7~byX=5gIvA}?vj!uwytY-Q+up)7-)Q@wQtb z?fTS2klQQLwn86){g-!3+sQ&`%k@eyi_0L*AIJTTrPKG=TpgImlgkTrYXqTdvz+)} zs~qUp+81xOF|=OI{m`^lAS#s&K(3r=ClBKGy-3WTGs8q4dW9b<^%^8?XEd}0a-oJ_ zUE`kf;PRv2ap#8hg*R=1!98YSfn-O~ZAQL)c~VUtoC9+FjL9Poca?LlS>i2p(>#Ja z;>A$e{-a^d4=UkC@{mE|pofO8e!h_kiIh2znx}-FA;&=&7`kt45{X2|MB$g;cR*uf z5`~wC5?wmbm@yz!vklRk4;%Mhj3OiY!A)f-%*eSUKdzaDbLR~)1!o0ocYQi+IiAIv zXWp0d>kL)#^jo1WB~*F69rPC2G0uTFZ2Nu!V(mcSi9tg=VHQ?4futu6{qhfPM3^f| z)GT8zw(2ZUy8;FC7}V7>mmcHVGlSJx?X8G`3jHopJlXXx~F!U)EQGt5L6 zpb|}aNCJ`Pf0(>8SrMrr5E2Cm#L|t$8HA>(eDRtv%pU)P)X}yer>_edmVeE-C%!oT zPs9n$Bqap&YlmFwHpV^R+>@X2$$*uJk9WSf)wXR{@c#G_V(@XC5ehVGI1+jv+v7Hl zM$L}hpto4~%Nal`bLX!4h_G`^(%`}}Ym!`yFt@qrb2c1=izm+Et$uHt=Z%Ov+^X7e z1bxc4v;dk-6uUkvUm6M+y+A0vUt0dx*38zo`pL24}!b_h)~rl>L7 zfYOlZ2ol?dMTFtVe|zvO#I}c=v4u|-j*v7ELaD!nJ$tPPqzUz)?OS8T7YO@OfI?=? zuFo)G*iX{Fv*5sAck%Xs!H73KEI_dw?UAQz#-{GR24dwoIvRfRwQSLITe@S_@>A&9qIUvKORp$^_T8pJ?_u4t zw;2WR)^(A?#rZO_^!h9KW8Pe;?2J8N$gDFHh~5}P_wq=neP$zd&rCip1?#~r+~7?q2*#gE?}lG5+e3n5{#7|~-n#*FEMaLsAsQ8DlYd9*A( z2A&O^Yc28@q24QQG`EnDQ9_h0n!QwF@TH*tOSEqqdt94Y#CHPeaucA+HWp6 zm=bDFoq`%UKTNRBHwE~ZPu@VQN^j%fo?FQ-gPT^+Wp?2B6LhLI7VCG3^U$+v4A3cy zp^WDT$Ul;6p=1u^e!VN)RnA9tO8rA@-aK0jIQ#f0sU@4BP|ExPwTpp7vXMG)0jb0P z{3iPId3S^!D%5E<7zOh$+JKnRxc>83w#Q0)O9Jj3>d5MZ1N&5vI!FRHRwM zNhXjipM51Gtpp-lA?z?YrI)iXrOu0pj==WcezVz)MZDIbJj&%V%tR;S-&7p1+6vJN zb+O{qp=eur67(vw`-KDe%)|BQ+^sR1l^cXrOHU#+LTeWrla|esXIkvscNg92PC>6$ z?Qs4v*GW&Pgecw0pkSa5dR^vHP8wDIpXev&ddP`z%`P0CJKvZ^l~$OENa2$3(O^J5 z+m)3%xVU&?(WdWEvtU1A$t7H_na{`8ZKJUE+kc_am~_t!MUXf}#y%|Z&+#)@_w#qg zz2`uAM?X7b!Ls{`54b%^5{I>HjUa4xk z3is15cQA553!xc46L-QBMLp)A(Tujncddiwzy)mCxDR3aHBe|S8;=@5f#{_wg;KdI zp+)=V=sc_$a`?Ex!_DCTOl~e2t&C`4q=pFsdhjR=zpp!mZEM!ya`0YpzR3r7IcLSk zr8QdA>jK|yO^ipK8!WNEdGufe#~W5(bS7{q-hr>LLcuC!jYm?4$zzI&duMRD_Ukws zUoP$?ZOdSVgDba%w0J3WOZ$Ve=Kjqam@#S&PCeL~z#?Jh<>0>a(0v%Q{?e+~rXD$b z4AzGwXno|$Ws+aK`+UGTPzo9MS*~QUXk7oCfpiiHNhF&XTmhiUkz?U5miaBLKPmb0dGj0w__T7(6`H2n&fsWM~v3Bcc%z7L7Z%@8I6k zn|K;~57F8?2_A-|K(caGd}H@MvIInbDaM_s@B(Ufx}txY;FV z3*-zrh1tt#H~6}|hLO{jK*b3hsl=ya%82_z1rq4qWD4f~G}stiH-k#Ub#V)wA+s;D zf->jy{^OWF`77LyHdozc(bC!9uSeN(mKCzo5413)ndXV^`Py}x(WjRw)t=68M$zfh zrQ*z32iJyRkdrBve83qKeks%pdl3ky*f95wwK&+Z%|xTAAbAnX1hvJ1i<^+s*Y>Hs z8+Sg&+r3*tD}+9mdoAWZ2MuSmGQMjYl%bKhzw`k1-uPFDupPo8d49z-dqFR{#7k8i zh5bsPQk7Rwqi1Jm#0cFzdo}j{{TY;J% z78^m!OX>F|bRlEKps{xlN`d-?K1S1#!;Sm4oKSj3CpjCP!PFaf zvq>baV9aUC8xyea$}(~H44qlnrzPfX{T5zs={vEOE~`R$pC^wbtPg6REVIGd^Bf%{ zjvqw3B$`|}k=o=-F*@vFE0TejRzZ`FvoD#(M(Jph5D#NdVE-;dl~Y^Mtac#kmNB>z z6#8)7Q2u3gEF>{>IfTYP>NWJ7Jq~?4&PUClPoP#d5Eoz(ElULyiX8Azl}0|#MyQsr z7v601Iri>7iHmpkVDF{xB~A8?PJ!rF+Z~g?dlNHOZ%5Vq^zs=B#wqk+xDojiewft? zCnkL)BowV*=S0=MtJW29@V0qR(wtCX$C9`)@D9qZ8Jpd?y8^#eA2D z@CdwUVN6eVA?Wgn&u5&cB}+h|P+{VV&(N;Md*bdH;%hg<|G<(7={o_K1rn1Y4`RW$ z%Z*zSNFZ{JCfv5MCtC_?%tX2o;P!6XO?BVVBhg~o%d#j znc4U5{hxnd{sRXwxQRwZG3aArpJT-qb#Zb*>ng*s;jgJ#T68-O2jKN?FDdN~=msg{ zFaxb;zHEFo`-s?n5dR!lg&PrvgjU3P@F-YYVx=hxXRD&{bSMrVuhJ-0z7ndpu8UfY zN}x(9C%C)WC>cBHfo>50ARYGnb_U-~`2x42_nR~@!b(#Pt?CX%sUd@m&lD^K{qh@r z-Tk)s&0Ig3L$i`o;n$!M)@<30!~}LD&RIpx&H6Y`)E}1WW{PKflh)$vs2{iP3 z4eS4!Zjp(T5|PnD;0b*+l_jJi6QCtByz_9J589}lyI9x4vK{N->SWktE;m8)pfZq} z$h&*@0ZNxPZ81&qV1m$1G3aq&ux7s)i^QN+!VFYeAN0>YSs60fKu!?|!KFqwRv9sB ziNQ{My~R+Lps!aGoVd6bJ|4XGnL|i~4kKS#i0wx|GCX158hu-i%5*FwJ?Ik?aA^E& zT#06*!vs|HcoEf_yo919N~3)3;wa*i2ro~)ve!uwZ)c&2YOW(*Ca(1c@Hv!o{ue)tA-p`5=32Q9`-gq1e)f&Z8!Lx!5R zUU~F=?^Sr%juxvedoDzC(4sjLxdaDib35wH!CITl7IE>>f)vu11Jq#4mjdiEZ}6xv zgO|4x>i8M9QszC}wR(keoLflC4N3Bt{q=X|`{($1cHpV z&732I#?J~-wg2b~ywW@g%^MaI7Ke>RMQToif!d0GPFS#GG?p&eh*C~%6)lWlu=#4} z7Oa@q4*siuR-P*e(0RGzg=gOecaO{r;u4cnoRi8HNB0S@!Po8s@%PdLnlsSqZ)1=D zLY!Mng)!r>vr1-x*n}9w#$_zf&wV1wmsIOSvIA>t2UKX0+)?{M7(GR~1CmD>-_*&2 zICS_3KL7M%af&yw&}4SZjo8YlKexz1t3r#NSGVe!)<3JO_4$ zpF`&MZP4j0HHai1cXXIK1eKh|D%of`1x?}!th=-h*M2*Oqzv9fd)s6NNJxwo1fu9Z z83Kh71F>og_^g2&ctt;;$S@#Q$L1C}4_b_Ak;l!OchIdXcR5wS2|!|C?{;PZ-4KJG z6$%}?t>|r`yrZXu9+*O9eGoZGAOsnh7o2yWvw^`SLt?!^e0(@&d^#h`ZA9og8Zcm( zxJ!l%9$ms3{PX=zI=8Mh9Ip*1EhbrVDXPDiD)+>c2^N1zVg`NP zO!Zv7KO6x;ng3&5oHUsH<3Jofwih*hx{1Hn;NoEqp)>ba;g^wZ5f_$vus^TSZupyG zF|u|vW6-4%@6bCrpy{+Bc*b2=A}K1BvxWGrSh{~2PVd-*hzulsxjHik#DIvzFvP}Y z-drvsem;rhaRTaTMNE7m&fN)xe{eDhWCn2w(fIrDYU3H;VVkwBanpjgJA_jB_cVdfaaUc#9LZ~V})ZQ<+CTsfzE=Va8+NZ7?~*|Uc5?FX>u+zjzqG4OS4i}&aD zLzS|u4@$=M+Yd2rzz=w?W*@v<{{yUFeiA{Uy2sW5(DGQiJrG+z`w>NbvgRD8(p;vj zD|Vj!9y7)*f`e5R!wUkT@#nE|;`4}om|T>YUq~ul6kQv?Aq0QMT5y|h8*2e;Yt;U9 z2$~lkXVJqfyU^&4V$-gn*t76M1gDcB=izS3xIuJMfVhuYi;UBi#kh!V=JQ3KSe&~X zjd^d6!?d?P!@<8F3Pp>GdB(A1@><-AWSD_)K<_>SjK|prb1AqX6#JPy&KQ00FaW;3 z7A5GkDt;A%#eoc*Qw(l0-nt5#zH}2q@v8AzF}roQvE}p5hza8*&R@9pZHMY^8FOW6^l7iP3Lo~z^OYt* zr)4r<&VcF0|D9fh3)|KsHjNqY?vq@^nWPUyWEkZ{*1%RcjEm4^QW)T28;47Q2XXq= z8cZ4SJU$=15I1j8VNYMS?pm@P>-T>t?h|FP1}$rhz>7nYcP7maY=qKKPLR2K8s5>s zz+hCX$}UVMa7A;QZtlOpNIL6080(N02Y)C9m>h)`IZYr0XAIe!W*&Wo3_$G_LlLvF zaWM#wJBtzBW@fpPw_Be&Xj-nH@r(iWWeEHsbFM5+Ts$_+83~>Kl9&Ouc%$D$v~N>J z(N1iCz!Uu%nqW&w2h9B!L2~cV$Ap?H`q8BiV^c04JV9*y;TNWSyt(Jb)KGU{mM|GWVRr8KF;A@4NFj68X z>p;vwX8|LUbY#lH!r@|2+9@-GO%}hKy&63RWUZFt^yM&gY1S3dNrt?Aja7FH{@`OI zmCRVv#sg#D#kI(F%5lAt1F`PU=}KyDdSG?(^*hm6`@^5uwq`Z%MqUxW;Ral+E24SL zuGsbOV%Tb3Fm=u%yxO;%@n2;R*KQ|b&~w9aA!w8F88M5kE1;3vNK~DYa|M;zMby6I zSifvf2G-i9cy-7ul&a$Qi1rj68Hq1G zt}e*K5Vz{t@*~XKFibhlF4))Yt}qs#=JqH4_io{xKEn|df7JMl0UC`DN;=j=*-~}k zRJMe&h$1fC!#}rni?w5Fi4GsTrubmt0z6Yb`;6=129%)7Dvc$5@7_a{DkXkTGDr~2 zv|_sB88Mh~MKg-Zund%|Fhgz;2tRSFhaU*K0FbFm73 zzkgsHPG1Pc`LixqIsHxCh}s1^ZAFZpvJ9^e^o4^%_RWvL5Ix@OHXXaq%oS&bd!^Hq zMt%3usQrE?=sXHm?&#UiKgaPq{H>{@TG<|GF!~$gS^9|C@)y?Z`a+OM*0T3fgSAx` zyxQk|_|;|a&t$}kS^N3>^~Itv#FiSC9*kd3Ov|y&q}cNRzOc!|O%C0s51}CsF`?gN zTnPD1tS`#k^ue`T&8G*3%$5nFU2xF>j%urc=az{^Cv4sAi$ykvvEk)GA3H zGVVcFyd;lDg=HZ}Zn&@rUT6j-WH6m8lKvO^6%|)jQc@xUB2S=ii+7R1bbAXhpgwFu zD^&J0w09>N{2RUuF}sqtjZF+?*L2f^V&6vEDxRC$2{w zfV<5z82`m8yzyGe93c+|6S8&jLptM2)fI;ATx-A$53o zC%cI<0*B6x(D$Xu;)1jIH)oKT^edLEnvC0rER!#osAXrC1$M*#GNNOY>6#<35nsRz zE$(z@lYI|OfrPt9QGByyHYSW-snj!RG-*q6zTU3(IDEP~`{cp6Q`wSflLw0gf1JNM zC3#Q+QE;pggXF=uErW~ZI}57hkqUB)KnPxR-WW1C%|J3b)krp4R90n7Y&1?i+=MB^ zR%FSvFY0ZF3Ez!_mBwI(YIQg8+qQ+!B&PmhjC<_b^OYbEx-&KC(Q-Im?3wj8>UST8 z;oK!Jtl9Q4N;@~l=gXI1X#Wzhx6i(GND_a-svizw!RF7BH93t3UVL^EiWlJB47r)t zi*J7+erxK&z_WPf_w2BHt~UDi{s>yDaOJO2sT1QxL7AAQTWH5 z#RLDGuSalo0xFiK8<(^2>4TM(1D@;N42yTKz?v-w(7oj+DB;)=_Ewc(t1YJ_ta#Ye z6LWbC7OdKfvERQ5cc-kA2dxetaV;x|Ys$FC}^^EC~`6Zz*R7U0IBZ))RxSEas;ulGewgG8Qe~gy%a~=4qyoAncrX zdoRR>KR#5>X%4q8{|U;B8mgR|1N-$yydXt_mD(?~oQWd63Qo);EnNO?HV&ViVLUT{^(=!& z9)g}LJqysCyo%L}CnF&#=QUZ^%}I+)>!e$a9jybuJ|7{@LPMu}ok7&EUY=5HC1qvUbz zW*FWc@U9toXwjnPdru5`5Eb7Z1$UeJ#xo-{rJ*ll5wy${v~|m4z=&zEvubaACi~Fn z?_%GLDVRHPB7%>c6UvO`c657p{BAtT9WXV#NU_OtMMs<;zIT|r0Awa9&}l) zT(J#hN*75<9xPO0!!1Vq*`~zY@?g5IN*)`4@QqpZ(zVcQ)?1}GKV?4i^cdy4CZRfn1^|`l}a8NfYs#5 zlZ}VT_~esMj7K>J-VF0W@J6w308@Ldl9H0(0G2>$zaM!HH;>uk`QA-ooq^U9=BQkw zIR5@62seWbn@xo!Tt)Hll5nn>y!lZ%cNd&L6CnhGA-_K=@fLRP+lWm+o`k)VFUnUf z4qKb_-FMV@IEiywV~mMS#L)JiJ1N<})zD*}<=%`v}0EEr&1ggU%cLSh20 z`=5eUXk|2MTRBq#niHz=3-z($r#(o}2MA%H$Faq-?+G^^1Pr7Bvww{i=#Xg!57&H2v^#mDW8SXjWd%|2G#uiP=6 zjQ<k)?g$L^zI2Schap?7yi znf9$wSyLZ(uO}czcT>DKK_QZmxGUJYeG~Stx`fhItD=~%hZ1AS3;g)SUd&xNPW%qK zUxjSbmO!^peu0%!!KgIPDr_p1fd6h!RI+tPvHmGyBH08VqM;`3dRXe?mUXk5_aja6`}m9sl8<`?2N9)&}*O3)R_euZ(WICUM%=8qEsmfa`P zhsO0I<^~(j%pvZ7^m^!`mWsLXXj)WJ`({|MZVnnWF_KRnf>ua~5O)D$|5Lm^vSp6+wMvQj1RYveeH~Xqw<+g( z!LJ=!eTfo1`zYrsGKRR{|HJAHqr}Zlzkij+S-j8TCF5BtND72L`dgu!?lV4-46UX( z2EFt?CeG^(#~i5l$t<{Bxs7f}I1nL3U>?Nx^9(GmWIcz6D`k-J$utngt*(1b!}8rz z1;JXdDv5!9P3|bhAQ^+?OyFVgoGH38%gWrHt|AM_YZsOTZyIkN*`I^Q9Mm^hB%nC` zLu0Sw#1?-%-{(0vIcD6K!6T)N1w|^xU;6C2XwW+PDUu zY1|IB=iP88>c03l^sI>=rw_(~KX>55{v@>QQXdZX>8M1R#QYiiuwh?v+@n^hKB)XZ zZW^T`Gq5gG4CUiXcTio-S#IFU$lC@rZ5&VW&v1R zY0$7^9qe3o5ux#hRL_9;^CsE)$Mc`)05k=6a3azIS zIL{cwe6y(zjeRFEF`}SPykh#?M12tcy|4{imz_XZU=dWP=>->OlLt0WLE+1U4-yKA zghH9EQn()pEf$gu<0$zNh^JJxm%mF`O~nOiFvdX=NiW91-+Sp=7$y!`ib>-qW5>a- za6j~{xC5qpE3FO|PBG3wR-v1U&-2rnx5WZ6DyB>UWbBp4e2y}P{_&J#q9TLHPXdwP zOfV-9o>Ox$rS~Q##^LOhLx>M3i{|aBWjGVY#mNRWnl{2OtM@63ojs*4J-Cj#6D{~|7^E}FLW6(p6txpw44B2MrBOVMVDhoQw$O1~8%kwy$8HE10K=`*s zX8gAFjF_#ZP@$Ta(5SPoU}P8UB1{(-T}19K292L>&Uwyw179QQu;z+_oU6(tma_no z7{$q+?-3|;}4eNo$;+QGzkf6U0+W>jOnuniK)dx zQ!!&~UP9NI^Tf)@*dpKY$@6&Nf6{mc)Ga;$s}D^Qi;)UU))5pMi-mJeVdl3z6>=l= zR!z{O`8#mwnRDG|3nMh~3E23-TL?=qBqx}IeZ{msJB(*pM8bLKB7cM?F?n7X;DCxA z?J;WFdwB7sib9~~Kape>)cV=XlP)mRqw$!cfyAT{>yM;ISK-S5d1=}G~RTYnA8+#fsjPFkE_%Sgb7>) zGd^2{0dF=`CUZu>Rwe&?>3i%rK2@CY@717Q(b1?mX_WGMdRR5QGU9ax@*&=NWgb47 z*TZ<0J@^O5;f?lFaOB1!ab~y|dQAy*syhb7hPI(mv3Rw+`&R*u;|zl8qak? zZOL4Uc&dm$4qenwf=Dcj88sR=)G5{(AACIwEnE5tt2u)jPay(ldt{Wb!Y>5l=dX6+ z*k6ZmGweT84>*a)=tyNJQzJ+oOvg2^{G>%e%YsTMrGg?WG5~%Se+DfX=Fl?*lZ3)! zKC)JXI!s7c$Kq`zVCe{{y}8KMaV*jtyt9bJbQ{y!|hN;_1SD zeB1oM5L2+60$lDANKjU=2$^ytqonaTNHU@2@tYW|OeK?%tYpz4U1TbWn4usM2*K6G ziNL@XEfEIOtBFxp465UtFbax!Jc}hOSD;;ojQ3XBf7Bm?y7WejaT{DKZEXx3y%;*b z$HyGb&z**&7n66;>(OE|zTNny@hs;M9F~OF+DyRFo5>_X)F|E;&rY0WJX4Vs93DR& zm&2DB&y28Z0gWpy(wsyR5VO@#h^V)4xrjC8hH75z@!l811d&vRi<3q?XMFJ~1l?e) zo{x-5KtOOL{`uoN4sHDlr%wHi+mUCHpi7P+r3W(tVP+NM9R#Y`>qYlQzmB@rVE8i5d!L>OR@g@z;%=3TO%xhc)1lvh)%-Le`D8!~K?KKQ48HS-w0 z_;P?aHstoZSa(6sDYKz-OTAce%gDY$LoyT>+Gzc7@Wd{ZEk!qL&Lc8fkA5x26_V94iC_(2Z8S#nYFBX4tXjn9EKa9e`^?&2^U;p98?W^#Qyp71D z+t8&T)Rdsbt+A^QjcrqLOX>(NzKx{tZ^ZSxWlkX6{fpKHJrlffrkhvXtqwXj?SV0K z--2Ihccl%=w&YlCCYrM;I3& z$vi4N3mNj7K;#FLB+Lh&=MN#P%OAv;2&FnngnigaA}r!~UJOnm1%6&Fu;$mLXwWEQ zn-nn~@=nhM`1SC|;!6!mgs=VUX!pgWM}q2B*Y9D$tnT6>8GN!WE4_&|hbQOQ9@(iO zCQgsR?WSVyxu3-EOO6lLEjt9YM}KNOQ;}Ij9XyWpYhM+!E4kN|#(9LWX>)J0C}ldw z9fmG?Ei{R2!(<6NZ3WaS)fBzoeH~5PRDg>kHE1yep)f-#U)4Ax7(8d6sRSWTxp`Z3 zOgy3lAqVQcNmypteeijvGDG(v~j<2GR7+F1w)`v*xn@dq0~ z65*g*fY}msYtf}eHk9=xg_=b2ia_{*yt|wsQge`jaXu#rgg1mYjFOYbe4cJAy0fGu z!t|t8Vwgz8*RwH}uUUeYZ7Ud`NgEG?VlklgB>a1i^MYS&gE}7Xqwc5gD(4cHKG}#r z{ofHsdT~uW@#}9p(V%|r_d()99nt-3Y&bkyTmVCM7B5AUnxj$a&5Q;9RXmD|v%kcV zt8{9JEE@=}-*CJW8NwiIVX2 z@q~AA5BQcXf^t<$qHNV7aI&`+b5SfNv6_@eF_erbJ{r#nE^9>_6aQl-qH->dmB$q4 z@;wQONrD_=5FH(l8|NS3_Qm_Sb?H9tT)T+}{`U|R6@c*gKtU1#NK(?``3-4 zM4FAfVZ1rgOeE1TUt4Bpu@fG9XYqLj#Xaib`|lT^bI)4FXVS*$OQ9IlZa8j7v0aY% z5{)a`mYIaoqZzL^SRadj*alssVPmSAK0UGX%-jsupkx=+{3Z@vibY#!br=GSOh4_? zW-h$C=Tz{w@L}_NJrR;%+L;a-r@??Dv&HiWNe`fpSue&mu{bOP`{{wgSVPyFhGh(^(|wq#!{ckM6=rvK`WGJSKkAU#Tm}NzmFFJ>Ul{+d3KH zkru&wW@3FO2K}JiM}&cVKF167s1PkA$SndfCl1bbW>B%Phrt8Zdax3dgupllXBXc~ zi-Uy0RmDJ+9I|UBNrvxd#G8@xc4Ck%Oe9j&qc&zu{~mpYHZ(qyHg+8F$B5no5td-c z<=1Ka(7nMFxD9BJ>r-~%kFX7b#8L3}IntvsW8s0bMxsL7j~!2C&)at1o_msXz2n zD%o=b6p0@WUWD&9jz>f!Lmmo9BFDuLlSQdNp+pURZ%yfl_5!k z852yrXU;Ys>xofb45>r#hwx@_WiwzUd)ZKO(s50nBiWwQhM&c_2y<{*bZ89t_UDbNymx7T-A|A(w$5e(|k9vx;f(5@mYI6rL`j^3SVJQHy2DJ&7X zD)S5CCddCLh~yx2afhMRGix+|L1NWwOF(NQ^fjvz(1>GY6E0?Na+!L^CPcD`;&HQpY){`J-B|QDva_bF4kgL(yGTq-ziJ|e%*s&U z?OqWhht9#6X&urn;EIY)#M~LDF?Zeoq466{R{L(yICd81^I+xF!{%AkuG$_6(GiG? zjY3>}v{DIQrFspteYF+pH7Je}MV!(ltgM1t-8QQ<0C%Dd?Zx%t68G-E0gjEIj{QRN z8mv-ZHM$q#k_`Kw)56qxt~3h90sx&byoKUI7Y=mdQ4D>Oc+>|$&*Q{EG1q96OK=U# zAt8_XXeTTN7eNAI+40eeR)RRTVkcq4b1=T9SP&Wy@tA(ApWKH|5J+-I250LU7`t#I zhIVqz69q0hPLGD=y5ih}t;S~*xy0_Q>&0N7oaEVT#Ooy8EG5t8w^p`5-tsru~)o z{G%{?`~fWAJWQM!v<mvM$;6$J!wfXmv0<-A+L&ep;o&40r|Vxl_2)+UX` zpjAV{p!4{Y81ze;)Ux2vn^?1d5_&WmkAPsKka!GeJ@kM7S-kM<1ceZYjF1Pxt#Il7 zx)`(()24JmucrMmcGyB1_dv#z|O@(KO6^p zJ5weZ-7|EVQ_Cfr$t==BiR}F0ypWPS80(PaL3h$nF=&C1trhaHwQ)j+TEp?j(SNc1 zzaKIBv*+Pg(hUxF8l{sb)A85>+RI&o3Vu#_>x1X9^WR0ddhaUcP5Tvop3K@cfuBhU z%A!JkFTaN*LX(m?nsj$5h=>ZuwU9k{v3*x8`~I*}W04vzUkkApk#h9@}5fWqGh*PmR{uYB42uXw{0q-_zMR;?S-@%-BPxrED zU8OIktsD>EVs=UvR(KS!{@Wdx|I>W<#~F4nG>4N_b#!Xl0~3CD3+&#`-=Es$iHwfJ zvN`+l^}M+VO-OE;VXY~N3A481&3;9*R6|0-`)S5LOqn%AoEcnk)>a)c@S{0M@+k;` z&LedhMPJ3DW8|DXO3?X8&^>02B$M0?spJ_06 z%DO&}>8n3StA?4+@;P-b3|(4xK}?dNhn!AR487Za1E+3}-sr|Q=LS?M zN5wyH!HWHsm0!o7*Gx$(B#UpwV0I`KS2jFjAidBbD+EIFAW2X#%YZrERt%O?`&Wdu zqrDe~51fMuGy5s7shk9BBj!%~1*0ch^fBTMWWF8SeDVe}SCJWDjK|Q?t^6(3|(4m%=ljjPBmer{U*6KQf*v3M-k7E(_jp z_Qx`6AB78<_1%$KsE$k5m9##%yON1KTpWGz>3O(ydT z9v&V-?wA7jl$+8$&L?@EGcJ}V44EYmX?-vQfr6SXG`ZUnSDr#1jt<`Vbka|FeRyl7 zvuJLCMpECN4KVzTX~t)i?`MVoFKKS8lD1 zwH?L`{uYzx4N|Oxyud^MV03Kt3Qk=#1-NKw7)p!{Aepd#7JrdKawWX#qjAL-G3uND zp-v4S*yj?_J@Ajhvo%^HL1zg1j~uoR<34Gj9H)iwNF91L8H?u85OKNeayN7(tgL08NJ`aF^xnuusD8Q%_iy|Jwg8y9-65HrUt zWjZMs`lvaAKn@t6kkG(ZTNdR#s-tzwrl{SdijsX79vXu%F~Y;+aQgV)IQQTv;&j}r z=+Skjx2X@U%SiEk8Qb6HsYcvE=))%}hwOi~aM3<=?m};Q{$R?xzhLIVfryHsQ$x`h zs9kVleOZxCQ!?Wk;!}dm5D3|paRpi)bX&2P6(fzN)%8k?k1>Dyq+Dxq_BdR*?vKh9 zOuGv5<}&|}S^*=L{9_d2Aj*}fk99k@qgq9eTu%v%j!i`EGA|%Jku{D$ua=YW&4#o| zfC0PNQ}^S`Zw84UZb<+2uz3+(XZ{Gi_0yr~#D@7J@Q>f&f93=p1l~kY!b2o#x%T+? znGKCSG*%^HlUM|0?A=hdSP2xVRTVZ(DnXx1yD(jz{WT6>oo+l6ux|^E3p+B4-xu=> zbdd|heMyd&YBU~bUcNit_@F16bgG?hmSI?UB9{Jm6u&N6j|%}ugmPiIGauX3Xq|** zW8Up;JWCyOzh8?U zUE82&5i^Z47je%&0_d~N z!-|pZgkVqhziXV{hAwWmc+Jwy0JdAlZ52C7=e%bPQi&gKZ|E9 zyCvwYD?;n~rKR;#XrNPKL~LYA)b|^VUye>ox!+r!V%hwY7(K3|vNwql40tUN~F1j-4krqeHjMlLuifR#g3JF8Jm63{0E2MCoc^4t`%! z$Z#Mj|2y;MIpY5{kq6zkFTB<*U&({;D&?M1v8FeM@=wM=Qgz!r?Z}7)!V@kLS|3b@ zWm+tEG%^uL+p2G1`>F4h4z>A-pin)!)EfdD8z&5y@D`qHTLHz3+A5Xqc@19G5w9%9 zx;^8Sb1O{=oV@TSih4cXzxOaO9<6KjG+7_eR>R;A*Fjf|m;MQZusZ(!XfsYlOoLX> zuGgu-84gw@;cf4WQbqjW?d=5{TW)})K}bj_9{4{%U?g|J3qmxBCxd%tQ0TBliHCJL zl=dhOzpC}&)2=0S9$fS3gT;l*-fe><9c#o=K#<3nx4*?3W7;W+5;=v3!ATg}{v#Z{ zu~aNLCJ;(!pi}nVC~4Sx!@8uH7rx?YM=?0DjYBJsxg;si#IZ&kYs9hgK0cq%3p9gZ zmKtKWE64U$m2mjK-6&ZscU+bk#kTF&G5FQS=G*JCOZYr7Sj5c6dEADA>Ai&t86gnP zIxZCk8MqrDb5lncpM$GYX&gUs3Kc5meSuGExP4zN9kE`#g`r`?b(BxIdLsd?8?;88 zj@B*kOo_hO^3UwYYErxlpS`~YbC>>)B13tue(|xWKJm${`MCVaW*iEhBQ$A40i=Xh zQxs)g>fyO|&!P9I_9#}&1x`YRr}829-xl0s7(6zfM@2?p@5*KlVZ{xR<-{e`=)sukWP@sFG|KVKVX62k)a4uf86&>3>USI_T$Dw77UMA~9 zqpg8KQ#NH-Yc5F8oV|`kKaCLN!L?-oTe@_t*9V_08j6x7EV?ac3M856SO%lxaAemR z>{z)EXD^?@y{M~*)!h(+Cu4C;X7*SKE5*mIEK2)SM)68@;8CSAwB<`fZ)b~uOhzXaSXi)~>oRXlej4T5E8jFH5NbvX#h|~LS|SO=e3OL3q5!x9|q{rzr`?|4%lRT2B=qRD3!We038*}7xN`iY;rf@0@yE>x#xpTloZ8{LUuL0xog!jx8K2A{aQaenkBCgd zg)_JD%a42T@5z(69dZR>iB~c$gq74?VXfF{-4L6^K)T@`mUL{3&BuN~NntVNHW&+= zJAM##wIsqWb5}&7jD3S&a7)p9U)I103kYE#JWG3zJK~!eYtUyAVw*xw9^~JPSHjS<@dONf zwSxtDP@8>u@NBfKmv(mA#ucY=lAWab&+Y3GZ|<(%`3JO~OLcNYz6 zRrAEh^Lk+0$yqph`B&^Z@E3-^`U9RVIS`(<^@XC8vBpZ(ygQ;(ue`OgxufO8ult}? zrQ~h~5?Y5A(7I0-27(Z_4n2gH(p;?4dg2ckI-XToF^Y>f@o#&A$U{IEwZeF0K$pHR z6$tVm!o$Omlw`pV?0rRM=aY0BJgSKo0%VjxsMaurm&Jn$fw(%mqQ#5cj=XS!#Buw6 zH2&B58?Dda1ok>y*y{Yjm z{}B-tX5L4S2fP7Xv^-7{BWro%PdG9{AS4V*0BMCNu(7s9^(N(wXN3pxiF)kbbroIf zPe8+3&GGZ0$q0!&tN2FEM|flyen0gU){W|dH@kd?C5unv?3G~Lyb~c_kHLNaD4e|% zgt4!DkIP}d89yEH5}L79t+eY!3Oeky$z5!N;%*{@o30cfkWf6_Y*0rK%7_D}#jGnyAcYd?#XuPAV2Lkx;A$ua({~jT z@$syLJqQ0ljVh1#?LRG`Kl=KeFszz;5F6Gm!>x!zh>3kyFyRYz#O1c~HffIHWabRGI&y6Tc;(WhfqYjj}I=$=*#&N)#4Js4@Zn zy|V^ePE5j{b2D%zaJ^VFmfPJPIkFPh=FWpA{K=h<<%^dWq98eQX3L&m1X(^=@kKW$ zM-(sWs_a6U-{e6Ie6KYsx+kZzY7i}^j%A(7lL}op>+zDw!&X}!onI?B?(y9}pF(Vc zMb9!WTDi_C2nwb9ib&4_p%h@NcA0@i3q-LiOL@1zzT-cmTvXK9ciIC1EoVHzKYWCO;Hm~g=U6wG2H37e7+(t?A%18i;V zU}aqzCGFdwcDY6<(Xla-%Jbu%3|QR@#DdRdlb)RnwTFV0!SAt~@xX!Vx;zBpHo{re|M`R2cS zj=O?3sMrZt1GrlS zMu*1UJT35uiPhoi)j;fBaR`V1_z&0aUd4mxtB8!hirCl)BqXLv7=$@#oEPJa7_25@ zsu9V9lZWKNZFKowf&{^9#}&ZrH4+COc}}a2gU5_Elc0G{t3~Gfrb;aKcEUQbD*|hq z3UJfnDUxYf93uA}boWX&= z{n4k>^GMXQ6_bFe-TadWee`0ns#uHnDB4wj2P^(C><5ycICwk=FSe(kYA&nLDd<>*&iy~wP*K@ee8jLVnEYA6P32`A+StOz#vS%n&aky|fVIXJTCFXtwRU0{AFafr zm3Xu^4jTty;n>64R=i&jlZ~CDcof5d7LOwwT%Dn{vxCOk3L5sQ5ksTVDm{|;oYK6{ zN}P-D*YlCDTUjfw>BZ1$tZ{4Ia_svvxgwn?=FkE^ZJLdS&vG|p6`4g$ToRtE{uZu; z8g@%$+PBvG7qMtLjh-MRJwRe$Uxb7`Fg{@kZ}-Yrzity+w59HvKVXN0-p$8h>)*o4 zGk|h|{fTIfoDze95qck_=b_LcJp!S-h3X=kII#*xVa^h1Vn50Lh z;2i0{RCJm_X-*6Zf5tFqUC_5l%|g1T=yXm=8XSB-Cy)I1QVZb) zx11qDF+Ir z`-(`9Kqv&M9m_3jn~Cwd7!-i;^KOM-4{bn=iW#fu%_}f(dq{^F*nV=3&~P5vPeHnw z_(;ovP0&afk|ZQiFr!LlRY@0BYNWyOIPJqAbqy8?uci5*Q-os2VwshpHY6}S}_iaLF=wa&~e<%_7|~b@@~@Yfvm-+wVU~h>p>tQN^Bk7<))L*IUOf)9Pd zHAFpl6)`cUt_@sM+<1k-Xj<@enbYd#yj85=UQNg1+pV9%owGX+5G7Xpp!O57;m^;F z&nUm~x)`i>l+6>Vcou5wgGi4+2ns0@24f#==gw_xnUJmskWyYvvHzc4C|lB`G~_$R z4OoDmejSUjL3{0AXC)VN`q zVU1A`)0f3!Jgzfl<gu=&`ZHLxF}z13EVvkEOdl%=b-S zh!`RIn^t@UXCG`*3it##NojSk)h#U#Y7+Eo(b7n1U2vEa1}{#2Ob{uoDR3n*aHK{W zQZUQol!LEv@K}B(hxvC9@|;7${9GQ(&*Cuu9giD)56Ok|g>E<|39x+uiIA@;aCY)R zySgJWW$`dnE$3|UBUC(!l`Ae_)O)RrXX3_qE)sX7V7C_1MXf~8T~lf|*BaL!wR)~g zZd=K9M`e|LTe=z_l{rGUXF0Fu@bynK(YjOB9B;D7ZWA}}L}OUjN!a<{9OJVJ_&b@g zuulyBPRbH`FZkP~C!)|IRRS@GAQP@-E_U-RZP{zX)L7BOr5x7(b_lJT(9@N_xNtEP zEt)=ypb+we0usbVG3aAuMwRqk($bKGL9&pOp`d9LQkF`=F9o<1<{UhhWRdbX<)b_% z@hCDVOe4dYzL}-ODA%GSL6EnC;8TZ%c^CltT;Zh#`dr!lZS>533 ztg=XIxaS{*dexgiXDq_gIFAx)-b>1HL4!Ui1d;c8A~wdfH$DY3Jp#MLV2V7odS*LP zk)unLwP)qTU#2}%V<_Bexr%MGis?Zg$PkarHTlm4-gF0@;!Cfu63+Rz^QP}YL zV(d8bC8Fb!yGRSrEAf{YJH(*kN!K?oHpzoZsZQk&aW67RAQS@BAi4Ot@L5jG4H5>5 zp=gjc)^_OIaXMx%9|3n(G~@$r-itNfKd9jgKT51@?AMXfBkJd7CGrLTSZYs=^F()mARm z%(Uk|oCZSUIM{Gpzz`QS0pUTv7|)b7NlkvW7>rBu50EZp5(wuA)ABe!m~qH?!O{Wd zDU(D@L_)YYd!v{`Jv6G-4jucqMTOc$h0fBCh+%h&~3#OEGnHOIKGL=HY5*4`BVu+Ec6T#2>(Y_klF!V5Y!IX z=ZPwhQWqiyY;Ek(r}ZSvUjG5yot1x=XW(AhgF4N@<|Cho2|*=60i)>Oib3mxX0o(C z3cc?#k0D7#l8B6l&=Mhu(0R}K)JY5(=YdwMRs7!5zx@g?b*o&UGX3%!obOeAJ0dKR zGFZU=MQB{c7>)}NbkW-objS3&D44l87mLBvd9F#CB@9R_TD=&YBUDyNB9t|30!Jd@ z9O*6w=M3kP1(6W!BWG_b2*kDwiaPqiPAIitEt8O8lyiZRw-FF?9T9Q25Ep0iP?&?i zk0h~D3=#*e4hHvG8^+&Fw;N@LA~aP35tlcE1VZqDi<|ZZwF6eCGgE@QADMP4B#Cry z`~klG^&_~s)aNBp-hrcz{$YW6q=yS+m>cBC#rhq^9P(m(i)-9fpdvj zH!PT>Qo?NmC?xp2NkDkJSH+N?ALFC1yTidwWf6(mC41n3Oy#v{05GsLWjz9=nA=1QTAe81MgX;aopk|=lOcsm1Q1@N@w0#OZ z+<1?37k3`U;e}`W3?aHy(c$15keth@ca0i zNMMY`(9&RfE-fOu)EE%qk0-hDeYB1&gr!O#nI{l=V|i2Qf}j?_pgP?TY;!_wQn`Ul zyK7SZHOyW+1+}UdWh+Exo~9%j1$MS}DCO1?-z=Vkb{(sTxoLc%kfKeMw{SLalX9%nmW7p9 z>WvBW8Vs68-5-dUXk&cFAh78L;E!c2QU;`@X8*V~DXmpGM@SS@UP&hALw0JQ@sQ5_I;3J8H#723+f5U(O2vh>eJb@5=f;W{nlsAqf zLU$H50a_x|CQVuZQ+!{n*B?_?ypK}Fd0%r5k-{Jz)bSH+JN~6054tB6aI!KNk5M~L zTAHH_fYE$bt&f5S3JJ~^`n{!lRAy)~mGfmWNFvId;(Vk_>+L=hF=0;c0*DMionWyy>gu2hz}Zzu%Io*Gi5zeFf$dFaSwU}SjZ~_(y0n(Ge|{45((#t zBohwK8F|dX*Qns~z5F-)_mV*PeY7Y@4kRzStvL8w_{it@I)w?}Z{e~^36(&^#mxeN z5PXsgT9OD^pFu9B-SrTIeo7OGIM_SF)2TLwy!;kM&g=nKXOmKyCGaO-ycmgo9bQ)o z22*TyPi=x}yVP_pi$T{FCrDbFj*9$+R7xluCuNZb%jh zgfJ%&nw4}v(1$JE53FjEH(LRFXBBg7gty)viH`lALD^Csu(5gU=RX2gi{82(hVg^G zz~0lF5fsiO4ih*z=u@SpL(7Aj4$V|%VsUb0X0octdr0BH#m~413Bx=t!VE1wn&uOm zGnRC1fkmsnM$SUThDDW{>2J%->rL&Q2YEj0>e zSuDsOD%+pa8;~I({TT3%Br!A-gdCjz6{7$E4dO{eK~$1d z_&i_d`!f8SR00tfHyZ>(@I~G*vOY~oSs+H27Mm~j7K7S>Y0(oT5=YzeDChnRnzd|! zm)~gvH&;72JKMs=$zG6(l|mMYiF(Au#^K?;Fl_#DAAZ@o1-ByqL{xNuxyy>+Bwzq+ zofw>`v^=DblNv^LCaa3PgA@#$FH}rqy$CH@&MB5&Ths$^j}mY9Dj50dM2w!*n_H9= z3UIrEilrMOL6^K&x5j&i_#c;B-h)jNBkuJ>TpU9T3b-cCiv{UQrgwnuBfgfC>yh(D z&K>h%4t}2`2|i|;Fj7J#5OLA7NgxE@K*lGD(ET7YYUHBtAO^`qY6vE9PZ8#BTU&d< zPFq9_C)itwVeJH+J_(_5{)o{92yM$BiAnK@j*UsV)vTO|G>y5@3!5c!qDsqyC$Ri?iH*(JO|E>{HISh?)k^znd)_g5TinGfW~u! z(7W?vhtPP$KInspaElar?iId947!pTO{XiFaR$Cd;r+xzC6JsU5Q1-#?gyzk$|4{Z ziZQdMjTjUF7H+N7U}a?`ZjP8`$)<2>;6$XlBUX)KagaEeZAC4I&(lm+$>Yg|R7~jS zreb35(xO5_mlpk2rny9yRw>sOSh?XF)N4xJ<>|$#OW}B~Ni}hilJi0|p3BAJ;d0Ac zB!x~yu)isVo@srTXQqr~v}4Z{z#ba8L5ioNDHtaaFi z;Bo0P!3t{gZS8yv?CDIKxQ>g5OB=Y*;1=~wjQxRZhHQUx-{&a35qbi>iPR}KbxWv6#io9XGk-Khf~!ao-Qmkp6jM&1 zMP*w<&^~s6#}pupfGz?z`em+m_U^xgFFaW|wm9FdqbMX0q(fSIzpw;cuXO1fJ2uQG zX{tRkTD(|ba_pnym?wYdRWu^vN{CyvwAjb5@XpRXUpr~9NUY{Q-8ARh@*s~u*^RpR zXTqY&_hb$>Y~lp+&hAD;hK1y1!LUET7N+*q0sBO$Ksfgj+yxG$V^Gfd_dlcB) z&OVg^Ar52@LeQwQRC>YFe1|iUSL|eW{~Aq{R})efvBSw`=n&{7q3AMe>vq%i(PGh2 zTmJSo8*=9B;%}|v-D^dDJKy>+^O!0R*({fIFNM*8J;{^u`>gpW~BWRS>j-yQ~n-3dbx&A_Mzd zlSn#A0FRxS>|oKG4))PuVddkaW%7?7X~pJG-x{H@FkWTk;;x>7zN{c~l3RTB*<}a^pp6Y=hYEi^Xn{L*Kc;iT zRC)}|nXb>2Sf_P=Q)0BbCf1xg8;~EFtm$5e( zCDrHJ{rl2LYpp@Jb3_hl7}5>ntKXWOTRHtC(ndLPAEUj52(yd+D#qLVgjI;FAltB@`9rFa!$k(TIcBO43mh6R0T$V({<=9q^33aM5>goWXF$5 zL8Fh*J{evCYwXra$LW&+ZUOah?~Prlufv?rj|ATayHZ>+UVBv=|2( zXiWr4wRTx^&Xq*bu?HAT+>$Pm@j|&ICd~}{`;xon+PlWkEe!YQuA3S+o}LYR-6)vM5RVB-ynLSSspsQ~pinP9D{adoM}FFVY&iY`vz z_=##v(hzIw;&$Q^E0PaC_{XBI*C_(RSzZt{-SDq;?D(uy(RS&4rWt!qXL@stU1Q$b zmTi1=OCa*v`_QKM1o;uYy7t(!+@=P$-7kabLP8fEKiHmri3Z1m6@%54y4PQ8K`ou) zC*w^K$p8Tl$N(+^fmN?WO)p)mpQlc{@ZW2EBAQH{3;**e+`QL02&COc0IXXCk;CTz zx0m#orM)!Tc8{KHWI96t4M7f^`R z;AnaJL*wmgL1Fm_8Pi^alNDKNh*I)9wvN}q11ldfM42Y5NK8NxgAnPVq z@skV7+A#=(GMeD>hrKgML2+n8hI@NhfK|@p=CxiF;{|#_R?l$dyRF%~%{9*R=T8%E z#0i*MbR>#Msu1}yD{qqeE z@(K;;i8NLAVle3mGmiIa{&a%9^n6S9dbL{L&F+b3!*=kf*&Yf@VTrESGrD=-r#a?=faG zpgCv?rmg7&E2J{PjV$ch%OxRKv|)8gNwde9$=ld6wR6`X=-KQWodG};H|nh?yePF9 z*mF++FQv7JFCSzY-q6`A1RMXRF{lQd*sRa!=a>J4HWa`s*}$@67~4^u1wSW9JKH%f zT-tYWG61Ak+iiq(MOmPHV3$$K>WZq}dQ|nVf>Cbq_^^-xZ-cq>4H0B2o8>2Va#xqA zu9Y!B;>f+M%`nCZhFM>K@oYZ5s)7Fw?p=R#sv_tDe;62l4h+puRA_%IY{0C&bqU$F ztq*?JAed$yZmdjzrYQXbeU4RH3FX0WC%p%2=gQX++f@1 z0}I;nlL47dJ2>>;F@k*pUM{+LA<8ToBvz2zcSm?aXSi-sHal2n~nH literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonfail2.png b/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonfail2.png new file mode 100644 index 0000000000000000000000000000000000000000..0f0b1d4f59fa046156fb8911ba14cb73ccb60710 GIT binary patch literal 73351 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 BEpq?> literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonidle0.png b/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonidle0.png new file mode 100644 index 0000000000000000000000000000000000000000..dde0adabb403485d9f92830f5ab95a4b4396ad83 GIT binary patch literal 68649 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 OK*OB3F|9W7i2WDz654kF literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonidle1.png b/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonidle1.png new file mode 100644 index 0000000000000000000000000000000000000000..7295e95efe9c962cf5e5cee94ff928241b81765c GIT binary patch literal 69329 zcmV)@K!LxBP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D|D{PpK~#8N?41Q* z6h{}w|982#dq{|2!QI`xxRsWo#i3AIoVH&n6{tZ4D^lEx6|3Ox?h;5s+~ty-ee-s2 z?=BFzBzKn!&5usAvuAsIyZ?Ff=FJh(sdwRw@4hgdbbX3T<1c{|a^D)xSkW z_u`{%p)PC-btNmh`-J_%-%<)mUiW_~=1Hk&zxfD1B8jhVq5Z;NVVeqxwd9q*y06jY z)w;gCdeKo`D1tx>e--L&p$K)|*HF=g>cVq`*9hChtIz+>QYzXdk5bVt`RIzSAh1GR z_)CQZSSXgP&~tU4FVyKVE1jY%+`?y3A%wpLMY!nbudq#m`f4Gu6^dM4bobF~>3O

    8CJfgqHE01HJBS}KAVQxSw#5L@9f5@Bog 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%0DW8d%49(wA_m-Wzf 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 K%$b<;G5-Nz>y;7! literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonkiai0.png b/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonkiai0.png new file mode 100644 index 0000000000000000000000000000000000000000..911378cbe91f2a6fbaa7d2ee1f8e71de3f7e2d75 GIT binary patch literal 76964 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

    g`m? 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 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonkiai1.png b/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonkiai1.png new file mode 100644 index 0000000000000000000000000000000000000000..f89568bca25c2a7bbef738d024c2f2f25ac398e0 GIT binary patch literal 75434 zcmV)~KzhH4P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D|D{PpK~#8N?41W( zQ`Z;Ae+glSz4sIZ#EpCJy?2+@E^DpbwXLmoSN~nw+Nrj7sI~6B_a3O=Ku|&U-YXE2 z|2^;J1yDdn62S89=a%_h8D5cGA$zCLJN;Rg1dzTn}nPM-HJ79)@)g`QSH-l5`72N zF$>i(`gek5PmNxW#F+$Mu;NM7Nx-vNi&AZqsOPGVWwDm2YP3(sN$jap5X8O(EzlzJ zEwu3HLN_*vI|(#d=jN=Du-mcb#F`6h&aCxf&6hQ@*aa~lF|Q9K*aTVUw4KM=P1eF# zORCUPStG$0#9naYw4h;HM81U<9!=<`CZQ%P+>|x4)a_X#VfSFoleJN*W5RQ_1&KY0 zegcfDvfeM?9*9W&)O~4NbuuT$H=`yLQns= zMd&p|i^#XoT%e1Te8Kcri1HBjoAL!Lh}HdniHgT6ZVUeDidL=g_hHbvAmOG7R1$dd zuanhI;@*a}@vI5{bq>1!4fGA_BFHU{?#QB|T`@mNibgimNBa@2(Fs*Vf) zPJqq>oex3a$?xvNS|DqaRoisl)EF8VYFym!rv|YnKm2yqF0e*cJ-KkwSj%CJgq|L+ z`2AX_2Q4DsLQ{Y)KtZTUqy!PCMlJ_xG;vt5k=P4@FIe@2wlC)UMW^C^RonFU{w4@? z#lJ!$0VhkhVk5B={*pVRViP7T>2>HF2re2&)@U*{n6+nFBbSZZde?=x-;W^nvJ%p7 zHT3!SvqnN6!5RsD3Tq_vg{;xz!g*?;9<+#j3ypyw=oLksnjqXH;$)3dqmVxe>!ZIk zfk@l*mmVWQqvs3Oye==~ilS51HvPT731TIPvLNb$z*iJ?p;Z)g3R5KEqec^z)M#4> zL!|Fd=ZT_c$ZBuP+EmtBv&IQL`P&88Ok+WsQ!JyeqK3X_C~HSqBa1$UH4=K7kR?C8 z@co4I*g_p>5&0Ihjc{?15R<5r*w8kK^Zk~3MNtj1sU+&+F**5pJ=(V^fbH^+HrT^0eq8z0qIZUL=IH76;NDN8j zNZiTAL9Pv2R7A(=yt=a1k2N}v+_^I{G*W+$rUaconvgA1(AVLh??5M&#QgwkcUhyz z8yeM6heo3p>d;8+>ABSTwTOKUw1|8Q4@0;(DhfIY2Q?D&`&&h8okX3QAnNp&1YHny z!Q!NCdOdotY6YEK4SHfdi1n;sVC>BHO(D@YghY>;0rbTZ80$-5BGH41z8=gC^kBxC zwSfUFjPzk*pwIp-i5-0f_EvP=bJxDAsba(VZT_!<{ZX=QJ%s}41(Aq1Qy?{6jFZPU zu?H$yp&EnkNoo{{qEwW}o>v129W}D*$;}~jj6GSS--EN@$rs;Lq3=p@^`&xh&8rav z{2FT~StF~S7EcK-9ARR%#b5srT138u`%$S}wSq1PI5ojSr&e+Qe&4zv>Orj0f*g9B zUYn>^)WuBD4GbJ$VdV!WsU5s6tT0(A2Z=pSG4zI@5 z#M8JMTa1`H2br9Z=Rmgz;$KB5B0)kY{3WxPeh&%V+l}X@5kq3=1`|_vSg_wptS9{B zJSDOcWM##pn01~?MIray?u)!3==)G37u99fNbG6x6#4IIBqO9nXmR0GLyO3_AP73O zhY7l1nG2CF6&oF=zk;~?u|{qK+NReMOv!3RowL;S%wT321Q#0*3~;uGoo5j8JC8w$ zIpxdJJ^BSXi2H3J_D4q|FY7EjqIN4ZDuUowA&QmIwH2?Y?*e=K_VBinqOGkV8%62E zP%MSMff+=GtRvQwKv|dxnJg29tfR~ommpnViRd&jk`i-~nsNgrMR%$bd$QcOutqK% ziohYUCs!4@aD+uvG#qHL>MPJ9^8Y=m5_NL1RP>*dVAEd`Y(dZktDIUzKRQKmP_zp@ zhhB?bSGb_66?9rQ&n=;s7{b`JHLNW>(az2S-8@^N(5EK~gStVnwlYqJ(o&AQKok5(7EN#ui6ErO~f!UZ;uX6ei+`qBg9>2Kw@mc#6|UREs!V0 zLK%G&d8wDMKfMqUckZxG^&%5{(seZ?^z^FaquK_@Y%Z3V=mb+UH<%gO!q>_S zJ=~lj_6$K@$3al&)5mChL`ntn)_j7s=eD3!MnN7;7fai|=+@O0aq)882tNd+g51@* z!@{CBCbaDYkKT{N#Ld4>!d@MvnHkW#x&hbXuVK&K1SBUOLun})>S_d+)G^jBvL=W< z`RmDr(~=sY0xcr{=uuJBNr(keCrkZ)mrGS*E;O>vX)z6@rmdB`g%FZk>FHa-#MA>8 z=B{uzvw$BH^j3~;klT5q*ta8!>?uE1)59nu9>;!Kgo}4~GI^jNj;4)BY>U9)fpBw` z;Ox10WT%tvX&;f;27Lo2qTfsZgQ-$`Bd)^a804M(9jimnAR(ScRFxpHrypq@YqW^! zHf!Xb(~}Xq_Y15IKZndT%7CCo5OL}OC<=!njD!`l zg8N4B)Bh)F5&7na@MDp33XKF^aKQ+cIyJ#k7kub~pwnN9a%s;RJtl~GwW7|4E{Y+M z*u&J!8+N9S@U^x<8xIc{djzA{KLjP_lpdw|VVZOsQJa6j?ue6+72HrAZ9J6h=hV}) zg}$K;42>*cWGsb|krB$|a>(Untd(%BxHto{!h1R#aflJ%J{FVae*<&l#+e8&x*Lj< zdskrJ_5CO=BJYeE5^~CDxsNrVmaiQPjz-?!p?~k<=kPT-5bUVQlIP z8#8wV*w~_@uOF2Dy-?h`Q=><^)CLlBvx_^Bn;3<2_mYv6mxml#K1#}RQ6kHPyd<6J zxbEZIDI^Biz@2VAbLSLc}iGeMWp>{_Gs-N3|ZU$O?FbWD$Ll8t{1ikXKzR0T4olqvZ7H^L=iDf>prIVaToiJm>3iQ ztDr&9_v@}Tf3}=`_p-~okr;6V|3pM1BjY$b9W}?(*My_Xa6B{P9awmEP#tMHDC9+m zSo;osKDi2IY+D5hJx#ol`jgPp1wpH2sr=x7PUu@izVV=wUQyU9`qfF$D~dY#)2WfD z3xZC6>8_*H0Mro(rgoLWE@7f>XyguaD<626IiRzP1DyOiBftG1l$z2@H4nsPa%QdX zLD}82*mXA^cT?}7FrSUgm6hVDn+y^IFSPLu!DRn75cPW=`ue(cbCty1Ld=PuuqpB` zGEz^l3y$=y_UP%mV&ULvFd4N-eStMKlIN_}@q(4LFMO?R z(bL%x#_nxV&~9*}tsZR%G&yQ=b{+QKi^0u=B;;gXVt04b4RI7nEaBnW56`r1198te z&^M!>wDFMLID#t|)?<5EC>!a}ykzYN^qGhmFMkh1eYz~0HrXfF;EN5PB0rzp@*E`e z)c$0RCS^(JX(1H}JzYHi388NhdF>;Jy5L_YQKweX1w(g`AnIhLlL}L#O8UK7BU8IZ zQ5TCP%u;uPrIj!IY@N{8-3^8=ZBW>D5M-A0GR+TKlw@&sEAE6H#L@UPq$Xd1qO4Lx zMU%wBrVmE9b46g=+0gf+X_2M@saf%%@3G?KF{GwYF{YaJO;6t)9}Hnu{K)rJN18sl z=Qm>M`cIIZO@Ll5)+OCyO-m5#9=7pG)GxAE*_)s;-G0T=rmS! zqhyyBKNdY_!KiB2SYNf<;e58FJlJjJkPbg}z1PH3OYM!D1&-7Thp`s0$W5 z2|5WoS?eV16ahm@KsnT@qJ9k%rXm=dxWU?%MBM@XU0k5=)S4G{YpSWy{E^12@$sL4 zSyMlRfg@d-&5NAygIMc0@<-WfE68+|t6P zH(u}B8OEbNQXOetCvPMEr;gyudQb>XD$k8J5bwd#L zivD#HcCyyVQYT>-Trh&LleJD(JDEeBStH>W{`~_;&Aj2|7=R(3?yz-h$3%TFitL-8 zB~=?J{4xL7Z`crZ52;C~n8-A8410YL86l+GcuaN-glO<9s)Nmo;)oMCe&J6Xy?lX< zo+zwbg~%8K`i@8cZZE^go#yIJ6Y_4s0;Dxf*Xd!y*q2PAc+LMN@1r-Q!n2^=;UdGj!xZ?-RDWjjhlXmT0=2S zj0BGUg7tTj5O?o5lnV7t*DSHJ>xcQBf?+!5Gu4siO-Tk5_!En<_C`9A-xWi`PNFV^IFetT8qIo=f1QpC_rL?fPH6)ur#hW$k)9DIjGKT~ZRaE3{n57C z39(!Ts~unCuj@AvbMF)sW$Fbh%@>grgLiA?Bs ztB$hKmt|MHK6DC9`#r}*Lv^qjAulMv?mu6|!3*o5P^znYQN#^}R#I9^a@-3MH`EiT zpVZvZTfBHNKK$@Q)$V^1LD&UrU5J9AJC`O#$ttIrPFm|v>39QJqY%fbtdY2rJBB&{ z5_VxCmV;6U=t~S)t03^yp|Xq2ST7I`!=6<=^U=lf%oZHq@HPH9zZ;nuXQ6BcIofN3 ztJg4$ZPf(^0ew_Q9t}k7nlzx3}Ou-553O7sk1XyVDOH1)T(LqAtXSbMZZuGj)QPjA4*@XhRv`bZ&&OvS)QhiEjMia3#^GricVMT1S z7yAE_$O~Um5NJWzD=to|7{4wEJ0<<1-kv%GqLR3ig>GQz3~Spq@UgZ>e^-0>whl(e zX@lU`&H+LV{nRWnJk}O*St+b`e1lb?mywWg zkliAh$%5FFFftr}`u7i5?Ixz~u(fN49!`$vAJi89V@IHUUr$KcD_3_q3z^XWvv4gIto;=Q zITxUB=!XTPW+HRs%c^HIZ_F<4#mN)@;%wwO$Ype&JeoMUjleUrm%#iH4Q;(2`PF?` zzVkOECG6r4h>S42?F4jxb~%j2G;P()xN!n&j<3YU3v0O|=qkt$|0`?cpTEf(l@X-) zd2-)0Yis^d7I{J3$pWu9fl9(I_}qmFR1$ZZK<&pGJw~SW1H!H^F@Tv>2lVmw!>A6u z&}POs1az>4DSPGGp2Eab;Jc?^!HN?*p(sm+o7)&no%?CCO7D9=lo?X|&qw&}(s|@& zQ3bX~n;vbaV)%}0W1qXb}7w3rS4>QOUdNDMq+Xlx53BXgLVN+C5fgsFiZ^kfQ@l&~gOptMwh z;?h#&$nsEBl!21sBq$X$dgpn(+bjeBo!q{}z#Fd&oCe!5 z??A7)oeOy>?(Ud}pAH{EVSze*1_}Kp)=scS@$-V)hOGQ%+oeBBA}@$L33bIN8Da92 z1f9g38s&X}L;67IZ+L}Ivjj>6pF)|m0b`>?fcxc|JM2v|FBIX>98 z6M31ZVPf71uaBFJ%zjU*p3yw8IP@D<9o&tS6e_FPH17ejIL)mB;ppUmRxT3wD#b7p znK7Zahr+@YWe$N*dIa&6v6xWN1w? z{?Bn>89qI60NLrsxLy9*eF0|Or!g_%4mBH;x6WeonV)d{)OwV$(WnZFviXTMa@){^ zEUk{E)v(QW682FLdHV2zdqs$lA#o>*orIl2AYECb&_{}np~vapJYcn(nze$d)Rpto z@ev_n`1*~ zDi!&;w~>~Wg08Y0DB2EIJ)>!2dhIk0{Qfcahi^k!DH%CU2g-l$<<=k1g!Dy^o|DjO z&|Fvqj)A^!Z|FPtLL%1ZdiOfx=!eXxT?o5%4I+sJ`gEQELw4-Hio~ru%yPmJkrc-a z6d8&Nqmi3k48OL0Akn9(ujYf;s~em%PT+EU66A6&y#{Fm&C!vohAepU$y1aKr8{Vr zE}OaWg#TvP8)G{s6(}#_HSB&lcIKF%A zUF_en9^W5Y!G${>zavL_@*!>*`a&CTJoUp|=xO0B(R&SgrZ;A`_Jmks&-r}Ugx`Q! z=t0#pnjV&H)bab@-yrKNV!Ztw)Dv5fnf% z7i#Fo9>JOiYh*Oid>-{){6)~rXpSN;xLW9>lUs&dGW27RrA{?ZDR<0V)+VtgtVHE7 zFff6G(?C4i=XtDL@G<^8um-b#c?te)t#JHA36}k~61O9^K&=7Zh%USTIDTF5q;Cf}xD4fXDLwRW%t$E=qjWIM3q7BX zc-*f8tZX`}j&Pbmb9dgXk*k-CNV)*XsBCtc*_=LbMeCg`bDB^UtoOF8QJu9}tUbXR znObUbr{c+u4udg&@LSlqVJ*Jhw+v(E59C50OUr;ghn0AJ_A|J4Zwq_22;JNF#nji| zfvuhTCA3E0@vYd+;=;wu(`fMct!uE|`6d5g6G6IV3BKLE7TFp6WLsmSgg)%-hTzSK z?_t7QD`9EwqV+I7LnC{LB<7GvOrdNMc@E{Exp>0giF1yXf7fz2t38R)5)f28M59fLWc6l(2UP>s$*n( zbLuRS7{bYUC|()-D)#L72cKUh^mfpauX@e1&w3!`Iz>Zn^Q#$rW zmx0uQYad!X75u~;wDRVci5KUGV|`>Kj525~M&l#-YazZpyop=9)f6x^X@lt@k74dh z%VF3585rs)-a%>O4LvYM0JGI5SqgyeIYR*%@M5m7LnI5^chLGsTZXMOync;VU4O7knepOYg1UG z$JM5{y+a?o)Nc;9tX+*yw|#`Roh;NT28cV+3cNk>V=P)S0eJ;yxnp_;u6QcI9n(L5 zird!}y@$KvF<(!$MRY6j6buhh6j$Rx3!Du8d;>q6-T_%brG!q6gb4Od!|=|SS!nC?bynImmq@_~z+&vl05HF7krFQ;#aRXDDh$aL-U0M@m7U_MF?;h2WKe zbFu!1l~}s_D}?lRtTb_|WR~CN=<(18M_8V3XR z`;KroftztN^F!zWiG?{jS%<*86&byxex*2k{Tj;lf7$HZHj*~|G0DXmrc!biaFD)G z(Ff9>W@*hgMUkfyC-~o~*A)EkLi7x!{v#`1?Rz)3Y>kB@UdPHUt1)?A2uw)&D?rx! zwadA9YwSFHxaV*6YCQo8VSK8M4<;?1p*m7G6zugcozKDgxeJgOUO&rM!oEq5=Vrpy zje>*NXI5ljPf|P#vvph4T6*;;R-ZeI%v36v)3{KfiYNPYK=0nKLC>H3zKsV>?3y@q zgOx(Z!@TA~-)At)-4J^Eyc++9qfmVNJh$JRuxR@;@ozD~G-w!>Xm_%t&&Sp!Zy~)b69L`opJ=Re zKNn2(v4coL#sUzXlm*KZYq)J)K<=S)C;vk1J$2HiMnh`V9Zz@mg-`d7pyx*YdlR7N zV=(Uax@xC|N{Gbp@E8Lx-||(jx1)|C_R?>Vi<;L$QL%S>OmT6B)SNuQ9Hck2bAQ}? zv#{oylE~A^qLW4^kK~qyM#2Ou<(i?{Q1Z=l0&isOh|MABe}~^d4+9fScz3kwk@_?8T&6DLpfgsJonr{jsUs2#`7*uhA6R9Sx zomTtb13Y@-&lRgN^TWw7qJFdrZrv=zTjO8C7l+oPF!vI7%)wpC)$52=nc4pPf*m>%K^=U$7UUtix@3yq4N zS36AiaDl{-w2)9B^uiV#y;F*lDZ5d)VzSjenE%AfSbuQ~ zXO$}1Z_vtb5?<)u1AZbOT)vtIr97K`a|4X@bH{|wYA)gC>Il5pyBE&iJ&8}3e1OAi zuOKxY*!Ih1?2SIdhNMcoKK%)F>dA+XX#)3q@F7ll{K z>&7-!^sQ|$*0UWfttsAvgS3izFY?*ZMM~>-1Q&9XwdNaBegWyh8`$TDib>Bacc>cNhIC*meyRh;&Yq(pR@t81tCNhT4$DP8v z%!)t3J>SluFNTer28-&Acn)JjVC?&I(Iuc4B4T#ot6#sv#wB|YbM;5aWJa_CxP!vuf=_r%Bnd3&!Hr zo;}%Xo8!dEOU%L+!8_OqHuMu~7;|=3qzeA0DU%my?aCo5e#eb)cEd%gj_4i-mtmOJ zZ4?XwE2VmEp_!wvXNbxE>{b?)2S$|0GBBmJ1qSpT14C1?jul8vKY%6czeLiu&r#Au zg(^pt?@#;02qdhR~>2LQNp~#WLVpk$IZ)SF(@iD#+-38F{@{L*f!8cFdh$d^z>ssUmfhSKm^?)?w zp;QHwpr0&xUf}gj@ZG$x@zh7t)fpr2Cn*hhY0LtwJ+>4IIaTLkLM&~Imj^!%(Tv61 zzJSr`wTO?Ua(--l<=PcpCOuv4$e+@u5K-U_4@Bv#Sl-LhUg03`TD^ek3h$I5878 zA>A-z@iM&HIRGx66hJ59a!9Q`w+@HCdl5y&S^Sfm5c*xFVVGYlc0Z~$oHocePm^a& zI&NNS#h-iucZU(U z6S))XZ--;gYQ6)}6xX&KKdA3SbBtt`HYsafS0YbenobIx1UeZsaY11el&XMM zPH+M*5=rpY6JOxDZ|787a_`p7B7FSV%h+_8RMDx!8)w3mRvkavt3QlA>fhP6&^(ZIn+|W+a({My-c4kNomTAu z-QIc)ZwwuQj_oEe0id-Z3fxK9j^*FYN7jv|QKUw|Fn9>Y`1#&%&C_HO=@~7K60G_E zoXFD`7A9KBcSXG)#lq9f3uTj3n{rF1eux)-o?m5(0^(+92A0o%9qTXc96n^IY87V87Q;L=^;J#>v8Mg;bTN&g2stv8p zk;|E^dBHc|sHz9Y2BGj8oh(L*pQbhEus(%@P(Lrd|oGYiu0`pu^a=px5Gu z|IZ+4GamMi<%^@jvT~3RnW5U_X*IM5UVLdGCUzO3&V3^*yoPVLE=R_ZRm{F`8t8YP zfZ?qx#s8DOQHoTWe4rkR6oypnKO^$=`t*ebfv2-WV>hzk>11#=``Gs5G5^nxt97Hq zTuZ?(Uwwgf=fc#BcHCV?V#1_($ZubLtb5V%Z75Q?Bs^?<&~-`=&I;EQV)j}ihZx|; ze^+C?ts4@vqw5`QbNNUNK3n}aV(w6!KYMLGM?5}i9!ktLx004Hxp5AcQj1i3I!0uK z$^IUYI#j)&t%XMuBcG1w?o=LYoRJZOq{9!ccy(*zCiYCQKvtAT70V zl8{;3_k>%Uk*d8G{wt81qmyqZF1(p2F1UsGyWv$P(Xe-wV(g-?@od-L+}sUOTyPc3 zcCSI+iPcTQbtCEZ6neHMFO3=!bV|WOE*rr&-(;+LZHhd7aSBwdxZsK6;mM)J3A~Y! z4L*DSdvxi}FX6wRQaSL)i;J=9N-PR;_~HXb#vSnV8_SUI{&3%VRN76|7VBAtj@4>e zKO5GK`FWz+nEt9DAO4{=XYuhzpJ3nBL;OUtNQ$BDhr;5i4^_|7EHNbsC@SD1cXSCw zPYm*LfPR}wrPW%f0vVHrg4{&pMI1-rnZHnS;2RX~T!fNrvpaEM^3h zLaCHPp(y1W+gFs8uw(4FqJ;e`5wnhMU}$IsV`DQ&r6#a8lEBAU0zXSz=v%l#;n)Tu zW<}EkC}&zW;#|nhzu&~_Q&iGY$)%KDws-{s`cz%Vfk;i1Vavy_W6k;f?0fTdHKmr_ z@#gU9FzH=$VnJ;q%gVs6&9CF|slQZ5xFQTxy_|;Qmsz72P?~t4N)(NF)>%)pc=2L< z_@PFvM(Q!>dLo~@Fwsg2ot|LLj5SW+=k}Y8x7IAGcA|CP&xf&i!*;~q<9*aL>prXB zGmwm&$?ZQ3g@F+gFP%be&MkI|i@s$Q9!12xGsw=4KyhIVN@VE}>6xHZmcd#gGgy+C*kv(+&t*biz*;^#mIhf78|miV zMpnj6B&J-)wfJxxy?qJCBEoSd{4~y=*p6!_cOd`9QP^i(gFGh{G6PeHOf7V3Y8n}s zu+MXeedcub-SW`g!UdkaDi!9agsGVXPQm>lz7vHTDaq`67oem#9-&FO=%!x^iAUw! zIJ!h)Yy!{3P@GN7WZ#8bj7g>{c`PW>gF^pkoI({SNcA~^c9UWK%qRR4YKhOiL%0-6 zf+j(KTi&}r6ZkXg}nq8$pa z{ezR&E;4bbW}K`gD2?#bK|P=!$S-4Wfu~4RhJ+1o;+IR;kQ{%IiCFpXVrJa~y?gaQ za2p4-Zta19z}E2bZ3`#owyZxn;MQ$byMKyONZoDZJ4rV!A@Vv%DmQ zbprR0lOKiKsaJ6<@&e99-9f~y^T@ij7fx{}p~%faiIW$TDSfSqewm#s?t~skHfMMh z!$|Cm9-~K8yXcWHx73Hd-vEfBZ{uci0{eH&U@MA4XmTOC8D*627a%ssGd`FeEg z#fM4VkDT>;tDaws-_KuU7ic^?B1Y#{;}AUW8z}2&1zO1z$Sjp3E#(HY01{E`;0w{2 z+h{$cd$l77Z9y^c*TW02dj4BjcIEo_ zlo>wcN^c<6N7vx@6Z;Ssy zZnEyBUcHgkc?|!AdQcS57u{0M;6h3X4nU(@6~yPQXfn%tjB>HVUU;7NK%K0%<*K~t}qH1 ztUB=DrHq+D1y^=p)yhv15l8h->An|n*ZI?(`rz?dZ=&n0DR2t*Vb^8-FHHKUR7TRm z+5nCL?(po<5uHa3Mz3LG(Y1YlbT_v`J1Y+qirr8svVyFb3U}~1pvccp?w!QU>o|Gy zB5qtb51XVbkl3($#hh8q+_UPB%*Ym3!p@+mFq&P9Md)ki1&d zc}K#AEXrOdA{{L{k`o|Fk?8M9`Rqmh`B4^K-Ucm&wjIkvsFWUvw3 z&SeMjo|8~=n^yQP8b;63t|r% z)ctoUlNTXv%ZFILe*>~Ik8{VwdJgE`dL&+*^b`iaF&~aTwpuef=n^+%CYY_G3Ea8_ zqxF!1=sSEIdUY6rE+%$RDD036d&p!(oX~TyQAR<*ZCsAMh|sH{D7tYP&XQ6no&DLc zynZ*0p41Ytq5F}NMcGefaI^45r;)WY0O%dX!?1PZ zgy$L-l;C<=Dsr;8q-yjFlCev9L&!g$%NqISY1q^#6R?^RdFn#RJ{Ph}QeGO0bENP| z?y~)2^1FEA{VCO6#M{5#gD(%AKxXO*Zr{smI64h}0j2g0k2E1mS?bvd_BnTODF4kW@vqi>sV&r^?<|E^uMqScF&f?54M0z0D;VnABUjIr8D2^#l{spJbjs} zCR-_mFz1Kp-)1mAT>Aseq*W_?PPQCNKm8xGc6m3AzM((nb?b?o-c-w5Gn5&d!O1BA zhPg?IPKseaLjmrk#UnE^5nX%qfVqw4eB!AYz=59*V&PvuBR%CHJ0gOk%LvTx(Gx{e zK82ip7F}X^JB(Rkr;(kh`_#e%0^7p9NA1F1A4TNHe!>sC*Wz;I4$iosL|t8&_5SuV zuc6nRsW338#_G{A`s_|6!FOkZ-(}cngaq_PH+>_Ro7*#$az{ZvyQrDftSrlABh(0- zh`x>ZJJ%496A7h9JJv~YaTT>fnjVQ`x2~}fLjlSpu9z@&YPCrTtAaX2>$V+Waq|i; zBxXQSmW}+}J4n;FLK{;ni0mq@M%4+{S*f_3nuCHoZZ$dmsASbpk1kmAjTTO+36ZCt zhZK@RC+O^wiAr}h4OzG=__GB+q3eL^7d`y-@(=jw*glk%GAoXFK`eBf@gFp<$gD42LX&tC{oAu)H1+rYE#!Imh~4})mYqC^jI^Vy zn^!<;*%dE!9fT)8_yXQtylZW}>KYPUTNg8U^zMpw{l>swVSoTr1EeymJ1?)86ID*^ zGjHNVOe~V4t|Bm>8BXr)n5F&j`K7Xg9Gt%$!CB!7Ju6I_@&t^G>ekuOoP&v57Zk+a zL{tKWNtYlsEfMzm#c&K9#x6eHcAg@)wkW=L43V*w!YOG2ES0s2*1XO|zM*G=zK9_5 z)N={>X~>S?j8sIz{{a^Y9c^w)rMqDp90Y!yeh?h<=HTQy% zC7-Y4zl5?xhGT2rz>jCPsk43B*bc&nlb*zcFPFm3t(rtlO%N&js9k4`#rQA2!+Xyy zz@kCp(4p;A7@P46k*K8KwObppc=sA?_-QU=N4{qr08J>@fO}^)Lf}&hmzLxpJ*+(a zNL}y>w8HRSQxFiu8)%e%eAAV1$PX>!_H_w$ZUbC{AvLFX6Ase90j!aCgT5^N=rr8X zdEeepkta{0kSCrdT*;EB3{;%J+t~zQ(tGc7^--&W99KWQz7%mu`}qSRb4(BLMP4WE zS$?H4w{YOt*KA9{4V|a9YlA6EDBFT&putChoaR}0tn9o|DlN~z*BFS3G9fGGOC;(N zD=T{_y#Heby36D^_vdTa8@63lmQoCV*Kzpf!~db@OHb+C_pT|(of9}P5Q9GZ81F6o z8jA)DM@J_1`bK=VND7Gzz40$T+q(&;|9KImXa44nSA~Iv6&#FtUw&Cx4sxRE??&u2 zV+fw`@`kM=KY3f6ABL6TVJL~KU&ql9l-*~;%dOJlED9$l!{EUG>gk=4SQi$?^p=m;_4FP9i~i4W0IWb$zuVftI5h@q zzn;s6o5`$$k)UJy0T?^&NmyEGZfIx5e#ZHIdvmS|;!M~tC^*Q+{5KY|(j3T3$Q7nr zyey1i;I6i89(|Mw+*VvIiiGw{Dvzr(L5FNVz>y6Ij0EYR=6cd_8j zPw@VLk!a;VQGLA^=3T_rQ`_+4$vw#4@;YR>d zZ@+^nfez5q=Mx>?jX8vvlRxM@W2K>)6{ffH;c8T?pij`3H99$D>CpqEnL7Ps4N*7h zk?5BatawuM;jD31yq{NZj9UC^wG%1|_Cx>q^>@h0;#YoB8q&vGxpM9lZD5)hiQkvb zM}GcUZr{UuBxbeijzKfatI2Buy6Ad5`2;!zlLaegL)RRv-}XO9V|AbTQ6M_Mn28RB z>go`&0eaY)H)=vGU6Z`=J^X%YKX1V^fgj)DNxbvZPq25W<|b(7xCc97$Y-Bm(M#{* zt$t(R;xU?iZ#J-379lQr8^oAKA#!?A-jC~2g zt@#}9bkF^B{WfHckg-x6@+53*)e%1Q!&7)5iM)`4m^4$T)_i?MUa;cnLs#_0tK+HW z^d8N6cy*8V0K0!agYA(NN=k9tB6JAqjG}=ragS*l>Ah=M{molQ&pgEK+c^)#oM1mZ z{_SjTU(o;$dcxjWHH0h(!#}@#48v?4FGNytCtvreGYky9VQT$oSg*Aqf6v$W z{`_IcWfV0hM&H(t;e~I%f>q;s>U4r{Z-0#a>T4{XH5X$;#>2pbU!6`aoMWLI@a@)> z$l3Nj6f)K7UWTE8CG-t>%R45Q7GpIG-@a||cwk#D=MRyac@o>gPN67+<~?m3``AvcRJ zb4SaYN4j`vCHtbutxH(-{X(Q?P$(l!?DoTR!7g}V&HGxLfTx-Nq3=D7*+CxaLJ84{ z`*8CQ_MWBX7rHKx6y+nM_#qQvTEN`G4GK?n^_)kV;^-^*>i8bW3TS~36C9^8SorI= zu%Mvg{|I!e2aoNASHJ!qiw2H{D_QkyG)Hq^87T*`^!Q$cZ+aEQiF^iBk%<+Ij7d6x zf`%4HRuS^*Jd6+UVK2^yC||yH5{1XUaXnwGGVWSHsq6n-#yB`O4e}>X1cCO4^U}nY-bL1NG=@}Za5!CRR@buzWzETR2 z-)_c07RJ}3&=r!lLz$*kayyetgL*Xj!a8@^)JN&)-_VDo?vjQeWPVe8AZNvQ_8))= zAI_;U9DLvRtB{*>f!n4$@#FpcAUD>m*+T^~R{i-EGSYd0r;x8_I=8~BKP-i%z4q@w z8YOh;WrZo@pM|G8O;xZitt=5MLodQ+&vI^G7Z|4BZItlIcGd>W63}i(+7u|*xdfrn zR4am=YvW)neEBo@wEhp7@YT>}LVvvS*|&JR&v33l2tjid$F6L_?>l~iI5P$omb^Px zp+UI|8r2og2DMVVjFJ-$;?$*0?7ek2f;M2+{bYaN`zK&23mb(}(#0V}+0>wtnxn3F zA$=$kbNY}}^@)Zc6j4K>&+&M_DR6M8{$-Qn3$XqsEkxoIBH25Jps)`ms?;3j=XYb} zKi@NfKfvu9ng-(8&TTROr!QgSth*bKk_e8S(-klG?FXq9jo84&P5*lO6r@*=ar@dv zFDD+wg}M!&q>kI)oIH7tHpLMq@ayGEOh8iEmo~?op2N_2B<195fv~VM#mJ?f;lp8L z;Nm`-9bvk_?)%8woAJxF^Kh`|>_U|4X;$kx&V2*@0^4yBbTooKd?O690~9u`JM;rb z!pcTn)P%k$WdRUeHl+U51eR-_`iMMzIC9lcuS65BG&bUXtpLyd7`b3}wZ3>_&!@j5 zA(b+45@HPXbke9V-tz2r{JME1(o=bXr((d*cJ7MTR(=UPC;HgBh4R)*7uDsku5RwSpCvVAG!680>m<`#llIhtT>@3C-j<`*E+ zT*+@YZlU-t<+0Zd`o3Kd>_kQJ)XpXfXNL^HE33YSt)n{d zM@?onkey4HoOZD^1EziX5e5hKVeiFHMqRwMAK9BehDd`KcU9yRQuw59CwtA!9heC~ z1H4BU`LUmI^kx_nF*!FFLD+lxda9Tv_sc^!8zzdMwONl+cEB_kjA!^_>fa4Cn0f}Hc%a4ig`Nwi)-JLFSY zDjnl(ErLNKRqD|NrH6mS?O48yqP_D#jOsapS!{K=mHxZ584`>!&n(64u7jaZWeOQo zq0PWdZSiUh_aFy67ScmKAC{MO2AjjqqNMP_Rk+%~*x3smE4yq+KWP|4^O+>_wIkX+F<$j(3$-DjlQnw2^etZPH4cWRK`_x%!pwyX zT+I{m;xm{UNcS7>RlX8&g57Ul>m=m?`&t+f)DZ@Tm4Xmxi9NY&s8=IDea#}J0qP?1 z_q%E+!y5Mi$8;Elk+W69qH5T`d>dj?1&dV=qdXiSvuxN8E4^|A8+Wb2y;z}csUCXx zPQcQ?m&41WY8OpK^x3DMxO4$}dwF8}+0*!Q!!rEy^%-WR0nu@6*Ob?mM& zHuZp+m<*Li6UDI~*!95&4!F8^M_}*A>XZw;1s*?tJ^0ODj0K~fN3dM;R=fZiVtuC0 zgNHjGJFh59!pg84D2Ue0B&^bZ1gvb-5m$7jkZCU@EF-_X)?GHW6?uA|5F1au5V>m9 zv4}%hk8G?CqI{6jGT`Ew6DX$Q!3?QoXE?NKSk4%e8|Sfg&+oW>mnLt>Y!soL$9R0V z{(JcPK3w1hyuBot{qlPV@(98HbBD0$;419-bPcw?`aXVN`XUZzoIsSc1U~r&7+~28 zuYdeKp8M@x*pb7ko(z7h172(&1OwyN+t(BrcZJWspfjsAW7F{eWZ_3HD~ zrB=G!e`Z$foFB^X3COGR# z|8bsJ@b(9Acj$(r7xrND-BY-1n#>L9x|Dii>d?3F>@Ppyv9F$jZ!6;lTW;jGnXvSA z^b4doTKWl;xDt60(*0j?k86@5xo$GYxc*4%1JN!nyz@^}A zp?;8nxHJ88<0Ff;N~ifRqo+UB85XhsO0nx^I7-j`!#}7+=77Qxv4+J@MA(`QNL9tgi}Wlo%FO`^z53rBMe7zL zG>Rr!c>%Mv?2q67U4@`F4~9wB2AcR8JkbYVEdCfa7M*e7%vRiuQNpLp8q-$1hv73? zacK(3y`w28;%8$2QLu3!uNNhAx`Xx6sgTCtACvlyf`tw5RxZxJihob;MX3%}!s@wnf~8Fx)gGst zl=FbBdGd^qXwfmwXx0X`5qWwJ^)@tfLERB~qSXrBWBg+^x@rp9Iox&i63R;XoY59m z9Z=S~{$Z1**G^;YwX5u|<15PRNj$Ogk2UDfp?(4{K;L2TWM6#$#s6Sy-2+!bH)BIW zCbqn>6iO{kB941|ET#oGLnPsS*mv%ph4|EJZd+57=u+gxhVV2{K+lW@dyf|K^BZtJ ziWaXaU}4=A!9iWQLf9=dAKE`T9@7F@$E-?Vc0J}ea`$}0ZEF*8+o5oEsI=gd4Caa= zPkP#@B2PmJngym0L?4Wg|6_PNcS7IEe0WlIoZEd17ZS@?Tn0HhAirJxs|8)y^C$AN z&vDzdu;{gyzeWFk4RDZGfGql?G~C3 z>Rr@S|r+N3kmO5(*P}7o4`x*E7UmM@xuk)fW$GDjCc)uSp_LaeB4U;(GD0 z7pC5YJ^;-E3;uX7);Pz-*uajkR8PM?2wA{a|J}%L4qmiqrQ2YiO1YNnOej~hm#4MM zD7^XJAdQw(BbUtJ$$t29(L!`?Jr+?ByYb!S>-hDlPpS;BY$(HD=!Hq%F6>P6BaxW9 zry#zxhTGNzC34-R88$SsgH)oK4!0>$l5iWlVl&tUT?`{>Fb3G#!PKI~jBk$k_YJ_a zZCa~i^ixxg;KsghwH{LGXyXSX<4V>%`7}vW$wNXHlh*w54-;Mx6h2Q45#nHn0;h&eR(aa6r^y^ZfrH)39!K9@z#1y=kh0@66-k(_wt&!IEk9L0-n!&(#G|sT2x_M=XcDvJB@k zqfsi0;wD^sIUAz&nEzOD@@7EXrZre|D;4kWS&x;Uy^4!RQ=q73iHNO>DJFL60}C5I z1#)r0Rctx90SZ+hhc+XB+=9!AG+b1|)VwW%tbH}=9&AA~loVtlFOLe=-^J1E>#_W+*ARB1`U2sdo_`7x z1HG8ak;Vd7qiH3q_Ub&)>ev2`wvb3@&Xt4omE7Ng$ZO3n|1gmk{PBXwt3P2t+rBkM z)nsIpVf)>9_9DEQZDSRToF3D;?S@bk&;>8_9SJ>s@^2|{WcHzpQ3{=KPRA6WMTKkQ&_#|LuPg9ZlX~W+)Dt~zWX88 zpFYXeq@)qb41Y&t449>QM#C`H*PSJACKhRDyxB-7!*=2NJ>FF#HE$0;k51h1wuPpL zo>&i3)B6k>Wj@x0?!>h{7pwHo({C9x^Hp>Tph;NLkqrDD6${y|Q{29$F?0(=dq=f1 zgrK3v{UWdNORTzklHRDI$P0_0I0m`7!=wi7NKP!muGlp8BD|}{!6pDXJtlD5nu2P= z4w*V1wsu3feTp$(bLkRRKmQ(=HL^KCsfxdN@(rv!vyOfKSoUiepi6L1=nZ{S^^Ari z)nTb3sh$KHs>$3WC{DVFl;kLG+s)A(o|C3>+buLb^z`&lRP>;MCo8y)KmPp=acs0) z6|VlanAoZvOw9RJ+6nQ;5r6e>t%OvP*tRh-hhDi~o*GrlLLA9UHp%lLEd zS6W%Tq)DP~mtw)Bx%m71TCTt`6`u$W7>B++XQ0SVbN(P{r6H_#vDDXHhoW_q3S244 zWY^S9b_p9}w2L(y+{oT(p(!F)h>?`Qdk*gB+P$N=^3&(l76uA_;YD=u;@9se%W|;o zP9{n&Z07bg4f*AzW*(|N4q>j7ZXOazjSq&adqFy0QRLMHJFRVlVbit)jL2JG1+o%$ zQm>ywVLqR$hDxK^x8<#EZIC@^7GCVqg{$RCmbQkAcT0ip z#(?hO>}=rr(I}i*xeX^i{|<*1F2?S+-@>Z7FXQ8x&tcZMC-MHSzq!bosvtMa$}8cx z9bUC_-qKov(Om|^$dvz$lj4sd=FUN_So6GJUR@fK3};GeLpP})@*4NcS10oHMl^h& zyOu;=9Xjdi=!t-#-MLJPRgoNDh&>7HS5ffs*-n<4EAdwyC6g9oT<5M_$v+}FVJ}u5 z-itH)%2&rW6^{OM1Auf@!GzjOH_3UYZ@jh?;>oO(^< zwzUoU=ZE-rV^%RAaYM<6{=9k6`+t^1|EL?>Uyz0H5C_8uf~bT9PwP^HOM7Gmq|B$R|5;P*9%k)j&b3?FUu32jo1@Nm=bwS3!XYiP} z1B}i13h7iRd)b-uIJ#bY3#A?g3H;{&?Z*7ozacGopXw+Zqd5=7vt7C&eaIZu5$zy0 zXI6u#njcKV$jTm#&>F>^Lr6~I*LaXW-mdkdmmj~W5pyX9OBTO{RcAM_5fpDEY8tuh z9`;;3fx<%7f%~AWuoGv!p|04Lo_rGVH-yrnnnU5;7dCe4M2%!W+#g|6l`hwt>O`I_ zcj{TFhfzoG8yY#n#K{c?^f9X7c6cgc@-kI>WcBMlSyj%M#TXGBz)l?>rWAAU05<>i zGt$!Wq|RP}5|9PDP%E3l&A)pEb@2Z)V{NyCB+qkP@b$ zkvsMxKToylRAho~ttzeV(g|forts-I2VS14h#qADc0?p%(_725mfxm2PH(-2cbEN$ zjP!%5BY>rCKg{pa38`b=QXSDQrBWvHV)_}ikB)1GYXxPA3}>=SxEq`Zwlar>E!_$& zXal)SjxArlivwZo9#NfhZIYD{jr1$ki#&yfPk;48NDTN@ut^E$kaOc-Zd+53U%r!V zrMWVi@F9^GB5ZUf^7N);0@6@|!@|@KUY)BJ?!saqGxj=UMfbRUQ}Z@Zx>F5P-BZx2 zBcATi0R|>irI2Rok7HNjErcD@ZgGH$q$C#Ml@oL%u3{(k#8WZcx9VN+M4k6*#BVK*Qz(PgN!0v9q%aqm38lDR63 znA|@8>Icx%=Mxb|-?@tX>+88~O<`7FEU7p!>FTaC{1->=xTPj5nRC|L5`8`832 ze{`c zM=SB+hIQC@BL`R4RUb{#n2_i6z(2ntIq@ieK!>Cxry=WHxeK)_eA`%}i>Io{zAOrR zG7C|9hfkZX2}EXM3Bc*a#jLW-z2jt04Y- z7&5bjNfQx5>~t5OT^*SNXQQ(Z@3JA1lL~P3!VbvtwI@b*Zx`Z+ZJW4i@Px$B6OVN4;pPbA{WMte_?NyBlGjV6UOx8RfJQ5jo2Zh)E zUYgOdQau+57)Z=_pQ(M^iRUK(SBA=9<#(GG8!pg$J7D{^;Zo9Ft z*trBdL$9fZ-ePoU+ZiJU&P1F3Rfm8!6gj(IOPOJiEa!9f5U#Ff$n)1;a< z?O|3lpE9DcNJ~pV{=J8)sS*7p4{ z+06%opV4gLl8P9xuF-~#zKrGNZjrFCn=lIdN43{@7??P*j+ZYjs7*@bvPQJc8irC? zh8wJtqQWgi2YY+y)1A;rBwmllg%xXX=F1;(;*)Q1WYK3h_}&LNw)hKNSh)#zE+-?G z-EtLEb|l0U;4RTU=qe1#4-p$Hiwi2I8_(+&udik`nox6C$xKCIvcU;{VZV|;pDPE@xM1-K}_6k)vJ}0laqkF+k7BEV{_-=dECirT!AMPihLBy9-h6BF6S99 zy~61Tk(3yQ?5uETB2AN{tvarSAkh#kc~UBk-ANTip0?@5sTZj%@**)@jV0CQkk83c zpj4Grk+3x?YqUrb+nxWz{_D(wSEV}k_v?q@GhTqbgO(MZ!TLfBdn^R4o%*ScuulK# zajjN?uMNfiUEyFyKc&X5n<^<&GD}#|yvi>MC=)`Fmdg9G$scduR(Cm72h^%wqN9l%dm`0tN6 zdwV_mg&&+uEG~#bYJPmRMKg3sW^@cPGqqcbxmG9(QB+pDXfXfI&TzHnuLrp-2FG*d zC`fL2Y`>wspK6PP)RlCX{*tk+_M$YD`z`s309kAq=^I$^)bsdY z&mV|S*vZ7F?vYVg%oF@Yy4ioD>ec_4gJPht)mY4z@xoWyj<%XkYy zPhthbMoKxGesU$EV^oVA#D*9X7=Vdi&WFB6PuatnFe?C4x;@RV2|AVJ|GS7^&R>9O zc;keO5jJuoW}&K+B54JANiM`GH&lDgizHbVvyl0TxHh(CFf!KeNR+JV<6r%R7v6gV zCnNsh@}z5%xQrqsoG71=Z7fQPGMUk+;~TOXk_E%G7L0x6DHs}45hoxk{Q}~1qfpX7 znJY7|l)#ZLI65)ZXj@|!V8s`rZDG=tEO~Vhr$k~0sZI4(pgcDdWpch(l;~SPU#a^a z(dNL<*m(V}dX=fO;~+ft((ACX(%8AGH8kQ`@XI^s;ysq_@mE4({BE2)@EasWQxR&C!r$Y zqz|Q0EBStrr#GqS8{#lDwuOaz^;V!fF9&j!$m<(QVW4A?H_T4O?(p--&Ezd*iJ=GP z_w9>5V=DQ)DiIl@L}++1SLmd+*0P<-5hw-xlgJMWm?~$dNzZ+JVnmq7BmHatH}T!s(@RN^`E|bgH3vgZ@ogvx%B`)78cZKxx?S)BS zyvX^|s-w7Agb(L@fenkdBEJ?P#5wuE`B3)b)>7n&p%YxNVEi~pE%{Y*(e~?W}Z7ypx5EU%ZXgXHIaX z>~slTdzbR_(b(yQjLq%XeaX8q8yovNhTCjov{O zMV_F2YV_^7H=^)KBYhLJ>QaMsQ&bFvDw?IVBopGwJ(O0FUOtT9uisV2H=9}X#M5(M zhr>fg+%Aqlpsg`J-1s;Ced8C%YM6t`%mKFldD>a^E%>r|Qx$7R5#U2*kq76drvvA6b)d zD+7mrd?_(O7pO0n6 z?X^Ry{kkJkvi4vUqQ*_6itixwB&5*PxHl7tM6fnAgrz#c!Gnm54b_&syd(n(rEX^N zMM?z{ul|G7WYt0skrcB+dSKw>>fJN~R_yz996gZ>Ncj5nNvwNqC8|n?o|*yt`_UGh zy7&{MCf4w(V@e)FHZGd^?FSg$sS89BenC=d>VBO0O}8nB32TWx%q$;%ZW=;CaUQ^> zl4u^#Po7hjiBcJFB=|`skQkA-QgcwbxzJS~VO@9P6LF<6zY~ z5Dt#oo%5&-Mkd}cHL81hlfnJ_b171Y)TB@-?lwT=sfoyO!olrUXc|Khd3sUW7DT?n zJBwgvY5=_&jB|adITUi$l6GYt@=EIF>#8s2=XPNKolN$g{Nf5<*MS)K{sJb(+-{9f zO#9%;zId)}0OI4W;)e^Tn3!)=J<5~LM)3bG_yuQDcfqb$4^#d(pZzGR2kU^FmnmjU zeFp8?kbj;|q!OoZ??K7h&$!2Rh1{_%?Cq%tjrJ%i%7&a>{LO_DQ5mk&^~$axB@EDx z5|(HXT9JD3=QY?6mxGLy`U`?4$jiBh!sN#F%X@Y6Lti`AiRvI>UQL*jTQ0b}YOnUR znKBZt4!k>$Ru=DyOGT0L!L<@%o0XFn7#k1h=id6-J|zUi0RoN81qGxV8)5?A(RDzg<9XJ`kG- z{Po6<*b#RDHmnbs@cJUyI@N!|XxKB|@T7kr+`UI}`xFKA*O|T0lj+X2L4J8pSM^1u z6^iojAw`kgZ2aB zZhdVqsZCF~cnrj~n_Kb8cR%35u_XLB|643Sb`Sw3N{pELCW5;+Y#|Yi0H%LC4@3Nn zA(rsgbWuSlV*gskZR-N^%LlL_jGn&s(m+!gx2pwW6vtDR!KM#op3KhXXCsYG9bo3I zxuj&Y)+TA!PdImjoeO1CD5#>qxwJeK)NY}t*6g%>R>02DHWno~S7D^ll`PG>)R#Ij)Nli%ntjkkv#cb~ez{ z<3o#xw0!O4Yp;m(?7v9QU5l ze82ZAbaJuA=%+tG$Z-E^^XS(FGjn}(aP9(AGv231>^a4aJkg?YLQm3$bnIr&JvSDuQvF>vsA z#7Jj0jBX^CjiE^}S_gHkx1zS0IvS3uDY=qo`E-R*S;W42AiT+_tx`6LM}|uCX|8O&GJUH~q`^ zF})qR;s4x@;#!eX3Yb08Ac89qJ!^^Gxe3^w;Zc53^glby4p~R(=)+fF)((j=# zr*V_A)s|v;4vLES9P+j%#;|m$RLG%0#2q?}bCKi&Z%hYCWpZ*7k#V{4SH1=gei9a{ za75iALXXx#2zqR)YQOG?^(7eQP8KDv%XgDfp}1c6ET8nRMS5y&*V;vLzsQr%h2opk ziY=n3cS+4Y5Mwh#=TyFdN;Ob?LNl(tj z#O|%Qo}S{ESM1(`%R4Wtp3!hpQ%dpOAAhS$0}|Z=MyR%QLtdvL@a2-VY0YpzQGNt& z>lLCzQU3`kT|h(Z0+9?Q#r%lS$J_u0b#(Vt2f1o)Up|2R?8X<=iWKEXAwMQUwbyvG z9^4620)m)QXy}C35;LnF7}=^l8&x-KXx^bq4`vARk!m@aS0Rc%#ckIG?he#J+Ezj2 z>7D3X3qETOIAJ+c60z22maFekO>AFhTO}}>jmpB)7T6Ki=ss=u2|KdW`o^GH5 z=Rs^_CNWUwpVK%Zl;B395{es#RY#g8)CI+rD)>|wVgm$NYR-L-#|CIulBl?ZZX(TU zjj{lzA{&3cgcd3ee(7!4+GwxF!vnD2xKEH90%p!q9jHG6lSaeNO3E0j&hW%uyg<;&Rl-j670 zfC1Tgs6SqL|9#94X$@chp=^W@h7*4;#ktTjF5;#>P%^4bOZH+#=#}zBR3Z~h?(sPH zJ#>wcxi=ds3yV6mLlWz4m9dSVE2SosvV7dj=kH93#1&FQ&4o|q5=41}%o- zrB}ODCGsTj>amPSkLrWgEJP-zU^hs4bd3%^H%I=+xft7qSpupF9Lfdq&B+tk_eaAD zB#S zwXa)>gNDG-QM;A9vNIx(QJPS%r~_T0h>t{8mMXGF>If5a&CONFN`TCQdR#C%AT{?M zikm{P4`akzFJMUU6KqSnmZ70Z8$8+C4c*^fD`B8XL~hqaB@zpd+a>?2l_nG1J3ox2yk*=JbqaHJ+9o?!A>xLPFx&@q07r(aNCVRzDFB$ zw4r`ndp8#4(La-`L{WN@>PXW=oEd{Wm9=eXD20)I!@^`MLW`heB~3F~o4hm=MU7va ziG$`qUix||0$r(KwstTy4a5`O+hXW~Pc$m3e&ff$+L8~sC71Ep>)~~B&QVykvc8ju zb)e#1E(m?4^B@xG!Pu$q6-T39}Q{Nf{=2;a+oP5!Q;WMz*}o6E(lHzr<&HqbZJ zZYbrQ=v0Vr>NZ5QE{OAzP*|ABZA;jQ)xuMAE-b$GY7_8@RDnW~hun;&lV8Z54WPdM zYZW?pKF)4n&7~n$LIMYuVR*eqKTMdr2qu75$#}7I2n>w*OqpdR@%aAYIc#3C zTL;q`Rg;iZhF@QM2RlO#sdL;LnYP2+exs4!uDm>8W1(>D081O~O}-MDsaGJ&XuMo4 z52LIo6D2bKI@Q;gz$(C>+tvhBuv)2TtgG)KC<~Ak!$%Y~6)Y{q`1YTlFn{_A7@Kxb z9jzxKDJ*RIU|h$k`1tuZF#3N>VeRdrdQfwing9cO^iU`L%gzahvJMH$IFZ*up0BDb zc{P-`21-4cSk^o-LU~{uOz(@pNz?5xJ!`_dn9;r~>y&s4IXB}JzS^)J7mw=xVou8d zetz~HtO-2=Dx^UeN!#JozQd6}<`r(gvB~Q^9FDe{&C_x}G=$xsl*8=VrfJcZ$%~+n z^A@dvf*CUwnzJJHIq#+Rt+e_fLVjwpYP;!R$RuRiTa)nTpL_7!;P>HbJ50SgyhcPu zY*gw8OWVF^<394MDV~F(17m^s{r>eGe0~HRpp1cTM0|&D`{%b|t-Gd(&e}a@W z-Q8@IwQ}XNA7I7#qg>Hn!bsW{FL&#W+)26(m#hi7u{rwM7_gB}g}D?h5Sf?)`K1OH zSI`n=Y-p_%+-PD+jY;_G3i+DF;)X|YXakYh5~6&5blKD(;c@krV%GoO#=i$QV)K?T z%%8jrLxN@^&}{;IoyQ``eF8f9Oh(UusTd#f7#0nE2LGG(BEETd36`z?9h1KL4k6D? z=fX6#1-&+1FYP*6A=plx1qK)5YoBADf)bQ8>j?<+r-fcM#g-PR>p1~Npa)E2p6>JfoFR6=iDPiY4L4*efAVKe!fpN8Kxb`g8%3B z<@nz|xWr0n-*VAF(Sb7qZ-@7fzU4Qhq;~ z8W?kKM@?a7DuRQ#b_=xB7eixf7`SRK5!-)}iir^IS_Q-0im(4#lz$UtS8EqaNsC>T z<*UN!V$_KxPgLp%DS?iu&BJ}SHm!UimGYfIX-Pg5CA!VYQjv_o&tpcrZtTPJ6UJF- zN3m?n-}raQUM_B3)1)x{+W6it`0UVb6y%&|?`s0T){`*5Pk$6X_B9j_SuxyL6b5#M zkL|-pX$|FGVgf`L*QxfJ4mq?8S%s;F3x!y)Tfy62^OJp=z}nma)I-w-NZE&-zk z!Od0W#ZVUG?9K36rP@}o|CHs?ID*J)TS2Snc8jWtI$VZ*|IxX6c!(0(5KxU{16Qm733HpXK2AJ=f=U^>Dh zspvP5kit5TY;g5ph1G)em(nO~`(!P?*te4_Bu*1RL%WW_gaOl#Hu5$8iA|V(4s4hy z(yq|wWrrdz>qe8ChRi7~AKIBcWL0-vA=gj8p<~q5z?u^69i*_e)u3bXWS8C>FJOu9v;6fVWV56@`1ZT<7VhX2dEEdhA#{ z`SMK6e0wg&&lrpTy?SCm&yg6`YdRk5|11_wT!7zS`UU?kJ%nS2GqCH+ZTMp6CS<0a zfrrmHyw;}|+D!Tod7Y^WRMSV#p$(*#+9_PEMp=Zz$uh`pH*V><`^lG=U)bj4%+)a7 zy+dGU-buCBG@xje4s2v@t=$FBk2Hn`yf0pW_FawbVQSun+t1Fq3xq#Bl(Hp)3UmKC9@Tb;Niqv4wFUv)~j_SPDM3KE09veAF9rZ);^LzH~$I9?z zd~)n8US0Vu-q`Rz{BYuDoQwDeiHW>5PA(Tpkx*Q4m227A#}J*k11BTb;pgMuujPkyNb3S8F4N-7=eb32O#(3+-Fn+0WX@Bn11;1C z>3vyV=y!JQKbRQ`g8D0P>Q2l<-IL;?O4=g`yztL7CiK+xe!Ndn?Y#rIT_QtzlPVweTg@o{1b1_+ljZI zUW1p$EWxmLbK&hc8m4Al)T`U>hbDKIZut?XzMBKveJdb((AU;v@v?H&dWr+Ok}lm% zN7?zx#nKxSS!wx&EjH4;B~SRYbHLcZcI?}0cRfOFu(s`qKD|b2G>YiI291F3x8w;& zA1k<5NYWUUl>*tr7CIGGTB|@$rVCOKdaa6l`HQnHMJ)?Ia3XK%025Pw$U@<1lA^{- zwJN=P8K+LH!H&a6IM+)#BF?hv(0VdHA2T1z-~1nT?c0ovM}Nbzogd+y)h}SdvhjH9 zqkeenn~C`7&$;;P=o0KcwGLZ%9>DhtR^ipbZ^FxYv^oQw0L8|KhHt=^dp9Cy^<3^V zGzSX3dcwxeN42LtGE&YVBkqi9ukk6ZAo2=H%}My`3%M}|3>X6krva)Xjf6;wu|Dny zo;F%_poRKC33;hx=>5gL2AZFYGh{@!YI9Q@?)4LMq=yVU*qJZ&1@8+TE5MOs@C=cFfL>| zzJB}#tlzN`&;I^4X1vi0!2x0}37LrzjYjwwSei_sj@f_>#c1D^8SvdZ>*2|F`(n|? zMcB_of5j_bVnpzhu%N;RqVnjBf}9KZ>+E)H->?j3m-efUHeCwZ_JOy9=G_$!pv=Rz zm~0fJ#HtQ7G8L}&5-o{5(Qf)Myx6%b8!c&f;aN@idJn;fC!bO6wNPKotFYn;5`JeJ zCL$^wpe(EzH%@Td)lpJZsiHcGJB8#_6nc$Noh8q`0j0rIDoblM@k-bRwT7hy|0TKN z$c>O@);K*D+kX8Rx1zT)o4tfPCNc2F;E?h7{K?t)WzR2o^6Mwy;;dI`K|@_2ADk2G zv>*FmIDR|w16F%M`-kv~*Nn{^$Q6+Il1XCiM+=Ay%j}X5cf(VuPCGZ9<=vxJUV#7&W!(7 zMTM!**Kz(DN^iVt#X{V=x0UVDhZe!saWG!#-5-lT{|t|PI2I2{jbCS|D%P-x?)Y^3 zQvCAfy9jALrIO!1C-Vfp+xaJ~c6_Ni(ln9S1j0<)p_Gi$;yc)toB&yFmg-33VOU|@ zEi2M)>Rn@Yos4|UDk*d>*ZP*v41KyKuK{t z407sOFRz}Q|N0f&ytjpIvCklqqFwM5EF3fh3pOr8yRNm!X4e2{R50e1f%tdl_jqsc zbl5r3LM*<+%gZ{0Z;$MS&8CmJ{iaA>=P_`!*Ud8gn=x^~nO{|VjfbJ6{PHhM)4prb z#>o`ZpId;T-G-{mR%#P^k5~Fm#_0E7QXOfbA=H$NSIh>5nT3_t24xqeptPEx53=y@ z<*AFxkf@X5Q$isUd9voUYRS`!my5ij1bLaY3zea2N)}2ZNc5fAKP*6WvZ|h`4k-Kc zeVmKl&9+G3jnKKnD9j%_3A28jQ*XCSeXw;h!D|~n#z#}1fUnPZc7%5c0j76S+=@AfaD=a4Rg&HqF$#Zq}YKH02AgXcL+%IdL-$@)N66 zM;eEUB41QkDbhi!I0rgm{L*DuG-d|8J;!M@^??!waB~=mFJE1b$zQyuI@&@*q1gDE zvXsi1YGxExnn>mJ==SZZi{_NXMc2`pH@4u zrf3ussU1vIrS0PLwV%JnC_5SdoH@cpw9wZH@E?meCrrkeSBG=GZ(~6dF%w?xhsE>f zp?ABn>Q%Sox>>d3FPKJ!a{En*f`CD=vC&)!yqd^JJAu%otL&mvUurtXlzL+oMU|~8 ztwSMqJztrL#qWNBc|9k<-g%hX7^oLw1DKk3!DAs$W6AR0&}CY0)dMYP0*i;FcPeA* znO*vz2mWd*GiIvLA#r*nie+k%Cqb_$@TA2W7kPqSoP0xq<6lJya%1CF+ciU?YGUsQ zb5%{)oZ@U463ATb(9nsoOvvn}5MNxtyw^C4YX07Ug?r}3NzjKpv^EG!@)e^cy z{kphf%2&&=aLfce)9rCMIu2)}sUY@Qs+r-dh)Bw%)DG~NfVYOci6!r^!R%#U!Q0x`xNK*t9lH28l8JPLqxA<}8T6{k9J-j#cbqoxA3L)N8(8hBjd_Bg&%WVwW zdQZfNc8_Czzqwd4?iGCf>Qa3C;~yBY_#Jc}>{jJ!%twXHOyKm|(>T6-J+gEdoMpxY z{$Xz4xX@^17d$BDd3lLY-gwY2uPn%@xX_etfr|ckY8pfM=+tuvRy_HJ)Js*V&}?36 z?L#5``?Z6U$`~vyiH8vzmS_V?8*uyDW^UL|_iR- zUO731mDUiE=29b}*P10yJqf)yy$RVfRKJSrW)w_J^RjE-ZCY9Bp_N#V_dzQQ5FcN= zQdo7x^3+Bgie#M+8)?vDr#GfgLaR2qi$AU_G`e`~^BH)xM=$R45|rp{Rp=#{U)aO# zHw7p~vWKJYDjMZvoWhkWyJ}oCqBR@>U}MXtrpV2_f*ad5bK6xxcU>{N4YQfCmYPN@ zy4lV0;OJaqa4X1-+5Kb**xB$!NNB$?0tuQ5MG+^MQFoCGU?@(~j`4UuG})@oJ5~*p z=Pp0t4oZruSjnv{Mp$xXcQ;+@cd{xO6x2J=1SSc+V9jd`y~T?cq;0BoLVXt1 z@S|+gRL@E*7Kst!-U$O{3}m|xhbACyY(I@taj7Vir^C$H7Y@BNzYrs7-}g9oYY(#q z%h1t#FqZuBR=sWj89Sxdt`_3R%6&Nf`!4KVvmYB*?ZnOPCvbK5Ropmn8(D>>u=BKr zL3#e6dO-^*eERi9^6_i9mK4Q~l(NxIBA2JgttUH2O`t@eQY=A0%pshPzQgvksA6iA zh%Yuncc}p+E_^<2U7@tFfjs9RqVC2qV<8XMlN4~ea~rY8j^iF{F-MOh`fz!p{Y(1~ z;_Ti7IIwLuwyxiXo$GfY^-=^={bCrJnZwxP5%!lfB1O#fJhf{bF2rkMBNTf$#p_ zgzaaJVb|5eI1#lMm*Xzt@a<3>j*7&QTT$43^dwGgIe_$omtbh+0(%dedi(t;ZMn5m zJ6yUPi}Z|}+_9A0bo44KgFHZcanp6dJUJFu5_8z^r=3yQ{gf0XKq4`QXS?ALi)x5d z(=ha2iiqtRrtXK+37 zJfhPsAvx5A3Yogk1QmT|; z^S=w(<7BvVRy*}vw^^e|404;$0~&ML2=7ZTNIgfz$ye@8$X%0FTF6=GwSk5==B?YQ z>%C^lvNfJ#U36|eGSm2^{D#Ip81nKoZo4*+<-YFS^>}B-^H})rzu0ho9b%%lA}8Yn z%1UXt#h+V><)(cK%}h($kAs)jLXk;h;@{{LW+G>7lAwsS^}985s%1;qO)Zx`BqxW(q^-R|ut)6^0`*b{CT3 zc5^iY3Ue=V#o(z9K~=^T#ii3kwUXVq_wH=P%ELSH@xJ|7v2ZRfeEYNNS^o|096Ey^ zUYdg&TQr}PFZu9xB&P8?Vk`}Sd7HW!b@hQp-jufFVMLryKvHhi^Oj}Ic*`ryLP>Ir z>Il2PB12JJq+XXn7ZuIelDme!pw@iy1icx(fgtkS6-_-+o`PAKoQJ-4v>MO}7OJyD z5jG~_r?_oRAYz|o`#mOxNF&5@&=cvHVdNu#Xn z`oY0vI9xnO!_8|9Y#ax{)FPx(LaD;sP+YpW9$%a|g*QL{25VmY9wl`+FBKX8`STdk z%ANh1yqS=haRf1cf5B}xHZm(G^t0C2X^s^-w}0ITLs6z~CO-=^laaRZHT-rh8FwNm z6~=?mC{a(}jVmGPVmk~Sy`R8{&TnAmxaD~7oul~nlau)V>94S0&}fVqH5ktBBia2@ zgyi@=Sa;|kmY+L=O>e!3#Eblh?NKJ;awfKHTZO;Q9Kf6}7viTE<|95#GucMTRg)5T z0de=jxP1o;4|w!emG#sj51Vimh&gcvnduR#Jx)U@pSJoyVSX~iskc;nKz2-}blYUL zlbDf^r>^r7}LOGsL489)DrvL)|9{_X_a+tFPn73%3w^cQ?Bk3t(m2 zALD}hVp_+3=o#1*Jv$9YNZ*<0+PWKhJG-F2lM{Nnxv}rsxR82zC1UcG=;hK1r4E(z)HepU(Km1< zy+rldbVEjVI=krdU>Yz;bwu+}75$Q9KjGlnzwz(+6UfgdKWh0sMd<*XTzjBTt4?^T z(+E8I)N6SC(}kEje+r&_VH74l)ej>lc0jM*&hTsJ2#;PJ;5TRlY|Gr>l@o_jdv7E& zi?ggWp56F2a4}JaJF(HQxN!$A-Mg}mvz~swCQN=laQxc^`19OZZuDDLnuaS0Q3z!s zZygZ{W0%&D+SG5Zf&~8LCm-O~Yca^pJkO5kVO*P@=sjsT{{$^Tdg8`x$A|g+DY9nI zkN;r*HA<<%LEaEbSVNXMg>H$J>`8-sJ3`{gmx8>o|0_f$a(w^^I;lT>G4hR4(O3F+ zTAO$!1t;^HM4rk!b!3fO0$^`W39Y>){ukq-~MklESrpU<4W}QSnH|KCE zF&p>J-$FpUp0IbS+lA~A?1GGgS8ySoStv?2T4J4t3;SB8okyvj(TJGYAuBZuNeK^z ze`yP)q5!uFEzrrW74+F?Leo$o@zi4I?XcqcC)(3%IU14v-&!tcfBpSlg^-Yb!bbbzFC=Ic4 zSLEizaIUz7#OpYdScoe$t1Wf1$c6YwH`bW0mZG0mM;Q3^4wH4yJ+qRq4YXj+oum2COG;({n6(ib@ccDdwzPMOii?lkVW!$$RM6zYi>IYvIIG zK@&Q%5<6zi-(y2Mie_QMQaS5@+JvA~+PLNPOU-QHb9*<=MY9o;Qg;is3iI#5yr=}O zokrD43~Ec!`SmzY@s$5w98$Ld; zi;al*s25M)vGDclzyw|o^3oLU{>aFAT|1-hWd4r;;Tz5 zT#+kRitz2vpF>v6CAcHYoNi>{hH+<2j~f-(2EnQQa1<70WBay`_(yry_zBkN-;irU zm~_=B6-AydIU$UaYWPu<4b_AbLn%QICpUDP+@I|}9QsmsZ_98p{5tY;qfjbwM&}N_ zAUCN+*H{B)G2u9Td?V{P_;djJhEACI#MAH$sD6d!j%5e%)uAIuh@k~HVgv?_!`R+4 zVN_4|ksdou2CaIalcN*j(u$FpeU}}PAwDx2aTnt;eCi13>r;POJ3N9Nk+kmwF2?a8 zl|{uF=x1n(;=snwJz^CbiSYDd6ctocBUdXZ5uIC#p4K+dcMeb;t3NckT6}s9{yKjM z7cTBXSxEx7hQrNsC?4w=fXUqk^by-&^s{`aig!1;WCn4`v2AqML&sTHEWR z)GmnKA7XBVs#N44Co=-KlEQH)G7LwU;Gd3)!I2ARuzT-L>^pi0M?){-P*@mtUO9%7 zkw=l162^5BJYw_-9)>4B`4sv_+C6DBooH7}8*9fm|6g3-zbo~hD7bgsjJ@M)r&%?%! zn>u)aG&cD8c;Ocay-AB)l(2Ceh&jRSpxiJ2k< zcNhRYgZeM!($On8nsWWv>GI{cY@x z(QVQI1X_3^C%zcbnVIa`%|%gRG>%6_BKKk>d^+}n)IzsPe`)2vh}g0Pc}D)QaguVQ zkh*eZ%|^Vn>KEi@o#gfnO#?Bvb34pivkbka^+5Ltqu^^5h+w%GCZnLEdfTWZmMzSF>3Mkd<*0c{!1+Q%GfoQ)Ra_xoKXVJ`1iL{ZvP^hqFEJihCFo zjPlcG-{NZA71a?gV*rUfi8qNnxfrHzY63N0MF%a_Uf*+r>p6om4q&N{gdW7~k*+gRHDXCIIDm zbo3>ruyv_^bg=V_^*DK#X5`s<6G<_ql_MmBU*{jx0FuCdc&6ttu68UzD_Fljb__Sp z*RAZ~z`3I^%*UGj0Q~syc4`EqS807j<5FznihdRb+#H6kiHtvtgdHE(dnGLWyP^}T zvHZv$RTLF1O6iQJd-lX@vtPy&-_3y)ALXl6NI(Za6@uRn{(?oLo`<;=A2v%9lp90; z!0XS?M);vy+|8#e^5XB}_q%tndHs7h`SIsSxL*6~wSdXcrnf)FYpcFPZg#oAKNk{$ z$G-fGS**OI(ELTe@xFNex7YFQ=3lY={g3e3s96})ZaSP@N5RszFN~yZIhR>w8F~=v znCEtEi{MGMTaeihC|x?`s;Ld}%=SKVCtS71i8jSil33GZ5A|kh!OkttN137&d&2jr zj&MIY4UvWLN$MapF7U+tmOQBp%^p&MYr3;(wE<^l=vlWxC%-Om^{$=Eg%)q#-f;rQ zqhq;AN=Q85)N73D(fV=g=tkU0QJ++pOZ_o#{LgV8$k49?t3=DugKEDiQaGW4ES`v?*G{K?0ebr+M)te9jb02%0DYz4H_ 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 E2j`LGcmMzZ literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs index dc89fa3a59..d11bfa05ba 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs @@ -6,9 +6,9 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; +using osu.Framework.Bindables; using osu.Framework.Testing; +using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; @@ -18,7 +18,6 @@ using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Objects.Drawables; using osu.Game.Rulesets.Taiko.UI; using osu.Game.Rulesets.UI.Scrolling; -using osu.Game.Skinning; using osu.Game.Tests.Visual; namespace osu.Game.Rulesets.Taiko.Tests.Skinning @@ -38,35 +37,28 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning TimeRange = { Value = 5000 }, }; + [Cached(typeof(IBindable))] + private Bindable beatmap = new Bindable(); + private readonly List mascots = new List(); - private readonly List skinnables = new List(); private readonly List playfields = new List(); + private readonly List rulesets = new List(); [Test] public void TestStateTextures() { + AddStep("Set beatmap", () => setBeatmap()); + AddStep("Create mascot (idle)", () => - { - skinnables.Clear(); - SetContents(() => - { - var skinnable = getMascot(); - skinnables.Add(skinnable); - return skinnable; - }); - }); - - AddUntilStep("Wait for SkinnableDrawable", () => skinnables.Any(d => d.Drawable is DrawableTaikoMascot)); - - AddStep("Collect mascots", () => { mascots.Clear(); - foreach (var skinnable in skinnables) + SetContents(() => { - if (skinnable.Drawable is DrawableTaikoMascot mascot) - mascots.Add(mascot); - } + var mascot = new TestDrawableTaikoMascot(); + mascots.Add(mascot); + return mascot; + }); }); AddStep("Clear state", () => setState(TaikoMascotAnimationState.Clear)); @@ -76,59 +68,25 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning AddStep("Fail state", () => setState(TaikoMascotAnimationState.Fail)); } - private void setState(TaikoMascotAnimationState state) - { - foreach (var mascot in mascots) - { - if (mascot == null) - continue; - - mascot.Dumb = true; - mascot.State = state; - } - } - - private SkinnableDrawable getMascot() => - new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.TaikoDon), _ => new Container(), confineMode: ConfineMode.ScaleToFit) - { - RelativePositionAxes = Axes.Both - }; - [Test] public void TestPlayfield() { - AddStep("Create playfield", () => + AddStep("Set beatmap", () => setBeatmap()); + + AddStep("Create ruleset", () => { - playfields.Clear(); + rulesets.Clear(); SetContents(() => { - var playfield = new TaikoPlayfield(new ControlPointInfo()) - { - Height = 0.4f, - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - }; - - playfields.Add(playfield); - - return playfield; + var ruleset = new TaikoRuleset(); + var drawableRuleset = new DrawableTaikoRuleset(ruleset, beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo)); + rulesets.Add(drawableRuleset); + return drawableRuleset; }); }); - AddUntilStep("Wait for SkinnableDrawable", () => playfields.Any(p => p.ChildrenOfType().Any())); - - AddStep("Collect mascots", () => - { - mascots.Clear(); - - foreach (var playfield in playfields) - { - var mascot = playfield.ChildrenOfType().SingleOrDefault(); - - if (mascot != null) - mascots.Add(mascot); - } - }); + AddStep("Collect playfields", collectPlayfields); + AddStep("Collect mascots", collectMascots); AddStep("Create hit (miss)", () => { @@ -136,7 +94,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning addJudgement(playfield, HitResult.Miss); }); - AddAssert("Check if state is fail", () => mascots.Where(d => d != null).All(d => d.PlayfieldState.Value == TaikoMascotAnimationState.Fail)); + AddUntilStep("Wait for fail state", () => mascots.Where(d => d != null).All(d => d.State == TaikoMascotAnimationState.Fail)); AddStep("Create hit (great)", () => { @@ -144,12 +102,111 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning addJudgement(playfield, HitResult.Great); }); - AddAssert("Check if state is idle", () => mascots.Where(d => d != null).All(d => d.PlayfieldState.Value == TaikoMascotAnimationState.Idle)); + AddUntilStep("Wait for idle state", () => mascots.Where(d => d != null).All(d => d.State == TaikoMascotAnimationState.Idle)); + } + + [Test] + public void TestKiai() + { + AddStep("Set beatmap", () => setBeatmap(true)); + + AddUntilStep("Wait for beatmap to be loaded", () => beatmap.Value.Track.IsLoaded); + + AddStep("Create kiai ruleset", () => + { + beatmap.Value.Track.Start(); + + rulesets.Clear(); + SetContents(() => + { + var ruleset = new TaikoRuleset(); + var drawableRuleset = new DrawableTaikoRuleset(ruleset, beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo)); + rulesets.Add(drawableRuleset); + return drawableRuleset; + }); + }); + + AddStep("Collect playfields", collectPlayfields); + AddStep("Collect mascots", collectMascots); + + AddUntilStep("Wait for fail state", () => mascots.Where(d => d != null).All(d => d.State == TaikoMascotAnimationState.Fail)); + + AddStep("Create hit (great)", () => + { + foreach (var playfield in playfields) + addJudgement(playfield, HitResult.Great); + }); + + AddUntilStep("Wait for kiai state", () => mascots.Where(d => d != null).All(d => d.State == TaikoMascotAnimationState.Kiai)); + } + + private void setBeatmap(bool kiai = false) + { + var controlPointInfo = new ControlPointInfo(); + controlPointInfo.Add(0, new TimingControlPoint { BeatLength = 90 }); + + if (kiai) + controlPointInfo.Add(0, new EffectControlPoint { KiaiMode = true }); + + beatmap.Value = CreateWorkingBeatmap(new Beatmap + { + HitObjects = new List { new Hit { Type = HitType.Centre } }, + BeatmapInfo = new BeatmapInfo + { + BaseDifficulty = new BeatmapDifficulty(), + Metadata = new BeatmapMetadata + { + Artist = @"Unknown", + Title = @"Sample Beatmap", + AuthorString = @"Craftplacer", + }, + Ruleset = new TaikoRuleset().RulesetInfo + }, + ControlPointInfo = controlPointInfo + }); + } + + private void setState(TaikoMascotAnimationState state) + { + foreach (var mascot in mascots) + mascot?.ShowState(state); + } + + private void collectPlayfields() + { + playfields.Clear(); + foreach (var ruleset in rulesets) playfields.Add(ruleset.ChildrenOfType().Single()); + } + + private void collectMascots() + { + mascots.Clear(); + + foreach (var playfield in playfields) + { + var mascot = playfield.ChildrenOfType() + .SingleOrDefault(); + + if (mascot != null) mascots.Add(mascot); + } } private void addJudgement(TaikoPlayfield playfield, HitResult result) { playfield.OnNewResult(new DrawableRimHit(new Hit()), new JudgementResult(new HitObject(), new TaikoJudgement()) { Type = result }); } + + private class TestDrawableTaikoMascot : DrawableTaikoMascot + { + public TestDrawableTaikoMascot(TaikoMascotAnimationState startingState = TaikoMascotAnimationState.Idle) + : base(startingState) + { + } + + protected override TaikoMascotAnimationState GetFinalAnimationState(EffectControlPoint effectPoint, TaikoMascotAnimationState playfieldState) + { + return State; + } + } } } diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs index f05c335456..2c94f5f1cb 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.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.Audio.Track; using osu.Framework.Bindables; @@ -11,68 +12,29 @@ using osu.Game.Graphics.Containers; namespace osu.Game.Rulesets.Taiko.UI { - public sealed class DrawableTaikoMascot : BeatSyncedContainer + public class DrawableTaikoMascot : BeatSyncedContainer { - private static TaikoMascotTextureAnimation idleDrawable, clearDrawable, kiaiDrawable, failDrawable; + private TaikoMascotTextureAnimation idleDrawable, clearDrawable, kiaiDrawable, failDrawable; private EffectControlPoint lastEffectControlPoint; - private TaikoMascotAnimationState state; public Bindable PlayfieldState; - ///

    - /// Determines if there should be no "state logic", intended for testing. - /// - public bool Dumb { get; set; } - - public TaikoMascotAnimationState State - { - get => state; - set - { - state = value; - - foreach (var child in InternalChildren) - child.Hide(); - - var drawable = getStateDrawable(State); - - drawable?.Show(); - } - } + public TaikoMascotAnimationState State { get; private set; } public DrawableTaikoMascot(TaikoMascotAnimationState startingState = TaikoMascotAnimationState.Idle) { RelativeSizeAxes = Axes.Both; + PlayfieldState = new Bindable(); PlayfieldState.BindValueChanged(b => { if (lastEffectControlPoint != null) - State = getFinalAnimationState(lastEffectControlPoint, b.NewValue); + ShowState(GetFinalAnimationState(lastEffectControlPoint, b.NewValue)); }); State = startingState; } - private TaikoMascotTextureAnimation getStateDrawable(TaikoMascotAnimationState state) - { - return state switch - { - TaikoMascotAnimationState.Idle => idleDrawable, - TaikoMascotAnimationState.Clear => clearDrawable, - TaikoMascotAnimationState.Kiai => kiaiDrawable, - TaikoMascotAnimationState.Fail => failDrawable, - _ => null - }; - } - - private TaikoMascotAnimationState getFinalAnimationState(EffectControlPoint effectPoint, TaikoMascotAnimationState playfieldState) - { - if (playfieldState == TaikoMascotAnimationState.Fail) - return playfieldState; - - return effectPoint.KiaiMode ? TaikoMascotAnimationState.Kiai : TaikoMascotAnimationState.Idle; - } - [BackgroundDependencyLoader] private void load(TextureStore textures) { @@ -84,21 +46,60 @@ namespace osu.Game.Rulesets.Taiko.UI failDrawable = new TaikoMascotTextureAnimation(TaikoMascotAnimationState.Fail), }; - // making sure we have the correct sprite set + ShowState(State); + } + + public void ShowState(TaikoMascotAnimationState state) + { + foreach (var child in InternalChildren) + child.Hide(); + State = state; + + var drawable = getStateDrawable(State); + drawable.Show(); + } + + private TaikoMascotTextureAnimation getStateDrawable(TaikoMascotAnimationState state) + { + switch (state) + { + case TaikoMascotAnimationState.Idle: + return idleDrawable; + + case TaikoMascotAnimationState.Clear: + return clearDrawable; + + case TaikoMascotAnimationState.Kiai: + return kiaiDrawable; + + case TaikoMascotAnimationState.Fail: + return failDrawable; + + default: + throw new ArgumentException($"There's no case for animation state ${state} available", nameof(state)); + } + } + + protected virtual TaikoMascotAnimationState GetFinalAnimationState(EffectControlPoint effectPoint, TaikoMascotAnimationState playfieldState) + { + if (playfieldState == TaikoMascotAnimationState.Fail) + return playfieldState; + + return effectPoint.KiaiMode ? TaikoMascotAnimationState.Kiai : TaikoMascotAnimationState.Idle; } protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) { base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); - if (!Dumb) - State = getFinalAnimationState(lastEffectControlPoint = effectPoint, PlayfieldState.Value); + var state = GetFinalAnimationState(lastEffectControlPoint = effectPoint, PlayfieldState.Value); + ShowState(state); - if (State == TaikoMascotAnimationState.Clear) + if (state == TaikoMascotAnimationState.Clear) return; - var drawable = getStateDrawable(State); + var drawable = getStateDrawable(state); drawable.Move(); } } diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs b/osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs index 2c04d3e1dc..c8e97b9f8b 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.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.Graphics; using osu.Framework.Graphics.Animations; @@ -39,7 +40,7 @@ namespace osu.Game.Rulesets.Taiko.UI { foreach (var textureIndex in clear_animation_sequence) { - var textureName = _getStateTextureName(textureIndex); + var textureName = getStateTextureName(textureIndex); Texture texture = skin.GetTexture(textureName); if (texture == null) @@ -52,7 +53,7 @@ namespace osu.Game.Rulesets.Taiko.UI { for (int i = 0; true; i++) { - var textureName = _getStateTextureName(i); + var textureName = getStateTextureName(i); Texture texture = skin.GetTexture(textureName); if (texture == null) @@ -63,10 +64,13 @@ namespace osu.Game.Rulesets.Taiko.UI } } - /// Advances the current frame by one. + /// + /// Advances the current frame by one. + /// public void Move() { - if (FrameCount == 0) // Frames are apparently broken + // Check whether there are frames before causing a crash. + if (FrameCount == 0) return; if (FrameCount <= currentFrame) @@ -77,18 +81,27 @@ namespace osu.Game.Rulesets.Taiko.UI currentFrame += 1; } - private string _getStateTextureName(int i) => $"pippidon{_getStateString(State)}{i}"; + private string getStateTextureName(int i) => $"pippidon{getStateString(State)}{i}"; - private string _getStateString(TaikoMascotAnimationState state) + private string getStateString(TaikoMascotAnimationState state) { - return state switch + switch (state) { - TaikoMascotAnimationState.Clear => "clear", - TaikoMascotAnimationState.Fail => "fail", - TaikoMascotAnimationState.Idle => "idle", - TaikoMascotAnimationState.Kiai => "kiai", - _ => null - }; + case TaikoMascotAnimationState.Clear: + return "clear"; + + case TaikoMascotAnimationState.Fail: + return "fail"; + + case TaikoMascotAnimationState.Idle: + return "idle"; + + case TaikoMascotAnimationState.Kiai: + return "kiai"; + + default: + throw new ArgumentException($"There's no case for animation state ${state} available", nameof(state)); + } } } } diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 6d9d263141..ebb3e0e786 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -282,15 +282,15 @@ namespace osu.Game.Rulesets.Taiko.UI mascot.PlayfieldState.Value = isFailing ? TaikoMascotAnimationState.Fail : TaikoMascotAnimationState.Idle; } } - } - internal class ProxyContainer : LifetimeManagementContainer - { - public new MarginPadding Padding + private class ProxyContainer : LifetimeManagementContainer { - set => base.Padding = value; - } + public new MarginPadding Padding + { + set => base.Padding = value; + } - public void Add(Drawable proxy) => AddInternal(proxy); + public void Add(Drawable proxy) => AddInternal(proxy); + } } } From fcded206559d688f0d363d12508b666ee779abd4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 27 Apr 2020 08:58:01 +0900 Subject: [PATCH 0463/1117] Don't specify IProvideVideo interface for now --- osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs b/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs index aea531e88d..9785b7e647 100644 --- a/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs +++ b/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs @@ -10,7 +10,7 @@ using osuTK.Graphics; namespace osu.Game.Tournament.Screens.Showcase { - public class ShowcaseScreen : BeatmapInfoScreen, IProvideVideo + public class ShowcaseScreen : BeatmapInfoScreen // IProvideVideo { [BackgroundDependencyLoader] private void load() From d46643ec5270942bb0e590fb5c2febea0101ba02 Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Mon, 27 Apr 2020 02:10:12 +0200 Subject: [PATCH 0464/1117] Rework special case for strong hits --- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index ebb3e0e786..9a2cfad8a4 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -270,16 +270,12 @@ namespace osu.Game.Rulesets.Taiko.UI if (characterDrawable.Drawable is DrawableTaikoMascot mascot) { - var isFailing = result.Type == HitResult.Miss; + var miss = result.Type == HitResult.Miss; - // Only take combo in consideration when it's not a strong hit (it's always false) - if (!(judgedObject.HitObject is StrongHitObject)) - { - if (isFailing) - isFailing = result.Judgement.AffectsCombo; - } + if (miss && judgedObject.HitObject is StrongHitObject) + miss = result.Judgement.AffectsCombo; - mascot.PlayfieldState.Value = isFailing ? TaikoMascotAnimationState.Fail : TaikoMascotAnimationState.Idle; + mascot.PlayfieldState.Value = miss ? TaikoMascotAnimationState.Fail : TaikoMascotAnimationState.Idle; } } From 48168dddcec916967327057454f649fe884a02d1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 27 Apr 2020 10:54:32 +0900 Subject: [PATCH 0465/1117] Adjust editor timeline current marker to promote tick visibility --- .../Compose/Components/Timeline/CentreMarker.cs | 13 ++++++++++--- .../Edit/Compose/Components/Timeline/Timeline.cs | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/CentreMarker.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/CentreMarker.cs index 0d4c48b5f6..0d2b35132e 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/CentreMarker.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/CentreMarker.cs @@ -12,14 +12,14 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { public class CentreMarker : CompositeDrawable { - private const float triangle_width = 20; + private const float triangle_width = 15; private const float triangle_height = 10; private const float bar_width = 2; public CentreMarker() { RelativeSizeAxes = Axes.Y; - Size = new Vector2(20, 1); + Size = new Vector2(triangle_width, 1); Anchor = Anchor.Centre; Origin = Anchor.Centre; @@ -39,6 +39,13 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline Origin = Anchor.BottomCentre, Size = new Vector2(triangle_width, triangle_height), Scale = new Vector2(1, -1) + }, + new Triangle + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Size = new Vector2(triangle_width, triangle_height), + Scale = new Vector2(1, 1) } }; } @@ -46,7 +53,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline [BackgroundDependencyLoader] private void load(OsuColour colours) { - Colour = colours.Red; + Colour = colours.RedDark; } } } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index 8e6b3d5424..25f3cfc285 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -50,7 +50,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline }); // We don't want the centre marker to scroll - AddInternal(new CentreMarker()); + AddInternal(new CentreMarker { Depth = float.MaxValue }); WaveformVisible.ValueChanged += visible => waveform.FadeTo(visible.NewValue ? 1 : 0, 200, Easing.OutQuint); From 104c61d622589b199dd859abf3dfb2e6f0d0e7f0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 27 Apr 2020 11:06:23 +0900 Subject: [PATCH 0466/1117] Remove unnecessary scale --- .../Screens/Edit/Compose/Components/Timeline/CentreMarker.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/CentreMarker.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/CentreMarker.cs index 0d2b35132e..8c8b38d9ea 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/CentreMarker.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/CentreMarker.cs @@ -45,7 +45,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, Size = new Vector2(triangle_width, triangle_height), - Scale = new Vector2(1, 1) } }; } From 8a47a615dbb44c3e236f8b4b61ebe1609a9bcefe Mon Sep 17 00:00:00 2001 From: Joehu Date: Sun, 26 Apr 2020 19:29:22 -0700 Subject: [PATCH 0467/1117] Remove unranked label from footer --- .../TestSceneModSelectOverlay.cs | 12 -------- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 29 ++----------------- 2 files changed, 2 insertions(+), 39 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index 2294cd6966..769c660f48 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -117,8 +117,6 @@ namespace osu.Game.Tests.Visual.UserInterface public void TestManiaMods() { changeRuleset(3); - - testRankedText(new ManiaRuleset().GetModsFor(ModType.Conversion).First(m => m is ManiaModRandom)); } [Test] @@ -217,15 +215,6 @@ namespace osu.Game.Tests.Visual.UserInterface checkLabelColor(() => Color4.White); } - private void testRankedText(Mod mod) - { - AddUntilStep("check for ranked", () => modSelect.UnrankedLabel.Alpha == 0); - selectNext(mod); - AddUntilStep("check for unranked", () => modSelect.UnrankedLabel.Alpha != 0); - selectPrevious(mod); - AddUntilStep("check for ranked", () => modSelect.UnrankedLabel.Alpha == 0); - } - private void selectNext(Mod mod) => AddStep($"left click {mod.Name}", () => modSelect.GetModButton(mod)?.SelectNext(1)); private void selectPrevious(Mod mod) => AddStep($"right click {mod.Name}", () => modSelect.GetModButton(mod)?.SelectNext(-1)); @@ -272,7 +261,6 @@ namespace osu.Game.Tests.Visual.UserInterface } public new OsuSpriteText MultiplierLabel => base.MultiplierLabel; - public new OsuSpriteText UnrankedLabel => base.UnrankedLabel; public new TriangleButton DeselectAllButton => base.DeselectAllButton; public new Color4 LowMultiplierColour => base.LowMultiplierColour; diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 36d21c8b46..914e730c27 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -37,7 +37,6 @@ namespace osu.Game.Overlays.Mods protected readonly TriangleButton CloseButton; protected readonly OsuSpriteText MultiplierLabel; - protected readonly OsuSpriteText UnrankedLabel; protected override bool BlockNonPositionalInput => false; @@ -268,30 +267,11 @@ namespace osu.Game.Overlays.Mods Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, }, - new FillFlowContainer + MultiplierLabel = new OsuSpriteText { - AutoSizeAxes = Axes.Both, + Font = OsuFont.GetFont(size: 25, weight: FontWeight.Bold, fixedWidth: true), Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, - Direction = FillDirection.Vertical, - LayoutDuration = 100, - LayoutEasing = Easing.OutQuint, - Children = new Drawable[] - { - MultiplierLabel = new OsuSpriteText - { - Font = OsuFont.GetFont(size: 25, weight: FontWeight.Bold, fixedWidth: true), - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - }, - UnrankedLabel = new OsuSpriteText - { - Text = @"(Unranked)", - Font = OsuFont.GetFont(size: 15, weight: FontWeight.Bold), - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - }, - } }, }, }, @@ -340,7 +320,6 @@ namespace osu.Game.Overlays.Mods { LowMultiplierColour = colours.Red; HighMultiplierColour = colours.Green; - UnrankedLabel.Colour = colours.Blue; availableMods = osu.AvailableMods.GetBoundCopy(); @@ -444,12 +423,10 @@ namespace osu.Game.Overlays.Mods private void updateMods() { var multiplier = 1.0; - var ranked = true; foreach (var mod in SelectedMods.Value) { multiplier *= mod.ScoreMultiplier; - ranked &= mod.Ranked; } MultiplierLabel.Text = $"{multiplier:N2}x"; @@ -459,8 +436,6 @@ namespace osu.Game.Overlays.Mods MultiplierLabel.FadeColour(LowMultiplierColour, 200); else MultiplierLabel.FadeColour(Color4.White, 200); - - UnrankedLabel.FadeTo(ranked ? 0 : 1, 200); } private void updateModSettings(ValueChangedEvent> selectedMods) From 1b9362041a4e4019c84f41f8230aca4f3d194459 Mon Sep 17 00:00:00 2001 From: Joehu Date: Sun, 26 Apr 2020 19:50:11 -0700 Subject: [PATCH 0468/1117] Revert multiplier number changes and set width Safe arbitrary width taken from "0.00x" (highest width of 67), rounded to the nearest tenth. --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 914e730c27..09f4befbc1 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -269,9 +269,10 @@ namespace osu.Game.Overlays.Mods }, MultiplierLabel = new OsuSpriteText { - Font = OsuFont.GetFont(size: 25, weight: FontWeight.Bold, fixedWidth: true), + Font = OsuFont.GetFont(size: 30, weight: FontWeight.Bold), Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, + Width = 70, // to avoid footer from flowing when clicking mods }, }, }, From dd36b839b9bb4bd558a7218e8e86c37334f86b5a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 27 Apr 2020 12:01:31 +0900 Subject: [PATCH 0469/1117] Refactor --- .../Objects/Drawables/DrawableDrumRollTick.cs | 2 - osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 38 ++++++++++--------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs index 536dd1209c..e12d1a0ba9 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs @@ -24,8 +24,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables FillMode = FillMode.Fit; } - public override bool DisplayResult => false; - protected override SkinnableDrawable CreateMainPiece() => new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.DrumRollTick), _ => new TickPiece { diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index d1d2571ec7..6103dea1fd 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -181,29 +181,11 @@ namespace osu.Game.Rulesets.Taiko.UI } } - private void playDrumrollHit(DrawableDrumRollTick drumrollTick) - { - TaikoAction action = drumrollTick.JudgedAction; - bool isStrong = drumrollTick.HitObject.IsStrong; - double time = drumrollTick.HitObject.GetEndTime(); - - DrawableHit drawableHit; - if (action == TaikoAction.LeftRim || action == TaikoAction.RightRim) - drawableHit = new DrawableFlyingRimHit(time, isStrong); - else - drawableHit = new DrawableFlyingCentreHit(time, isStrong); - - drumRollHitContainer.Add(drawableHit); - } - internal void OnNewResult(DrawableHitObject judgedObject, JudgementResult result) { if (!DisplayJudgements.Value) return; - if ((judgedObject is DrawableDrumRollTick) && result.Type != HitResult.Miss) - playDrumrollHit((DrawableDrumRollTick)judgedObject); - if (!judgedObject.DisplayResult) return; @@ -214,6 +196,11 @@ namespace osu.Game.Rulesets.Taiko.UI hitExplosionContainer.Children.FirstOrDefault(e => e.JudgedObject == ((DrawableStrongNestedHit)judgedObject).MainObject)?.VisualiseSecondHit(); break; + case TaikoDrumRollTickJudgement _: + if (result.IsHit) + playDrumrollHit((DrawableDrumRollTick)judgedObject); + break; + default: judgementContainer.Add(new DrawableTaikoJudgement(result, judgedObject) { @@ -237,6 +224,21 @@ namespace osu.Game.Rulesets.Taiko.UI } } + private void playDrumrollHit(DrawableDrumRollTick drumrollTick) + { + TaikoAction action = drumrollTick.JudgedAction; + bool isStrong = drumrollTick.HitObject.IsStrong; + double time = drumrollTick.HitObject.GetEndTime(); + + DrawableHit drawableHit; + if (action == TaikoAction.LeftRim || action == TaikoAction.RightRim) + drawableHit = new DrawableFlyingRimHit(time, isStrong); + else + drawableHit = new DrawableFlyingCentreHit(time, isStrong); + + drumRollHitContainer.Add(drawableHit); + } + private class ProxyContainer : LifetimeManagementContainer { public new MarginPadding Padding From 81df22d2a77b1b85d4c14ade4da13532ede7a3f3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 27 Apr 2020 12:17:36 +0900 Subject: [PATCH 0470/1117] Improve test scene --- osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs index c2ca578dfa..1822180795 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Taiko.Tests [TestFixture] public class TestSceneHits : OsuTestScene { - private const double default_duration = 1000; + private const double default_duration = 3000; private const float scroll_time = 1000; protected override double TimePerAction => default_duration * 2; @@ -45,6 +45,7 @@ namespace osu.Game.Rulesets.Taiko.Tests AddStep("Miss :(", addMissJudgement); AddStep("DrumRoll", () => addDrumRoll(false)); AddStep("Strong DrumRoll", () => addDrumRoll(true)); + AddStep("Kiai DrumRoll", () => addDrumRoll(true, kiai: true)); AddStep("Swell", () => addSwell()); AddStep("Centre", () => addCentreHit(false)); AddStep("Strong Centre", () => addCentreHit(true)); @@ -192,7 +193,7 @@ namespace osu.Game.Rulesets.Taiko.Tests drawableRuleset.Playfield.Add(new DrawableSwell(swell)); } - private void addDrumRoll(bool strong, double duration = default_duration) + private void addDrumRoll(bool strong, double duration = default_duration, bool kiai = false) { addBarLine(true); addBarLine(true, scroll_time + duration); @@ -202,9 +203,13 @@ namespace osu.Game.Rulesets.Taiko.Tests StartTime = drawableRuleset.Playfield.Time.Current + scroll_time, IsStrong = strong, Duration = duration, + TickRate = 8, }; - d.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); + var cpi = new ControlPointInfo(); + cpi.Add(-10000, new EffectControlPoint { KiaiMode = kiai }); + + d.ApplyDefaults(cpi, new BeatmapDifficulty()); drawableRuleset.Playfield.Add(new DrawableDrumRoll(d)); } From 7dc090cc24ccd64f19aa5f010ded2c99871f176d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 27 Apr 2020 12:23:53 +0900 Subject: [PATCH 0471/1117] Add support for hit explosions --- .../Objects/Drawables/DrawableDrumRollTick.cs | 7 ++-- osu.Game.Rulesets.Taiko/UI/HitExplosion.cs | 10 +++--- .../UI/KiaiHitExplosion.cs | 10 +++--- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 34 +++++++++++-------- 4 files changed, 31 insertions(+), 30 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs index e12d1a0ba9..62405cf047 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs @@ -13,10 +13,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables public class DrawableDrumRollTick : DrawableTaikoHitObject { /// - /// The action type that the user took which caused this tick to - /// have been judged as "hit" + /// The hit type corresponding to the that the user pressed to hit this . /// - public TaikoAction JudgedAction; + public HitType JudgementType; public DrawableDrumRollTick(DrumRollTick tick) : base(tick) @@ -57,7 +56,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables public override bool OnPressed(TaikoAction action) { - JudgedAction = action; + JudgementType = action == TaikoAction.LeftRim || action == TaikoAction.RightRim ? HitType.Rim : HitType.Centre; return UpdateResult(true); } diff --git a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs index d4118d38b6..fbaae7e322 100644 --- a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs @@ -21,16 +21,14 @@ namespace osu.Game.Rulesets.Taiko.UI public override bool RemoveWhenNotAlive => true; public readonly DrawableHitObject JudgedObject; + private readonly HitType type; private readonly Box innerFill; - private readonly bool isRim; - - public HitExplosion(DrawableHitObject judgedObject, bool isRim) + public HitExplosion(DrawableHitObject judgedObject, HitType type) { - this.isRim = isRim; - JudgedObject = judgedObject; + this.type = type; Anchor = Anchor.Centre; Origin = Anchor.Centre; @@ -58,7 +56,7 @@ namespace osu.Game.Rulesets.Taiko.UI [BackgroundDependencyLoader] private void load(OsuColour colours) { - innerFill.Colour = isRim ? colours.BlueDarker : colours.PinkDarker; + innerFill.Colour = type == HitType.Rim ? colours.BlueDarker : colours.PinkDarker; } protected override void LoadComplete() diff --git a/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs index e80b463481..3a307bb3bb 100644 --- a/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs @@ -18,14 +18,12 @@ namespace osu.Game.Rulesets.Taiko.UI public override bool RemoveWhenNotAlive => true; public readonly DrawableHitObject JudgedObject; + private readonly HitType type; - private readonly bool isRim; - - public KiaiHitExplosion(DrawableHitObject judgedObject, bool isRim) + public KiaiHitExplosion(DrawableHitObject judgedObject, HitType type) { - this.isRim = isRim; - JudgedObject = judgedObject; + this.type = type; Anchor = Anchor.CentreLeft; Origin = Anchor.Centre; @@ -53,7 +51,7 @@ namespace osu.Game.Rulesets.Taiko.UI EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Glow, - Colour = isRim ? colours.BlueDarker : colours.PinkDarker, + Colour = type == HitType.Rim ? colours.BlueDarker : colours.PinkDarker, Radius = 60, }; } diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 6103dea1fd..a19c08ae7e 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -10,7 +10,6 @@ using osu.Game.Graphics; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Rulesets.Taiko.Objects.Drawables; @@ -197,8 +196,13 @@ namespace osu.Game.Rulesets.Taiko.UI break; case TaikoDrumRollTickJudgement _: - if (result.IsHit) - playDrumrollHit((DrawableDrumRollTick)judgedObject); + if (!result.IsHit) + return; + + var drawableTick = (DrawableDrumRollTick)judgedObject; + + addDrumRollHit(drawableTick); + addExplosion(drawableTick, drawableTick.JudgementType); break; default: @@ -213,25 +217,19 @@ namespace osu.Game.Rulesets.Taiko.UI if (!result.IsHit) break; - bool isRim = (judgedObject.HitObject as Hit)?.Type == HitType.Rim; - - hitExplosionContainer.Add(new HitExplosion(judgedObject, isRim)); - - if (judgedObject.HitObject.Kiai) - kiaiExplosionContainer.Add(new KiaiHitExplosion(judgedObject, isRim)); + addExplosion(judgedObject, (judgedObject.HitObject as Hit)?.Type ?? HitType.Centre); break; } } - private void playDrumrollHit(DrawableDrumRollTick drumrollTick) + private void addDrumRollHit(DrawableDrumRollTick drawableTick) { - TaikoAction action = drumrollTick.JudgedAction; - bool isStrong = drumrollTick.HitObject.IsStrong; - double time = drumrollTick.HitObject.GetEndTime(); + bool isStrong = drawableTick.HitObject.IsStrong; + double time = drawableTick.HitObject.GetEndTime(); DrawableHit drawableHit; - if (action == TaikoAction.LeftRim || action == TaikoAction.RightRim) + if (drawableTick.JudgementType == HitType.Rim) drawableHit = new DrawableFlyingRimHit(time, isStrong); else drawableHit = new DrawableFlyingCentreHit(time, isStrong); @@ -239,6 +237,14 @@ namespace osu.Game.Rulesets.Taiko.UI drumRollHitContainer.Add(drawableHit); } + private void addExplosion(DrawableHitObject drawableObject, HitType type) + { + hitExplosionContainer.Add(new HitExplosion(drawableObject, type)); + + if (drawableObject.HitObject.Kiai) + kiaiExplosionContainer.Add(new KiaiHitExplosion(drawableObject, type)); + } + private class ProxyContainer : LifetimeManagementContainer { public new MarginPadding Padding From 2630fc1405260ce60ad9d7aa533c483d90a91147 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 27 Apr 2020 12:27:43 +0900 Subject: [PATCH 0472/1117] Break instead of return for consistency --- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index a19c08ae7e..1fce70122d 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -197,7 +197,7 @@ namespace osu.Game.Rulesets.Taiko.UI case TaikoDrumRollTickJudgement _: if (!result.IsHit) - return; + break; var drawableTick = (DrawableDrumRollTick)judgedObject; @@ -218,7 +218,6 @@ namespace osu.Game.Rulesets.Taiko.UI break; addExplosion(judgedObject, (judgedObject.HitObject as Hit)?.Type ?? HitType.Centre); - break; } } From 20ae973e4afba59b5e87645dffbf7964b0ea0676 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 27 Apr 2020 12:29:11 +0900 Subject: [PATCH 0473/1117] Use max result instead of GOOD --- .../Objects/Drawables/DrawableFlyingCentreHit.cs | 3 +-- .../Objects/Drawables/DrawableFlyingRimHit.cs | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingCentreHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingCentreHit.cs index f70d940bd2..105ff62812 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingCentreHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingCentreHit.cs @@ -3,7 +3,6 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Taiko.Objects.Drawables { @@ -12,7 +11,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables protected override void LoadComplete() { base.LoadComplete(); - ApplyResult(r => r.Type = HitResult.Good); + ApplyResult(r => r.Type = r.Judgement.MaxResult); } public DrawableFlyingCentreHit(double time, bool isStrong = false) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingRimHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingRimHit.cs index 9005dac653..e8cbd6e54f 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingRimHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingRimHit.cs @@ -3,7 +3,6 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Taiko.Objects.Drawables { @@ -12,7 +11,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables protected override void LoadComplete() { base.LoadComplete(); - ApplyResult(r => r.Type = HitResult.Good); + ApplyResult(r => r.Type = r.Judgement.MaxResult); } public DrawableFlyingRimHit(double time, bool isStrong = false) From 7731d45f13ce88392716c640dbdaa79b23d965ce Mon Sep 17 00:00:00 2001 From: Joehu Date: Sun, 26 Apr 2020 20:30:56 -0700 Subject: [PATCH 0474/1117] Remove unnecessary usings --- .../Visual/UserInterface/TestSceneModSelectOverlay.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index 769c660f48..ec6ee6bc83 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -14,8 +14,6 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Mods; using osu.Game.Overlays.Mods.Sections; using osu.Game.Rulesets; -using osu.Game.Rulesets.Mania; -using osu.Game.Rulesets.Mania.Mods; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; From ff736a22dd9f4e1835b07ec8f25c7507045ed8b3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 27 Apr 2020 14:41:19 +0900 Subject: [PATCH 0475/1117] Fix typos in comment --- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 1fce70122d..cd05d8e2f9 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -150,8 +150,8 @@ namespace osu.Game.Rulesets.Taiko.UI rightArea.Padding = new MarginPadding { Left = leftArea.DrawWidth }; hitTargetOffsetContent.Padding = new MarginPadding { Left = HitTarget.DrawWidth / 2 }; - // When rewinding, make sure to remove any auxilliary hit notes that were - // spawned and played during a drumroll. + // When rewinding, make sure to remove any auxiliary hit notes that were + // spawned and played during a drum roll. if (Time.Elapsed < 0) { foreach (var o in drumRollHitContainer.Objects) From b9f28c83731a4ca823c0090f654fab57d9da979f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 27 Apr 2020 16:13:28 +0900 Subject: [PATCH 0476/1117] Combine hit types and remove old drumroll hits using a more efficient method --- .../Skinning/TestSceneDrawableHit.cs | 10 +++--- .../TestSceneHits.cs | 4 +-- .../Objects/Drawables/DrawableCentreHit.cs | 21 ----------- ...lyingCentreHit.cs => DrawableFlyingHit.cs} | 7 ++-- .../Objects/Drawables/DrawableFlyingRimHit.cs | 23 ------------ .../Objects/Drawables/DrawableHit.cs | 26 +++++++++----- .../Objects/Drawables/DrawableRimHit.cs | 21 ----------- .../UI/DrawableTaikoRuleset.cs | 5 +-- .../UI/DrumRollHitContainer.cs | 35 +++++++++++++++++++ osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 28 ++------------- 10 files changed, 67 insertions(+), 113 deletions(-) delete mode 100644 osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs rename osu.Game.Rulesets.Taiko/Objects/Drawables/{DrawableFlyingCentreHit.cs => DrawableFlyingHit.cs} (72%) delete mode 100644 osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingRimHit.cs delete mode 100644 osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs create mode 100644 osu.Game.Rulesets.Taiko/UI/DrumRollHitContainer.cs diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableHit.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableHit.cs index 6d6da1fb5b..6a3c98a514 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableHit.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableHit.cs @@ -21,8 +21,6 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(DrawableHit), - typeof(DrawableCentreHit), - typeof(DrawableRimHit), typeof(LegacyHit), typeof(LegacyCirclePiece), }).ToList(); @@ -30,25 +28,25 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning [BackgroundDependencyLoader] private void load() { - AddStep("Centre hit", () => SetContents(() => new DrawableCentreHit(createHitAtCurrentTime()) + AddStep("Centre hit", () => SetContents(() => new DrawableHit(createHitAtCurrentTime()) { Anchor = Anchor.Centre, Origin = Anchor.Centre, })); - AddStep("Centre hit (strong)", () => SetContents(() => new DrawableCentreHit(createHitAtCurrentTime(true)) + AddStep("Centre hit (strong)", () => SetContents(() => new DrawableHit(createHitAtCurrentTime(true)) { Anchor = Anchor.Centre, Origin = Anchor.Centre, })); - AddStep("Rim hit", () => SetContents(() => new DrawableRimHit(createHitAtCurrentTime()) + AddStep("Rim hit", () => SetContents(() => new DrawableHit(createHitAtCurrentTime()) { Anchor = Anchor.Centre, Origin = Anchor.Centre, })); - AddStep("Rim hit (strong)", () => SetContents(() => new DrawableRimHit(createHitAtCurrentTime(true)) + AddStep("Rim hit (strong)", () => SetContents(() => new DrawableHit(createHitAtCurrentTime(true)) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs index 1822180795..23adb79083 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs @@ -224,7 +224,7 @@ namespace osu.Game.Rulesets.Taiko.Tests h.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); - drawableRuleset.Playfield.Add(new DrawableCentreHit(h)); + drawableRuleset.Playfield.Add(new DrawableHit(h)); } private void addRimHit(bool strong) @@ -237,7 +237,7 @@ namespace osu.Game.Rulesets.Taiko.Tests h.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); - drawableRuleset.Playfield.Add(new DrawableRimHit(h)); + drawableRuleset.Playfield.Add(new DrawableHit(h)); } private class TestStrongNestedHit : DrawableStrongNestedHit diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs deleted file mode 100644 index a87da44415..0000000000 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.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. - -using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; -using osu.Game.Skinning; - -namespace osu.Game.Rulesets.Taiko.Objects.Drawables -{ - public class DrawableCentreHit : DrawableHit - { - public override TaikoAction[] HitActions { get; } = { TaikoAction.LeftCentre, TaikoAction.RightCentre }; - - public DrawableCentreHit(Hit hit) - : base(hit) - { - } - - protected override SkinnableDrawable CreateMainPiece() => new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.CentreHit), - _ => new CentreHitCirclePiece(), confineMode: ConfineMode.ScaleToFit); - } -} diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingCentreHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingHit.cs similarity index 72% rename from osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingCentreHit.cs rename to osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingHit.cs index 105ff62812..86822e3ae8 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingCentreHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingHit.cs @@ -6,7 +6,10 @@ using osu.Game.Beatmaps.ControlPoints; namespace osu.Game.Rulesets.Taiko.Objects.Drawables { - public class DrawableFlyingCentreHit : DrawableCentreHit + /// + /// A hit used specifically for drum rolls, where spawning flying hits is required. + /// + public class DrawableFlyingHit : DrawableHit { protected override void LoadComplete() { @@ -14,7 +17,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables ApplyResult(r => r.Type = r.Judgement.MaxResult); } - public DrawableFlyingCentreHit(double time, bool isStrong = false) + public DrawableFlyingHit(double time, bool isStrong = false) : base(new IgnoreHit { StartTime = time, IsStrong = isStrong }) { HitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingRimHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingRimHit.cs deleted file mode 100644 index e8cbd6e54f..0000000000 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingRimHit.cs +++ /dev/null @@ -1,23 +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.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; - -namespace osu.Game.Rulesets.Taiko.Objects.Drawables -{ - public class DrawableFlyingRimHit : DrawableRimHit - { - protected override void LoadComplete() - { - base.LoadComplete(); - ApplyResult(r => r.Type = r.Judgement.MaxResult); - } - - public DrawableFlyingRimHit(double time, bool isStrong = false) - : base(new IgnoreHit { StartTime = time, IsStrong = isStrong }) - { - HitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); - } - } -} diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index fe9a89f2be..d2671eadda 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -8,31 +8,45 @@ using osu.Framework.Graphics; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; +using osu.Game.Skinning; namespace osu.Game.Rulesets.Taiko.Objects.Drawables { - public abstract class DrawableHit : DrawableTaikoHitObject + public class DrawableHit : DrawableTaikoHitObject { /// /// A list of keys which can result in hits for this HitObject. /// - public abstract TaikoAction[] HitActions { get; } + public TaikoAction[] HitActions { get; } /// /// The action that caused this to be hit. /// - public TaikoAction? HitAction { get; private set; } + public TaikoAction? HitAction + { + get; + private set; + } private bool validActionPressed; private bool pressHandledThisFrame; - protected DrawableHit(Hit hit) + public DrawableHit(Hit hit) : base(hit) { FillMode = FillMode.Fit; + + HitActions = + HitObject.Type == HitType.Centre + ? new[] { TaikoAction.LeftCentre, TaikoAction.RightCentre } + : new[] { TaikoAction.LeftRim, TaikoAction.RightRim }; } + protected override SkinnableDrawable CreateMainPiece() => HitObject.Type == HitType.Centre + ? new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.CentreHit), _ => new CentreHitCirclePiece(), confineMode: ConfineMode.ScaleToFit) + : new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.RimHit), _ => new RimHitCirclePiece(), confineMode: ConfineMode.ScaleToFit); + protected override void CheckForResult(bool userTriggered, double timeOffset) { Debug.Assert(HitObject.HitWindows != null); @@ -58,7 +72,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { if (pressHandledThisFrame) return true; - if (Judged) return false; @@ -66,14 +79,12 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables // Only count this as handled if the new judgement is a hit var result = UpdateResult(true); - if (IsHit) HitAction = action; // Regardless of whether we've hit or not, any secondary key presses in the same frame should be discarded // E.g. hitting a non-strong centre as a strong should not fall through and perform a hit on the next note pressHandledThisFrame = true; - return result; } @@ -81,7 +92,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { if (action == HitAction) HitAction = null; - base.OnReleased(action); } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs deleted file mode 100644 index f767403c65..0000000000 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.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. - -using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; -using osu.Game.Skinning; - -namespace osu.Game.Rulesets.Taiko.Objects.Drawables -{ - public class DrawableRimHit : DrawableHit - { - public override TaikoAction[] HitActions { get; } = { TaikoAction.LeftRim, TaikoAction.RightRim }; - - public DrawableRimHit(Hit hit) - : base(hit) - { - } - - protected override SkinnableDrawable CreateMainPiece() => new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.RimHit), - _ => new RimHitCirclePiece(), confineMode: ConfineMode.ScaleToFit); - } -} diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs index e4a4b555a7..a6a00fe242 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs @@ -49,10 +49,7 @@ namespace osu.Game.Rulesets.Taiko.UI switch (h) { case Hit hit: - if (hit.Type == HitType.Centre) - return new DrawableCentreHit(hit); - else - return new DrawableRimHit(hit); + return new DrawableHit(hit); case DrumRoll drumRoll: return new DrawableDrumRoll(drumRoll); diff --git a/osu.Game.Rulesets.Taiko/UI/DrumRollHitContainer.cs b/osu.Game.Rulesets.Taiko/UI/DrumRollHitContainer.cs new file mode 100644 index 0000000000..9ef944b412 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/UI/DrumRollHitContainer.cs @@ -0,0 +1,35 @@ +// 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.Containers; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Taiko.Objects.Drawables; +using osu.Game.Rulesets.UI.Scrolling; + +namespace osu.Game.Rulesets.Taiko.UI +{ + internal class DrumRollHitContainer : ScrollingHitObjectContainer + { + protected override void Update() + { + base.Update(); + + // Remove any auxiliary hit notes that were spawned during a drum roll but subsequently rewound. + for (var i = AliveInternalChildren.Count - 1; i >= 0; i--) + { + var flyingHit = (DrawableFlyingHit)AliveInternalChildren[i]; + if (Time.Current < flyingHit.HitObject.StartTime) + Remove(flyingHit); + } + } + + protected override void OnChildLifetimeBoundaryCrossed(LifetimeBoundaryCrossedEvent e) + { + base.OnChildLifetimeBoundaryCrossed(e); + + // ensure all old hits are removed on becoming alive (may miss being in the AliveInternalChildren list above). + if (e.Kind == LifetimeBoundaryKind.Start && e.Direction == LifetimeBoundaryCrossingDirection.Backward) + Remove((DrawableHitObject)e.Child); + } + } +} diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index cd05d8e2f9..28706ef0d3 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -93,10 +93,7 @@ namespace osu.Game.Rulesets.Taiko.UI Children = new Drawable[] { HitObjectContainer, - drumRollHitContainer = new ScrollingHitObjectContainer - { - Name = "Drumroll hit" - } + drumRollHitContainer = new DrumRollHitContainer() } }, kiaiExplosionContainer = new Container @@ -149,23 +146,11 @@ namespace osu.Game.Rulesets.Taiko.UI // This is basically allowing for correct alignment as relative pieces move around them. rightArea.Padding = new MarginPadding { Left = leftArea.DrawWidth }; hitTargetOffsetContent.Padding = new MarginPadding { Left = HitTarget.DrawWidth / 2 }; - - // When rewinding, make sure to remove any auxiliary hit notes that were - // spawned and played during a drum roll. - if (Time.Elapsed < 0) - { - foreach (var o in drumRollHitContainer.Objects) - { - if (o.HitObject.StartTime >= Time.Current) - drumRollHitContainer.Remove(o); - } - } } public override void Add(DrawableHitObject h) { h.OnNewResult += OnNewResult; - base.Add(h); switch (h) @@ -184,7 +169,6 @@ namespace osu.Game.Rulesets.Taiko.UI { if (!DisplayJudgements.Value) return; - if (!judgedObject.DisplayResult) return; @@ -226,20 +210,12 @@ namespace osu.Game.Rulesets.Taiko.UI { bool isStrong = drawableTick.HitObject.IsStrong; double time = drawableTick.HitObject.GetEndTime(); - - DrawableHit drawableHit; - if (drawableTick.JudgementType == HitType.Rim) - drawableHit = new DrawableFlyingRimHit(time, isStrong); - else - drawableHit = new DrawableFlyingCentreHit(time, isStrong); - - drumRollHitContainer.Add(drawableHit); + drumRollHitContainer.Add(new DrawableFlyingHit(time, isStrong)); } private void addExplosion(DrawableHitObject drawableObject, HitType type) { hitExplosionContainer.Add(new HitExplosion(drawableObject, type)); - if (drawableObject.HitObject.Kiai) kiaiExplosionContainer.Add(new KiaiHitExplosion(drawableObject, type)); } From 52cf1e18590fbef3e2f221e19a2dc126af2f4ad5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 27 Apr 2020 16:48:17 +0900 Subject: [PATCH 0477/1117] Fix hit type not being provided and hit time offset not being considered --- .../Objects/Drawables/DrawableFlyingHit.cs | 17 +++++++++++------ .../UI/DrumRollHitContainer.cs | 2 +- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 9 ++------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingHit.cs index 86822e3ae8..460e760629 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingHit.cs @@ -11,16 +11,21 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables ///
  • public class DrawableFlyingHit : DrawableHit { + public DrawableFlyingHit(DrawableDrumRollTick drumRollTick) + : base(new IgnoreHit + { + StartTime = drumRollTick.HitObject.StartTime + drumRollTick.Result.TimeOffset, + IsStrong = drumRollTick.HitObject.IsStrong, + Type = drumRollTick.JudgementType + }) + { + HitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); + } + protected override void LoadComplete() { base.LoadComplete(); ApplyResult(r => r.Type = r.Judgement.MaxResult); } - - public DrawableFlyingHit(double time, bool isStrong = false) - : base(new IgnoreHit { StartTime = time, IsStrong = isStrong }) - { - HitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); - } } } diff --git a/osu.Game.Rulesets.Taiko/UI/DrumRollHitContainer.cs b/osu.Game.Rulesets.Taiko/UI/DrumRollHitContainer.cs index 9ef944b412..fde42bec04 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrumRollHitContainer.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrumRollHitContainer.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Taiko.UI for (var i = AliveInternalChildren.Count - 1; i >= 0; i--) { var flyingHit = (DrawableFlyingHit)AliveInternalChildren[i]; - if (Time.Current < flyingHit.HitObject.StartTime) + if (Time.Current <= flyingHit.HitObject.StartTime) Remove(flyingHit); } } diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 28706ef0d3..4bc6cb8e4b 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -9,7 +9,6 @@ using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Rulesets.Taiko.Objects.Drawables; @@ -206,12 +205,8 @@ namespace osu.Game.Rulesets.Taiko.UI } } - private void addDrumRollHit(DrawableDrumRollTick drawableTick) - { - bool isStrong = drawableTick.HitObject.IsStrong; - double time = drawableTick.HitObject.GetEndTime(); - drumRollHitContainer.Add(new DrawableFlyingHit(time, isStrong)); - } + private void addDrumRollHit(DrawableDrumRollTick drawableTick) => + drumRollHitContainer.Add(new DrawableFlyingHit(drawableTick)); private void addExplosion(DrawableHitObject drawableObject, HitType type) { From acf95fca9cdd8b12027d907838738fa0216845b4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 27 Apr 2020 17:14:00 +0900 Subject: [PATCH 0478/1117] Remove old, now unnecessary method --- .../Edit/ManiaSelectionHandler.cs | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs index 78f159b733..d290e4ec24 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs @@ -35,35 +35,12 @@ namespace osu.Game.Rulesets.Mania.Edit var maniaBlueprint = (ManiaSelectionBlueprint)moveEvent.Blueprint; int lastColumn = maniaBlueprint.DrawableObject.HitObject.Column; - // adjustOrigins(maniaBlueprint); performDragMovement(moveEvent); performColumnMovement(lastColumn, moveEvent); return true; } - /// - /// Ensures that the position of hitobjects remains centred to the mouse position. - /// E.g. The hitobject position will change if the editor scrolls while a hitobject is dragged. - /// - /// The that received the drag event. - private void adjustOrigins(ManiaSelectionBlueprint reference) - { - var referenceParent = (HitObjectContainer)reference.DrawableObject.Parent; - - float offsetFromReferenceOrigin = reference.DragPosition.Y - reference.DrawableObject.OriginPosition.Y; - float targetPosition = referenceParent.ToLocalSpace(reference.ScreenSpaceDragPosition).Y - offsetFromReferenceOrigin; - - // Flip the vertical coordinate space when scrolling downwards - if (scrollingInfo.Direction.Value == ScrollingDirection.Down) - targetPosition -= referenceParent.DrawHeight; - - float movementDelta = targetPosition - reference.DrawableObject.Position.Y; - - foreach (var b in SelectedBlueprints.OfType()) - b.DrawableObject.Y += movementDelta; - } - private void performDragMovement(MoveSelectionEvent moveEvent) { float delta = moveEvent.InstantDelta.Y; From 03863d901b221e31baa22af9f65de9490ae276d4 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 27 Apr 2020 08:59:09 +0000 Subject: [PATCH 0479/1117] Bump Microsoft.NET.Test.Sdk from 16.5.0 to 16.6.1 Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 16.5.0 to 16.6.1. - [Release notes](https://github.com/microsoft/vstest/releases) - [Commits](https://github.com/microsoft/vstest/compare/v16.5.0...v16.6.1) Signed-off-by: dependabot-preview[bot] --- .../osu.Game.Rulesets.Catch.Tests.csproj | 2 +- .../osu.Game.Rulesets.Mania.Tests.csproj | 2 +- osu.Game.Rulesets.Osu.Tests/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/osu.Game.Tournament.Tests.csproj | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) 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 8c371db257..cbd3dc5518 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 6855b99f28..77c871718b 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 217707b180..2fcfa1deb7 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 @@ -2,7 +2,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 f6054a5d6f..28b8476a22 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 35eb3fa161..5ee887cb64 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -3,7 +3,7 @@ - + diff --git a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj index 3b45fc83fd..aa37326a49 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 @@ - + From c0b225ffc8e5c27bc4c624468994f61b698daab5 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 27 Apr 2020 08:59:34 +0000 Subject: [PATCH 0480/1117] Bump ppy.osu.Game.Resources from 2020.412.0 to 2020.427.0 Bumps [ppy.osu.Game.Resources](https://github.com/ppy/osu-resources) from 2020.412.0 to 2020.427.0. - [Release notes](https://github.com/ppy/osu-resources/releases) - [Commits](https://github.com/ppy/osu-resources/compare/2020.412.0...2020.427.0) Signed-off-by: dependabot-preview[bot] --- 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 25942863c5..73fbe3ab2e 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 9c17c453a6..3b05eb82d7 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -24,7 +24,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 07ea4b9c2a..f3202693f3 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -71,7 +71,7 @@ - + From ca055581af91975ed44438b25863ad0b33c7e9c8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 27 Apr 2020 18:18:11 +0900 Subject: [PATCH 0481/1117] Fix taiko hit target alpha on legacy skins --- osu.Game.Rulesets.Taiko/Skinning/TaikoLegacyHitTarget.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacyHitTarget.cs b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacyHitTarget.cs index 7c1e65f569..e522fb7c10 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacyHitTarget.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacyHitTarget.cs @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning { Texture = skin.GetTexture("approachcircle"), Scale = new Vector2(0.73f), - Alpha = 0.7f, + Alpha = 0.47f, // eyeballed to match stable Anchor = Anchor.Centre, Origin = Anchor.Centre, }, @@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning { Texture = skin.GetTexture("taikobigcircle"), Scale = new Vector2(0.7f), - Alpha = 0.5f, + Alpha = 0.22f, // eyeballed to match stable Anchor = Anchor.Centre, Origin = Anchor.Centre, }, From b88dd442526c8a58a9bd992101b5d7b5da33e3a6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 27 Apr 2020 19:47:25 +0900 Subject: [PATCH 0482/1117] Fix movement not working correctly in down-scroll --- .../Blueprints/ManiaSelectionBlueprint.cs | 20 +++++++++++++++++++ .../Edit/ManiaSelectionHandler.cs | 19 +++++++----------- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs index 9f57160f99..14d52dd08e 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs @@ -74,5 +74,25 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints DrawableObject.AlwaysAlive = false; base.Hide(); } + + public override Vector2 GetInstantDelta(Vector2 screenSpacePosition) + { + var baseDelta = base.GetInstantDelta(screenSpacePosition); + + if (scrollingInfo.Direction.Value == ScrollingDirection.Down) + { + // The parent of DrawableObject is the scrolling hitobject container (SHOC). + // In the coordinate-space of the SHOC, the screen-space position at the hit target is equal to the height of the SHOC, + // but this is not what we want as it means a slight movement downwards results in a delta greater than the height of the SHOC. + // To get around this issue, the height of the SHOC is subtracted from the delta. + // + // Ideally this should be a _negative_ value in the case described above, however this code gives a _positive_ delta. + // This is intentional as the delta is added to the hitobject's position (see: ManiaSelectionHandler) and a negative delta would move them towards the top of the screen instead, + // which would cause the delta to get increasingly larger as additional movements are performed. + return new Vector2(baseDelta.X, baseDelta.Y - DrawableObject.Parent.DrawHeight); + } + + return baseDelta; + } } } diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs index d290e4ec24..8dfc97f87a 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs @@ -45,11 +45,6 @@ namespace osu.Game.Rulesets.Mania.Edit { float delta = moveEvent.InstantDelta.Y; - // When scrolling downwards the anchor position is at the bottom of the screen, however the movement event assumes the anchor is at the top of the screen. - // This causes the delta to assume a positive hitobject position, and which can be corrected for by subtracting the parent height. - if (scrollingInfo.Direction.Value == ScrollingDirection.Down) - delta -= moveEvent.Blueprint.Parent.DrawHeight; // todo: definitely wrong - foreach (var selectionBlueprint in SelectedBlueprints) { var b = (OverlaySelectionBlueprint)selectionBlueprint; @@ -57,24 +52,24 @@ namespace osu.Game.Rulesets.Mania.Edit var hitObject = b.DrawableObject; var objectParent = (HitObjectContainer)hitObject.Parent; - // StartTime could be used to adjust the position if only one movement event was received per frame. - // However this is not the case and ScrollingHitObjectContainer performs movement in UpdateAfterChildren() so the position must also be updated to be valid for further movement events + // We receive multiple movement events per frame such that we can't rely on updating the start time + // since the scrolling hitobject container requires at least one update frame to update the position. + // However the position needs to be valid for future movement events to calculate the correct deltas. hitObject.Y += delta; float targetPosition = hitObject.Position.Y; - // The scrolling algorithm always assumes an anchor at the top of the screen, so the position must be flipped when scrolling downwards to reflect a top anchor if (scrollingInfo.Direction.Value == ScrollingDirection.Down) + { + // When scrolling downwards, the position is _negative_ when the object's start time is after the current time (e.g. in the middle of the stage). + // However all scrolling algorithms upwards scrolling, meaning that a positive (inverse) position is expected in the same scenario. targetPosition = -targetPosition; - - objectParent.Remove(hitObject); + } hitObject.HitObject.StartTime = scrollingInfo.Algorithm.TimeAt(targetPosition, editorClock.CurrentTime, scrollingInfo.TimeRange.Value, objectParent.DrawHeight); - - objectParent.Add(hitObject); } } From ece6e2db5cca33bacff5301804a18cc6b9dd3eaa Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Mon, 27 Apr 2020 13:12:31 +0200 Subject: [PATCH 0483/1117] Change removed class --- .../Skinning/TestSceneDrawableTaikoMascot.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs index d11bfa05ba..93b5803e87 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs @@ -193,7 +193,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning private void addJudgement(TaikoPlayfield playfield, HitResult result) { - playfield.OnNewResult(new DrawableRimHit(new Hit()), new JudgementResult(new HitObject(), new TaikoJudgement()) { Type = result }); + playfield.OnNewResult(new DrawableHit(new Hit()), new JudgementResult(new HitObject(), new TaikoJudgement()) { Type = result }); } private class TestDrawableTaikoMascot : DrawableTaikoMascot From cebc0fc0466d61db6f22c19358c8d22ce5f12d09 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 27 Apr 2020 20:35:24 +0900 Subject: [PATCH 0484/1117] Attempt to fix multiple selection movements --- .../Edit/ManiaHitObjectComposer.cs | 28 ++++++++++++++++++- .../Edit/ManiaSelectionHandler.cs | 14 ---------- osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs | 16 +++++++++++ .../Compose/Components/BlueprintContainer.cs | 10 +++---- 4 files changed, 47 insertions(+), 21 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index 62b609610f..8d012c63ac 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -10,6 +10,7 @@ using osu.Framework.Allocation; using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI; +using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Screens.Edit.Compose.Components; using osuTK; @@ -37,7 +38,32 @@ namespace osu.Game.Rulesets.Mania.Edit protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); - public int TotalColumns => ((ManiaPlayfield)drawableRuleset.Playfield).TotalColumns; + public ManiaPlayfield Playfield => ((ManiaPlayfield)drawableRuleset.Playfield); + + public int TotalColumns => Playfield.TotalColumns; + + public override (Vector2 position, double time) GetSnappedPosition(Vector2 position, double time) + { + var hoc = Playfield.GetColumn(0).HitObjectContainer; + + position.Y -= ToLocalSpace(hoc.ScreenSpaceDrawQuad.TopLeft).Y; + + float targetPosition = position.Y; + + if (drawableRuleset.ScrollingInfo.Direction.Value == ScrollingDirection.Down) + { + // When scrolling downwards, the position is _negative_ when the object's start time is after the current time (e.g. in the middle of the stage). + // However all scrolling algorithms upwards scrolling, meaning that a positive (inverse) position is expected in the same scenario. + targetPosition = -targetPosition; + } + + double targetTime = drawableRuleset.ScrollingInfo.Algorithm.TimeAt(targetPosition, + EditorClock.CurrentTime, + drawableRuleset.ScrollingInfo.TimeRange.Value, + hoc.DrawHeight); + + return base.GetSnappedPosition(position, targetTime); + } protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) { diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs index 8dfc97f87a..989e0f5b01 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs @@ -56,20 +56,6 @@ namespace osu.Game.Rulesets.Mania.Edit // since the scrolling hitobject container requires at least one update frame to update the position. // However the position needs to be valid for future movement events to calculate the correct deltas. hitObject.Y += delta; - - float targetPosition = hitObject.Position.Y; - - if (scrollingInfo.Direction.Value == ScrollingDirection.Down) - { - // When scrolling downwards, the position is _negative_ when the object's start time is after the current time (e.g. in the middle of the stage). - // However all scrolling algorithms upwards scrolling, meaning that a positive (inverse) position is expected in the same scenario. - targetPosition = -targetPosition; - } - - hitObject.HitObject.StartTime = scrollingInfo.Algorithm.TimeAt(targetPosition, - editorClock.CurrentTime, - scrollingInfo.TimeRange.Value, - objectParent.DrawHeight); } } diff --git a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs index 2dec468654..f9faa262ed 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs @@ -87,6 +87,22 @@ namespace osu.Game.Rulesets.Mania.UI return found; } + public Column GetColumn(int index) + { + foreach (var stage in stages) + { + if (index >= stage.Columns.Count) + { + index -= stage.Columns.Count; + continue; + } + + return stage.Columns[index]; + } + + throw new ArgumentOutOfRangeException(nameof(index)); + } + /// /// Retrieves the total amount of columns across all stages in this playfield. /// diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 0823be01f8..8910684463 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -401,18 +401,16 @@ namespace osu.Game.Screens.Edit.Compose.Components HitObject draggedObject = movementBlueprint.HitObject; - // The final movement position, relative to movementBlueprintOriginalPosition + // The final movement position, relative to movementBlueprintOriginalPosition. Vector2 movePosition = movementBlueprintOriginalPosition.Value + e.ScreenSpaceMousePosition - e.ScreenSpaceMouseDownPosition; - (Vector2 snappedPosition, _) = snapProvider.GetSnappedPosition(ToLocalSpace(movePosition), draggedObject.StartTime); + // Retrieve a snapped position. + (Vector2 snappedPosition, double snappedTime) = snapProvider.GetSnappedPosition(ToLocalSpace(movePosition), draggedObject.StartTime); - // Move the hitobjects + // Move the hitobjects. if (!selectionHandler.HandleMovement(new MoveSelectionEvent(movementBlueprint, ToScreenSpace(snappedPosition)))) return true; - // Todo: Temp - (_, double snappedTime) = snapProvider.GetSnappedPosition(ToLocalSpace(snappedPosition), draggedObject.StartTime); - // Apply the start time at the newly snapped-to position double offset = snappedTime - draggedObject.StartTime; foreach (HitObject obj in selectionHandler.SelectedHitObjects) From be59ee945a5ead6fe5d8d7cbeaad058e8180bf7f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 27 Apr 2020 22:22:32 +0900 Subject: [PATCH 0485/1117] Add taiko hit explosion skinning support --- .../Skinning/TestSceneHitExplosion.cs | 79 +++++++++++++++++++ .../Skinning/LegacyHitExplosion.cs | 29 +++++++ .../Skinning/TaikoLegacySkinTransformer.cs | 27 +++++++ .../TaikoSkinComponents.cs | 5 +- .../UI/DefaultHitExplosion.cs | 54 +++++++++++++ osu.Game.Rulesets.Taiko/UI/HitExplosion.cs | 50 ++++++------ osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 7 +- 7 files changed, 221 insertions(+), 30 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneHitExplosion.cs create mode 100644 osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs create mode 100644 osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneHitExplosion.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneHitExplosion.cs new file mode 100644 index 0000000000..ca0c60b67a --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneHitExplosion.cs @@ -0,0 +1,79 @@ +// 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.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Taiko.Objects.Drawables; +using osu.Game.Rulesets.Taiko.Skinning; +using osu.Game.Rulesets.Taiko.UI; + +namespace osu.Game.Rulesets.Taiko.Tests.Skinning +{ + [TestFixture] + public class TestSceneHitExplosion : TaikoSkinnableTestScene + { + public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] + { + typeof(HitExplosion), + typeof(LegacyHitExplosion), + typeof(DefaultHitExplosion), + }).ToList(); + + [BackgroundDependencyLoader] + private void load() + { + AddStep("Great", () => SetContents(() => getContentFor(HitResult.Great))); + AddStep("Good", () => SetContents(() => getContentFor(HitResult.Good))); + AddStep("Miss", () => SetContents(() => getContentFor(HitResult.Miss))); + } + + private Drawable getContentFor(HitResult type) + { + DrawableTaikoHitObject hit; + + return new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + hit = createHit(type), + new HitExplosion(hit) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } + } + }; + } + + private DrawableTaikoHitObject createHit(HitResult type) => new TestDrawableHit(new Hit { StartTime = Time.Current }, type); + + private class TestDrawableHit : DrawableTaikoHitObject + { + private readonly HitResult type; + + public TestDrawableHit(Hit hit, HitResult type) + : base(hit) + { + this.type = type; + } + + [BackgroundDependencyLoader] + private void load() + { + Result.Type = type; + } + + public override bool OnPressed(TaikoAction action) => false; + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs b/osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs new file mode 100644 index 0000000000..d29b574866 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.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 osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; + +namespace osu.Game.Rulesets.Taiko.Skinning +{ + public class LegacyHitExplosion : CompositeDrawable + { + public LegacyHitExplosion(Drawable sprite) + { + InternalChild = sprite; + + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + AutoSizeAxes = Axes.Both; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + this.FadeIn(120); + this.ScaleTo(0.6f).Then().ScaleTo(1, 240, Easing.OutElastic); + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs index 447d6ae455..bea1c6bdcf 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs @@ -81,11 +81,38 @@ namespace osu.Game.Rulesets.Taiko.Skinning return new LegacyBarLine(); return null; + + case TaikoSkinComponents.TaikoExplosionGood: + case TaikoSkinComponents.TaikoExplosionGreat: + case TaikoSkinComponents.TaikoExplosionMiss: + + var sprite = this.GetAnimation(getHitname(taikoComponent.Component), true, false); + if (sprite != null) + return new LegacyHitExplosion(sprite); + + return null; } return source.GetDrawableComponent(component); } + private string getHitname(TaikoSkinComponents component) + { + switch (component) + { + case TaikoSkinComponents.TaikoExplosionMiss: + return "taiko-hit0"; + + case TaikoSkinComponents.TaikoExplosionGood: + return "taiko-hit100"; + + case TaikoSkinComponents.TaikoExplosionGreat: + return "taiko-hit300"; + } + + return string.Empty; + } + public Texture GetTexture(string componentName) => source.GetTexture(componentName); public SampleChannel GetSample(ISampleInfo sampleInfo) => source.GetSample(new LegacyTaikoSampleInfo(sampleInfo)); diff --git a/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs b/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs index a90ce608b2..fd091f97d0 100644 --- a/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs +++ b/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs @@ -14,6 +14,9 @@ namespace osu.Game.Rulesets.Taiko HitTarget, PlayfieldBackgroundLeft, PlayfieldBackgroundRight, - BarLine + BarLine, + TaikoExplosionMiss, + TaikoExplosionGood, + TaikoExplosionGreat, } } diff --git a/osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs new file mode 100644 index 0000000000..aa444d0494 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs @@ -0,0 +1,54 @@ +// 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.Game.Graphics; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.Taiko.Objects; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Taiko.UI +{ + internal class DefaultHitExplosion : CircularContainer + { + [Resolved] + private DrawableHitObject judgedObject { get; set; } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + RelativeSizeAxes = Axes.Both; + + BorderColour = Color4.White; + BorderThickness = 1; + + Alpha = 0.15f; + Masking = true; + + if (judgedObject.Result.Type == HitResult.Miss) + return; + + bool isRim = (judgedObject.HitObject as Hit)?.Type == HitType.Rim; + + InternalChildren = new[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = isRim ? colours.BlueDarker : colours.PinkDarker, + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + this.ScaleTo(3f, 1000, Easing.OutQuint); + } + } +} diff --git a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs index fbaae7e322..9bef93d834 100644 --- a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs @@ -1,15 +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 osuTK; -using osuTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Skinning; namespace osu.Game.Rulesets.Taiko.UI { @@ -20,15 +20,12 @@ namespace osu.Game.Rulesets.Taiko.UI { public override bool RemoveWhenNotAlive => true; + [Cached(typeof(DrawableHitObject))] public readonly DrawableHitObject JudgedObject; - private readonly HitType type; - private readonly Box innerFill; - - public HitExplosion(DrawableHitObject judgedObject, HitType type) + public HitExplosion(DrawableHitObject judgedObject) { JudgedObject = judgedObject; - this.type = type; Anchor = Anchor.Centre; Origin = Anchor.Centre; @@ -37,35 +34,36 @@ namespace osu.Game.Rulesets.Taiko.UI Size = new Vector2(TaikoHitObject.DEFAULT_SIZE); RelativePositionAxes = Axes.Both; - - BorderColour = Color4.White; - BorderThickness = 1; - - Alpha = 0.15f; - Masking = true; - - Children = new[] - { - innerFill = new Box - { - RelativeSizeAxes = Axes.Both, - } - }; } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load() { - innerFill.Colour = type == HitType.Rim ? colours.BlueDarker : colours.PinkDarker; + Child = new SkinnableDrawable(new TaikoSkinComponent(getComponentName(JudgedObject.Result?.Type ?? HitResult.Great)), _ => new DefaultHitExplosion()); + } + + private TaikoSkinComponents getComponentName(HitResult resultType) + { + switch (resultType) + { + case HitResult.Miss: + return TaikoSkinComponents.TaikoExplosionMiss; + + case HitResult.Good: + return TaikoSkinComponents.TaikoExplosionGood; + + case HitResult.Great: + return TaikoSkinComponents.TaikoExplosionGreat; + } + + throw new ArgumentException("Invalid result type", nameof(resultType)); } protected override void LoadComplete() { base.LoadComplete(); - this.ScaleTo(3f, 1000, Easing.OutQuint); this.FadeOut(500); - Expire(true); } diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 4bc6cb8e4b..6a78c0a1fb 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -185,7 +185,6 @@ namespace osu.Game.Rulesets.Taiko.UI var drawableTick = (DrawableDrumRollTick)judgedObject; addDrumRollHit(drawableTick); - addExplosion(drawableTick, drawableTick.JudgementType); break; default: @@ -200,7 +199,9 @@ namespace osu.Game.Rulesets.Taiko.UI if (!result.IsHit) break; - addExplosion(judgedObject, (judgedObject.HitObject as Hit)?.Type ?? HitType.Centre); + var type = (judgedObject.HitObject as Hit)?.Type ?? HitType.Centre; + + addExplosion(judgedObject, type); break; } } @@ -210,7 +211,7 @@ namespace osu.Game.Rulesets.Taiko.UI private void addExplosion(DrawableHitObject drawableObject, HitType type) { - hitExplosionContainer.Add(new HitExplosion(drawableObject, type)); + hitExplosionContainer.Add(new HitExplosion(drawableObject)); if (drawableObject.HitObject.Kiai) kiaiExplosionContainer.Add(new KiaiHitExplosion(drawableObject, type)); } From df55439f8b15d167491fba706987278f50bd4ac0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 27 Apr 2020 23:24:12 +0900 Subject: [PATCH 0486/1117] Remove undetected usings --- osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneHitExplosion.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneHitExplosion.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneHitExplosion.cs index ca0c60b67a..3a9779c93b 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneHitExplosion.cs @@ -8,8 +8,6 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Objects.Drawables; From a34ec03efc685c706004fb578da9fddb1f5855d2 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Mon, 27 Apr 2020 12:44:20 -0700 Subject: [PATCH 0487/1117] Reword width comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Bartłomiej Dach --- 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 09f4befbc1..b32875f723 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -272,7 +272,7 @@ namespace osu.Game.Overlays.Mods Font = OsuFont.GetFont(size: 30, weight: FontWeight.Bold), Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, - Width = 70, // to avoid footer from flowing when clicking mods + Width = 70, // to prevent footer from flowing when clicking mods }, }, }, From c0493026500db4fe91dd727176c84c2fcfec383f Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Mon, 27 Apr 2020 22:23:04 +0200 Subject: [PATCH 0488/1117] Update osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs 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/UI/TaikoMascotTextureAnimation.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs b/osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs index c8e97b9f8b..be1864049a 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs @@ -100,7 +100,7 @@ namespace osu.Game.Rulesets.Taiko.UI return "kiai"; default: - throw new ArgumentException($"There's no case for animation state ${state} available", nameof(state)); + throw new ArgumentOutOfRangeException(nameof(state), $"There's no case for animation state {state} available"); } } } From 5caa4dedc2cd28b3bbd098a47a80c62349000b43 Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Mon, 27 Apr 2020 22:27:03 +0200 Subject: [PATCH 0489/1117] Update osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs 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/UI/DrawableTaikoMascot.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs index 2c94f5f1cb..a90bf11af5 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs @@ -77,7 +77,7 @@ namespace osu.Game.Rulesets.Taiko.UI return failDrawable; default: - throw new ArgumentException($"There's no case for animation state ${state} available", nameof(state)); + throw new ArgumentOutOfRangeException(nameof(state), $"There's no animation available for state {state}"); } } From 0972442b3a6bbc64845d7a79b926454310c014ef Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Mon, 27 Apr 2020 23:17:19 +0200 Subject: [PATCH 0490/1117] Use test scene beatmap bindable --- .../.idea/projectSettingsUpdater.xml | 2 +- .../Skinning/TestSceneDrawableTaikoMascot.cs | 19 ++++++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/.idea/.idea.osu.Desktop/.idea/projectSettingsUpdater.xml b/.idea/.idea.osu.Desktop/.idea/projectSettingsUpdater.xml index 4bb9f4d2a0..7515e76054 100644 --- a/.idea/.idea.osu.Desktop/.idea/projectSettingsUpdater.xml +++ b/.idea/.idea.osu.Desktop/.idea/projectSettingsUpdater.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs index 93b5803e87..3a78ad76a6 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs @@ -37,13 +37,18 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning TimeRange = { Value = 5000 }, }; - [Cached(typeof(IBindable))] - private Bindable beatmap = new Bindable(); + private Bindable workingBeatmap; private readonly List mascots = new List(); private readonly List playfields = new List(); private readonly List rulesets = new List(); + [BackgroundDependencyLoader] + private void load(Bindable beatmap) + { + workingBeatmap = beatmap; + } + [Test] public void TestStateTextures() { @@ -79,7 +84,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning SetContents(() => { var ruleset = new TaikoRuleset(); - var drawableRuleset = new DrawableTaikoRuleset(ruleset, beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo)); + var drawableRuleset = new DrawableTaikoRuleset(ruleset, workingBeatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo)); rulesets.Add(drawableRuleset); return drawableRuleset; }); @@ -110,17 +115,17 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning { AddStep("Set beatmap", () => setBeatmap(true)); - AddUntilStep("Wait for beatmap to be loaded", () => beatmap.Value.Track.IsLoaded); + AddUntilStep("Wait for beatmap to be loaded", () => workingBeatmap.Value.Track.IsLoaded); AddStep("Create kiai ruleset", () => { - beatmap.Value.Track.Start(); + workingBeatmap.Value.Track.Start(); rulesets.Clear(); SetContents(() => { var ruleset = new TaikoRuleset(); - var drawableRuleset = new DrawableTaikoRuleset(ruleset, beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo)); + var drawableRuleset = new DrawableTaikoRuleset(ruleset, workingBeatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo)); rulesets.Add(drawableRuleset); return drawableRuleset; }); @@ -148,7 +153,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning if (kiai) controlPointInfo.Add(0, new EffectControlPoint { KiaiMode = true }); - beatmap.Value = CreateWorkingBeatmap(new Beatmap + workingBeatmap.Value = CreateWorkingBeatmap(new Beatmap { HitObjects = new List { new Hit { Type = HitType.Centre } }, BeatmapInfo = new BeatmapInfo From c8ee941952c9e9de3c55dae188e40037f1a2366f Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Mon, 27 Apr 2020 23:17:33 +0200 Subject: [PATCH 0491/1117] Fix formatting --- .../Skinning/TestSceneDrawableTaikoMascot.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs index 3a78ad76a6..a0ab3e5c25 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs @@ -180,7 +180,8 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning private void collectPlayfields() { playfields.Clear(); - foreach (var ruleset in rulesets) playfields.Add(ruleset.ChildrenOfType().Single()); + foreach (var ruleset in rulesets) + playfields.Add(ruleset.ChildrenOfType().Single()); } private void collectMascots() From 9b3c1e41267e5e6f59effceddcb361f17df54490 Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Mon, 27 Apr 2020 23:17:52 +0200 Subject: [PATCH 0492/1117] Remove unused bindables --- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 020df5e9e4..d94503fa67 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -3,7 +3,6 @@ using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps.ControlPoints; @@ -44,8 +43,6 @@ namespace osu.Game.Rulesets.Taiko.UI private SkinnableDrawable mascotDrawable; - private Bindable frameTime = new Bindable(100); - public TaikoPlayfield(ControlPointInfo controlPoints) { this.controlPoints = controlPoints; From 834eeb6d9862fdd1f0badc6125b1ae5a0007d70e Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Mon, 27 Apr 2020 23:18:26 +0200 Subject: [PATCH 0493/1117] Reduce duplicate texture retrieval code --- .../UI/TaikoMascotTextureAnimation.cs | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs b/osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs index c8e97b9f8b..6fc8df66ae 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs @@ -5,7 +5,6 @@ using System; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Animations; -using osu.Framework.Graphics.Textures; using osu.Game.Skinning; namespace osu.Game.Rulesets.Taiko.UI @@ -40,30 +39,33 @@ namespace osu.Game.Rulesets.Taiko.UI { foreach (var textureIndex in clear_animation_sequence) { - var textureName = getStateTextureName(textureIndex); - Texture texture = skin.GetTexture(textureName); - - if (texture == null) + if (!addFrame(skin, textureIndex)) break; - - AddFrame(texture); } } else { for (int i = 0; true; i++) { - var textureName = getStateTextureName(i); - Texture texture = skin.GetTexture(textureName); - - if (texture == null) + if (!addFrame(skin, i)) break; - - AddFrame(texture); } } } + private bool addFrame(ISkinSource skin, int textureIndex) + { + var textureName = getStateTextureName(textureIndex); + var texture = skin.GetTexture(textureName); + + if (texture == null) + return false; + + AddFrame(texture); + + return true; + } + /// /// Advances the current frame by one. /// From 96660b2cca73a642f07cb35e0dd8e5bfd22cb89a Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Mon, 27 Apr 2020 23:18:40 +0200 Subject: [PATCH 0494/1117] Flip frame count check --- osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs b/osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs index 6fc8df66ae..6ceb5cd08e 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs @@ -75,7 +75,7 @@ namespace osu.Game.Rulesets.Taiko.UI if (FrameCount == 0) return; - if (FrameCount <= currentFrame) + if (currentFrame >= FrameCount) currentFrame = 0; GotoFrame(currentFrame); From 74d36cad784f2c1c3f3ca94f6eca84745d4b50f0 Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Mon, 27 Apr 2020 23:19:18 +0200 Subject: [PATCH 0495/1117] Change state variables --- .../UI/DrawableTaikoMascot.cs | 27 +++++++++++-------- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 2 +- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs index 2c94f5f1cb..e66a045881 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs @@ -4,7 +4,6 @@ using System; using osu.Framework.Allocation; using osu.Framework.Audio.Track; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; using osu.Game.Beatmaps.ControlPoints; @@ -16,8 +15,7 @@ namespace osu.Game.Rulesets.Taiko.UI { private TaikoMascotTextureAnimation idleDrawable, clearDrawable, kiaiDrawable, failDrawable; private EffectControlPoint lastEffectControlPoint; - - public Bindable PlayfieldState; + private TaikoMascotAnimationState playfieldState; public TaikoMascotAnimationState State { get; private set; } @@ -25,13 +23,6 @@ namespace osu.Game.Rulesets.Taiko.UI { RelativeSizeAxes = Axes.Both; - PlayfieldState = new Bindable(); - PlayfieldState.BindValueChanged(b => - { - if (lastEffectControlPoint != null) - ShowState(GetFinalAnimationState(lastEffectControlPoint, b.NewValue)); - }); - State = startingState; } @@ -60,6 +51,20 @@ namespace osu.Game.Rulesets.Taiko.UI drawable.Show(); } + /// + /// Sets the playfield state used for determining the final state. + /// + /// + /// If you're looking to change the state manually, please look at . + /// + public void SetPlayfieldState(TaikoMascotAnimationState state) + { + playfieldState = state; + + if (lastEffectControlPoint != null) + ShowState(GetFinalAnimationState(lastEffectControlPoint, playfieldState)); + } + private TaikoMascotTextureAnimation getStateDrawable(TaikoMascotAnimationState state) { switch (state) @@ -93,7 +98,7 @@ namespace osu.Game.Rulesets.Taiko.UI { base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); - var state = GetFinalAnimationState(lastEffectControlPoint = effectPoint, PlayfieldState.Value); + var state = GetFinalAnimationState(lastEffectControlPoint = effectPoint, playfieldState); ShowState(state); if (state == TaikoMascotAnimationState.Clear) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index d94503fa67..41879f173e 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -220,7 +220,7 @@ namespace osu.Game.Rulesets.Taiko.UI if (miss && judgedObject.HitObject is StrongHitObject) miss = result.Judgement.AffectsCombo; - mascot.PlayfieldState.Value = miss ? TaikoMascotAnimationState.Fail : TaikoMascotAnimationState.Idle; + mascot.SetPlayfieldState(miss ? TaikoMascotAnimationState.Fail : TaikoMascotAnimationState.Idle); } } From f387fe310f04cf87f182961e6209477bb6a9394a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 28 Apr 2020 11:07:31 +0900 Subject: [PATCH 0496/1117] Fix regressing hits test --- .../DrawableTestHit.cs | 29 +++++++++++++++++++ .../Skinning/TestSceneHitExplosion.cs | 21 +------------- .../TestSceneHits.cs | 12 +++----- 3 files changed, 34 insertions(+), 28 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko.Tests/DrawableTestHit.cs diff --git a/osu.Game.Rulesets.Taiko.Tests/DrawableTestHit.cs b/osu.Game.Rulesets.Taiko.Tests/DrawableTestHit.cs new file mode 100644 index 0000000000..1db07b3244 --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests/DrawableTestHit.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 osu.Framework.Allocation; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Taiko.Objects.Drawables; + +namespace osu.Game.Rulesets.Taiko.Tests +{ + internal class DrawableTestHit : DrawableTaikoHitObject + { + private readonly HitResult type; + + public DrawableTestHit(Hit hit, HitResult type = HitResult.Great) + : base(hit) + { + this.type = type; + } + + [BackgroundDependencyLoader] + private void load() + { + Result.Type = type; + } + + public override bool OnPressed(TaikoAction action) => false; + } +} diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneHitExplosion.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneHitExplosion.cs index 3a9779c93b..791c438c94 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneHitExplosion.cs @@ -53,25 +53,6 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning }; } - private DrawableTaikoHitObject createHit(HitResult type) => new TestDrawableHit(new Hit { StartTime = Time.Current }, type); - - private class TestDrawableHit : DrawableTaikoHitObject - { - private readonly HitResult type; - - public TestDrawableHit(Hit hit, HitResult type) - : base(hit) - { - this.type = type; - } - - [BackgroundDependencyLoader] - private void load() - { - Result.Type = type; - } - - public override bool OnPressed(TaikoAction action) => false; - } + private DrawableTaikoHitObject createHit(HitResult type) => new DrawableTestHit(new Hit { StartTime = Time.Current }, type); } } diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs index 23adb79083..44452d70c1 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs @@ -149,6 +149,8 @@ namespace osu.Game.Rulesets.Taiko.Tests var h = new DrawableTestHit(hit) { X = RNG.NextSingle(hitResult == HitResult.Good ? -0.1f : -0.05f, hitResult == HitResult.Good ? 0.1f : 0.05f) }; + Add(h); + ((TaikoPlayfield)drawableRuleset.Playfield).OnNewResult(h, new JudgementResult(new HitObject(), new TaikoJudgement()) { Type = hitResult }); } @@ -164,6 +166,8 @@ namespace osu.Game.Rulesets.Taiko.Tests var h = new DrawableTestHit(hit) { X = RNG.NextSingle(hitResult == HitResult.Good ? -0.1f : -0.05f, hitResult == HitResult.Good ? 0.1f : 0.05f) }; + Add(h); + ((TaikoPlayfield)drawableRuleset.Playfield).OnNewResult(h, new JudgementResult(new HitObject(), new TaikoJudgement()) { Type = hitResult }); ((TaikoPlayfield)drawableRuleset.Playfield).OnNewResult(new TestStrongNestedHit(h), new JudgementResult(new HitObject(), new TaikoStrongJudgement()) { Type = HitResult.Great }); } @@ -249,13 +253,5 @@ namespace osu.Game.Rulesets.Taiko.Tests public override bool OnPressed(TaikoAction action) => false; } - - private class DrawableTestHit : DrawableHitObject - { - public DrawableTestHit(TaikoHitObject hitObject) - : base(hitObject) - { - } - } } } From 84641765c5858976fc2fa43606e77f60d4f7e7f1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 28 Apr 2020 11:08:19 +0900 Subject: [PATCH 0497/1117] Adjust exceptions and fix capitalisation --- .../Skinning/TaikoLegacySkinTransformer.cs | 7 ++++--- osu.Game.Rulesets.Taiko/UI/HitExplosion.cs | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs index bea1c6bdcf..f0df612e18 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.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 osu.Framework.Audio.Sample; using osu.Framework.Bindables; @@ -86,7 +87,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning case TaikoSkinComponents.TaikoExplosionGreat: case TaikoSkinComponents.TaikoExplosionMiss: - var sprite = this.GetAnimation(getHitname(taikoComponent.Component), true, false); + var sprite = this.GetAnimation(getHitName(taikoComponent.Component), true, false); if (sprite != null) return new LegacyHitExplosion(sprite); @@ -96,7 +97,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning return source.GetDrawableComponent(component); } - private string getHitname(TaikoSkinComponents component) + private string getHitName(TaikoSkinComponents component) { switch (component) { @@ -110,7 +111,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning return "taiko-hit300"; } - return string.Empty; + throw new ArgumentOutOfRangeException(nameof(component), "Invalid result type"); } public Texture GetTexture(string componentName) => source.GetTexture(componentName); diff --git a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs index 9bef93d834..35a54d6ea7 100644 --- a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs @@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Taiko.UI return TaikoSkinComponents.TaikoExplosionGreat; } - throw new ArgumentException("Invalid result type", nameof(resultType)); + throw new ArgumentOutOfRangeException(nameof(resultType), "Invalid result type"); } protected override void LoadComplete() From 62be138aa912ca3cd77e53d3df39cff877b3f7f9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 28 Apr 2020 11:46:08 +0900 Subject: [PATCH 0498/1117] Avoid calls on MusicController executing before it may have finished loading --- .../TestSceneNowPlayingOverlay.cs | 17 +++++++++-------- osu.Game/Overlays/MusicController.cs | 18 +++++++++++------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs index 2ea9aec50a..e2913833a7 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs @@ -47,18 +47,19 @@ namespace osu.Game.Tests.Visual.UserInterface [Test] public void TestPrevTrackBehavior() { - AddStep(@"Play track", () => - { - musicController.NextTrack(); - currentBeatmap = Beatmap.Value; - }); + AddStep(@"Next track", () => musicController.NextTrack()); + AddStep("Store track", () => currentBeatmap = Beatmap.Value); AddStep(@"Seek track to 6 second", () => musicController.SeekTo(6000)); AddUntilStep(@"Wait for current time to update", () => currentBeatmap.Track.CurrentTime > 5000); - AddAssert(@"Check action is restart track", () => musicController.PreviousTrack() == PreviousTrackResult.Restart); - AddUntilStep("Wait for current time to update", () => Precision.AlmostEquals(currentBeatmap.Track.CurrentTime, 0)); + + AddStep(@"Set previous", () => musicController.PreviousTrack()); + AddAssert(@"Check track didn't change", () => currentBeatmap == Beatmap.Value); - AddAssert(@"Check action is not restart", () => musicController.PreviousTrack() != PreviousTrackResult.Restart); + AddUntilStep("Wait for current time to update", () => currentBeatmap.Track.CurrentTime < 5000); + + AddStep(@"Set previous", () => musicController.PreviousTrack()); + AddAssert(@"Check track did change", () => currentBeatmap != Beatmap.Value); } } } diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index d788929739..6d269aa944 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -172,10 +172,15 @@ namespace osu.Game.Overlays } /// - /// Play the previous track or restart the current track if it's current time below + /// Play the previous track or restart the current track if it's current time below . /// - /// The that indicate the decided action - public PreviousTrackResult PreviousTrack() + public void PreviousTrack() => Schedule(() => prev()); + + /// + /// Play the previous track or restart the current track if it's current time below . + /// + /// The that indicate the decided action. + private PreviousTrackResult prev() { var currentTrackPosition = current?.Track.CurrentTime; @@ -204,8 +209,7 @@ namespace osu.Game.Overlays /// /// Play the next random or playlist track. /// - /// Whether the operation was successful. - public bool NextTrack() => next(); + public void NextTrack() => Schedule(() => next()); private bool next(bool instant = false) { @@ -319,13 +323,13 @@ namespace osu.Game.Overlays return true; case GlobalAction.MusicNext: - if (NextTrack()) + if (next()) onScreenDisplay?.Display(new MusicControllerToast("Next track")); return true; case GlobalAction.MusicPrev: - switch (PreviousTrack()) + switch (prev()) { case PreviousTrackResult.Restart: onScreenDisplay?.Display(new MusicControllerToast("Restart track")); From 4fff08d241a2158da662863426e07fa1f1621f8a Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 28 Apr 2020 12:19:39 +0900 Subject: [PATCH 0499/1117] Remove unused using --- .../Visual/UserInterface/TestSceneNowPlayingOverlay.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs index e2913833a7..9944e6f9d0 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs @@ -4,7 +4,6 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Overlays; using osu.Game.Rulesets.Osu; From 81e73acb1a05863fbda05603e3505a8fe5c79931 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 28 Apr 2020 12:14:22 +0900 Subject: [PATCH 0500/1117] Fix tests failing when not run in certain order --- .../TestSceneNowPlayingOverlay.cs | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs index 9944e6f9d0..a9c52bd189 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.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.Collections.Generic; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -43,9 +44,29 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep(@"hide", () => nowPlayingOverlay.Hide()); } + [Resolved] + private BeatmapManager manager { get; set; } + [Test] public void TestPrevTrackBehavior() { + // ensure we have at least two beatmaps available. + AddRepeatStep("import beatmap", () => manager.Import(new BeatmapSetInfo + { + Beatmaps = new List + { + new BeatmapInfo + { + BaseDifficulty = new BeatmapDifficulty(), + } + }, + Metadata = new BeatmapMetadata + { + Artist = "a test map", + Title = "title", + } + }).Wait(), 5); + AddStep(@"Next track", () => musicController.NextTrack()); AddStep("Store track", () => currentBeatmap = Beatmap.Value); @@ -54,11 +75,11 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep(@"Set previous", () => musicController.PreviousTrack()); - AddAssert(@"Check track didn't change", () => currentBeatmap == Beatmap.Value); + AddAssert(@"Check beatmap didn't change", () => currentBeatmap == Beatmap.Value); AddUntilStep("Wait for current time to update", () => currentBeatmap.Track.CurrentTime < 5000); AddStep(@"Set previous", () => musicController.PreviousTrack()); - AddAssert(@"Check track did change", () => currentBeatmap != Beatmap.Value); + AddAssert(@"Check beatmap did change", () => currentBeatmap != Beatmap.Value); } } } From 743a92bbbedcd696d7df90bb07dd1bdb049e0b79 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 28 Apr 2020 12:40:56 +0900 Subject: [PATCH 0501/1117] Use a local database for now playing test --- .../UserInterface/TestSceneNowPlayingOverlay.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs index a9c52bd189..ee922a073a 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs @@ -4,9 +4,12 @@ using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Audio; using osu.Framework.Graphics; +using osu.Framework.Platform; using osu.Game.Beatmaps; using osu.Game.Overlays; +using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; namespace osu.Game.Tests.Visual.UserInterface @@ -21,9 +24,14 @@ namespace osu.Game.Tests.Visual.UserInterface private NowPlayingOverlay nowPlayingOverlay; + private RulesetStore rulesets; + [BackgroundDependencyLoader] - private void load() + private void load(AudioManager audio, GameHost host) { + Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); + Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default)); + Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); nowPlayingOverlay = new NowPlayingOverlay @@ -44,9 +52,10 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep(@"hide", () => nowPlayingOverlay.Hide()); } - [Resolved] private BeatmapManager manager { get; set; } + private int importId = 0; + [Test] public void TestPrevTrackBehavior() { @@ -62,7 +71,7 @@ namespace osu.Game.Tests.Visual.UserInterface }, Metadata = new BeatmapMetadata { - Artist = "a test map", + Artist = $"a test map {importId++}", Title = "title", } }).Wait(), 5); From 0d752dc7b847cc162a3076f72c20e487833e724e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 28 Apr 2020 12:55:57 +0900 Subject: [PATCH 0502/1117] Remove redundant initialisation --- .../Visual/UserInterface/TestSceneNowPlayingOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs index ee922a073a..532744a0fc 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs @@ -54,7 +54,7 @@ namespace osu.Game.Tests.Visual.UserInterface private BeatmapManager manager { get; set; } - private int importId = 0; + private int importId; [Test] public void TestPrevTrackBehavior() From 832fa74a5e06146fc7572b841f7d957c9d44afdc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 28 Apr 2020 13:26:42 +0900 Subject: [PATCH 0503/1117] Reword comment slightly --- 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 b32875f723..3d0ad1a594 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -272,7 +272,7 @@ namespace osu.Game.Overlays.Mods Font = OsuFont.GetFont(size: 30, weight: FontWeight.Bold), Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, - Width = 70, // to prevent footer from flowing when clicking mods + Width = 70, // make width fixed so reflow doesn't occur when multiplier number changes. }, }, }, From 7342e0015161a7b7e339370ac4aa6aadcee6f3ce Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Apr 2020 14:00:14 +0900 Subject: [PATCH 0504/1117] Convert positions to local HOC coordinate space --- osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index 8d012c63ac..b415c9f0c9 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -46,9 +46,7 @@ namespace osu.Game.Rulesets.Mania.Edit { var hoc = Playfield.GetColumn(0).HitObjectContainer; - position.Y -= ToLocalSpace(hoc.ScreenSpaceDrawQuad.TopLeft).Y; - - float targetPosition = position.Y; + float targetPosition = hoc.ToLocalSpace(ToScreenSpace(position)).Y; if (drawableRuleset.ScrollingInfo.Direction.Value == ScrollingDirection.Down) { From da30eafa3020d3c1479e32ca4e37f43e114098ad Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Apr 2020 14:47:45 +0900 Subject: [PATCH 0505/1117] Prevent potential exception --- osu.Game/Rulesets/UI/DrawableRuleset.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index 5062c92afe..7170d425e2 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.UI ///
    public PassThroughInputManager KeyBindingInputManager; - public override double GameplayStartTime => Objects.First().StartTime - 2000; + public override double GameplayStartTime => Objects.FirstOrDefault()?.StartTime - 2000 ?? 0; private readonly Lazy playfield; From d905ef53b37ac287c2072fb8bcb26c8704211c27 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Apr 2020 14:47:52 +0900 Subject: [PATCH 0506/1117] Add test scene for mania composer --- .../TestSceneManiaHitObjectComposer.cs | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs new file mode 100644 index 0000000000..3cd2530f36 --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs @@ -0,0 +1,69 @@ +// 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.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Mania.Beatmaps; +using osu.Game.Rulesets.Mania.Edit; +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Screens.Edit; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Mania.Tests +{ + public class TestSceneManiaHitObjectComposer : EditorClockTestScene + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(ManiaBlueprintContainer) + }; + + [Cached(typeof(EditorBeatmap))] + [Cached(typeof(IBeatSnapProvider))] + private readonly EditorBeatmap editorBeatmap; + + protected override Container Content { get; } + + private ManiaHitObjectComposer composer; + + public TestSceneManiaHitObjectComposer() + { + base.Content.Add(new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + editorBeatmap = new EditorBeatmap(new ManiaBeatmap(new StageDefinition { Columns = 4 })) + { + BeatmapInfo = { Ruleset = new ManiaRuleset().RulesetInfo } + }, + Content = new Container + { + RelativeSizeAxes = Axes.Both, + } + }, + }); + + for (int i = 0; i < 10; i++) + { + editorBeatmap.Add(new Note { StartTime = 100 * i }); + } + } + + [SetUp] + public void Setup() => Schedule(() => + { + Children = new Drawable[] + { + composer = new ManiaHitObjectComposer(new ManiaRuleset()) + }; + + BeatDivisor.Value = 8; + }); + } +} From 330521a2ae7f268b1645c03fc7feab010c48ce74 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Apr 2020 15:34:10 +0900 Subject: [PATCH 0507/1117] Fix lifetime override not working --- .../Drawables/DrawableManiaHitObject.cs | 62 +++++++++++++++++-- 1 file changed, 56 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs index 88888001b4..a44d8b09aa 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs @@ -13,11 +13,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables { public abstract class DrawableManiaHitObject : DrawableHitObject { - /// - /// Whether this should always remain alive. - /// - internal bool AlwaysAlive; - /// /// The which causes this to be hit. /// @@ -54,7 +49,62 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables Direction.BindValueChanged(OnDirectionChanged, true); } - protected override bool ShouldBeAlive => AlwaysAlive || base.ShouldBeAlive; + private double computedLifetimeStart; + + public override double LifetimeStart + { + get => base.LifetimeStart; + set + { + computedLifetimeStart = value; + + if (!AlwaysAlive) + base.LifetimeStart = value; + } + } + + private double computedLifetimeEnd; + + public override double LifetimeEnd + { + get => base.LifetimeEnd; + set + { + computedLifetimeEnd = value; + + if (!AlwaysAlive) + base.LifetimeEnd = value; + } + } + + private bool alwaysAlive; + + /// + /// Whether this should always remain alive. + /// + internal bool AlwaysAlive + { + get => alwaysAlive; + set + { + if (alwaysAlive == value) + return; + + alwaysAlive = value; + + if (value) + { + // Set the base lifetimes directly, to avoid mangling the computed lifetimes + base.LifetimeStart = double.MinValue; + base.LifetimeEnd = double.MaxValue; + } + else + { + LifetimeStart = computedLifetimeStart; + LifetimeEnd = computedLifetimeEnd; + } + } + } protected virtual void OnDirectionChanged(ValueChangedEvent e) { From 3eb7c8755c1ed9e21a36ab0cee16047aab20f6e1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Apr 2020 15:34:41 +0900 Subject: [PATCH 0508/1117] Cleanup --- .../TestSceneManiaHitObjectComposer.cs | 4 +-- .../Blueprints/ManiaSelectionBlueprint.cs | 29 ------------------- .../Edit/ManiaSelectionHandler.cs | 11 ------- 3 files changed, 1 insertion(+), 43 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs index 3cd2530f36..180ceb94f4 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs @@ -29,8 +29,6 @@ namespace osu.Game.Rulesets.Mania.Tests protected override Container Content { get; } - private ManiaHitObjectComposer composer; - public TestSceneManiaHitObjectComposer() { base.Content.Add(new Container @@ -60,7 +58,7 @@ namespace osu.Game.Rulesets.Mania.Tests { Children = new Drawable[] { - composer = new ManiaHitObjectComposer(new ManiaRuleset()) + new ManiaHitObjectComposer(new ManiaRuleset()) }; BeatDivisor.Value = 8; diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs index 14d52dd08e..b03bf7c078 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs @@ -3,8 +3,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Input.Events; -using osu.Framework.Timing; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables; @@ -15,13 +13,8 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints { public class ManiaSelectionBlueprint : OverlaySelectionBlueprint { - public Vector2 ScreenSpaceDragPosition { get; private set; } - public Vector2 DragPosition { get; private set; } - public new DrawableManiaHitObject DrawableObject => (DrawableManiaHitObject)base.DrawableObject; - protected IClock EditorClock { get; private set; } - [Resolved] private IScrollingInfo scrollingInfo { get; set; } @@ -34,12 +27,6 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints RelativeSizeAxes = Axes.None; } - [BackgroundDependencyLoader] - private void load(IAdjustableClock clock) - { - EditorClock = clock; - } - protected override void Update() { base.Update(); @@ -47,22 +34,6 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints Position = Parent.ToLocalSpace(DrawableObject.ToScreenSpace(Vector2.Zero)); } - protected override bool OnMouseDown(MouseDownEvent e) - { - ScreenSpaceDragPosition = e.ScreenSpaceMousePosition; - DragPosition = DrawableObject.ToLocalSpace(e.ScreenSpaceMousePosition); - - return base.OnMouseDown(e); - } - - protected override void OnDrag(DragEvent e) - { - base.OnDrag(e); - - ScreenSpaceDragPosition = e.ScreenSpaceMousePosition; - DragPosition = DrawableObject.ToLocalSpace(e.ScreenSpaceMousePosition); - } - public override void Show() { DrawableObject.AlwaysAlive = true; diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs index 989e0f5b01..11e9b56a53 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs @@ -4,11 +4,9 @@ using System; using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Timing; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Mania.Edit.Blueprints; using osu.Game.Rulesets.Mania.Objects; -using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Screens.Edit.Compose.Components; @@ -22,14 +20,6 @@ namespace osu.Game.Rulesets.Mania.Edit [Resolved] private IManiaHitObjectComposer composer { get; set; } - private IClock editorClock; - - [BackgroundDependencyLoader] - private void load(IAdjustableClock clock) - { - editorClock = clock; - } - public override bool HandleMovement(MoveSelectionEvent moveEvent) { var maniaBlueprint = (ManiaSelectionBlueprint)moveEvent.Blueprint; @@ -50,7 +40,6 @@ namespace osu.Game.Rulesets.Mania.Edit var b = (OverlaySelectionBlueprint)selectionBlueprint; var hitObject = b.DrawableObject; - var objectParent = (HitObjectContainer)hitObject.Parent; // We receive multiple movement events per frame such that we can't rely on updating the start time // since the scrolling hitobject container requires at least one update frame to update the position. From 31c3fd86b9ffc1117cff3628a4441309255c504b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 28 Apr 2020 16:22:00 +0900 Subject: [PATCH 0509/1117] Avoid using internal EF methods in tests --- osu.Game.Tests/Beatmaps/TestSceneEditorBeatmap.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/TestSceneEditorBeatmap.cs b/osu.Game.Tests/Beatmaps/TestSceneEditorBeatmap.cs index 2d4587341d..b7b48ec06a 100644 --- a/osu.Game.Tests/Beatmaps/TestSceneEditorBeatmap.cs +++ b/osu.Game.Tests/Beatmaps/TestSceneEditorBeatmap.cs @@ -1,9 +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.Collections.Generic; using System.Linq; -using Microsoft.EntityFrameworkCore.Internal; using NUnit.Framework; using osu.Framework.Testing; using osu.Game.Rulesets.Objects; @@ -137,7 +137,7 @@ namespace osu.Game.Tests.Beatmaps var hitCircle = new HitCircle { StartTime = 1000 }; editorBeatmap.Add(hitCircle); Assert.That(editorBeatmap.HitObjects.Count(h => h == hitCircle), Is.EqualTo(1)); - Assert.That(editorBeatmap.HitObjects.IndexOf(hitCircle), Is.EqualTo(3)); + Assert.That(Array.IndexOf(editorBeatmap.HitObjects.ToArray(), hitCircle), Is.EqualTo(3)); } /// @@ -161,7 +161,7 @@ namespace osu.Game.Tests.Beatmaps hitCircle.StartTime = 0; Assert.That(editorBeatmap.HitObjects.Count(h => h == hitCircle), Is.EqualTo(1)); - Assert.That(editorBeatmap.HitObjects.IndexOf(hitCircle), Is.EqualTo(1)); + Assert.That(Array.IndexOf(editorBeatmap.HitObjects.ToArray(), hitCircle), Is.EqualTo(1)); } /// From f3fbb3cdc6595bda5e66382b01027bcdf4498ce1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 28 Apr 2020 16:49:43 +0900 Subject: [PATCH 0510/1117] Add to banned symbols --- CodeAnalysis/BannedSymbols.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CodeAnalysis/BannedSymbols.txt b/CodeAnalysis/BannedSymbols.txt index a92191a439..4d7135a195 100644 --- a/CodeAnalysis/BannedSymbols.txt +++ b/CodeAnalysis/BannedSymbols.txt @@ -4,3 +4,4 @@ M:System.ValueType.Equals(System.Object)~System.Boolean;Don't use object.Equals( M:System.Nullable`1.Equals(System.Object)~System.Boolean;Use == instead. T:System.IComparable;Don't use non-generic IComparable. Use generic version instead. M:osu.Framework.Graphics.Sprites.SpriteText.#ctor;Use OsuSpriteText. +T:Microsoft.EntityFrameworkCore.Internal.EnumerableExtensions;Don't use internal extension methods. \ No newline at end of file From c3a41c8476d97a5710e90b6da19e72787b026449 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 28 Apr 2020 16:56:36 +0900 Subject: [PATCH 0511/1117] Also avoid using internal TypeExtensions --- CodeAnalysis/BannedSymbols.txt | 3 ++- osu.Game/Screens/OsuScreen.cs | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CodeAnalysis/BannedSymbols.txt b/CodeAnalysis/BannedSymbols.txt index 4d7135a195..e34626a59e 100644 --- a/CodeAnalysis/BannedSymbols.txt +++ b/CodeAnalysis/BannedSymbols.txt @@ -4,4 +4,5 @@ M:System.ValueType.Equals(System.Object)~System.Boolean;Don't use object.Equals( M:System.Nullable`1.Equals(System.Object)~System.Boolean;Use == instead. T:System.IComparable;Don't use non-generic IComparable. Use generic version instead. M:osu.Framework.Graphics.Sprites.SpriteText.#ctor;Use OsuSpriteText. -T:Microsoft.EntityFrameworkCore.Internal.EnumerableExtensions;Don't use internal extension methods. \ No newline at end of file +T:Microsoft.EntityFrameworkCore.Internal.EnumerableExtensions;Don't use internal extension methods. +T:Microsoft.EntityFrameworkCore.Internal.TypeExtensions;Don't use internal extension methods. diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index 61e94ae969..2124a66a75 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using Microsoft.EntityFrameworkCore.Internal; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; @@ -31,7 +30,7 @@ namespace osu.Game.Screens /// /// A user-facing title for this screen. /// - public virtual string Title => GetType().ShortDisplayName(); + public virtual string Title => GetType().Name; public string Description => Title; From e5131400e77d7ba9c213259ad883fc715206b9f7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Apr 2020 18:34:39 +0900 Subject: [PATCH 0512/1117] Remove now unnecessary position manipulation --- .../Edit/ManiaSelectionHandler.cs | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs index 11e9b56a53..55245198c8 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs @@ -4,7 +4,6 @@ using System; using System.Linq; using osu.Framework.Allocation; -using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Mania.Edit.Blueprints; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.UI.Scrolling; @@ -25,29 +24,11 @@ namespace osu.Game.Rulesets.Mania.Edit var maniaBlueprint = (ManiaSelectionBlueprint)moveEvent.Blueprint; int lastColumn = maniaBlueprint.DrawableObject.HitObject.Column; - performDragMovement(moveEvent); performColumnMovement(lastColumn, moveEvent); return true; } - private void performDragMovement(MoveSelectionEvent moveEvent) - { - float delta = moveEvent.InstantDelta.Y; - - foreach (var selectionBlueprint in SelectedBlueprints) - { - var b = (OverlaySelectionBlueprint)selectionBlueprint; - - var hitObject = b.DrawableObject; - - // We receive multiple movement events per frame such that we can't rely on updating the start time - // since the scrolling hitobject container requires at least one update frame to update the position. - // However the position needs to be valid for future movement events to calculate the correct deltas. - hitObject.Y += delta; - } - } - private void performColumnMovement(int lastColumn, MoveSelectionEvent moveEvent) { var currentColumn = composer.ColumnAt(moveEvent.ScreenSpacePosition); From a7a680b4862e5adf6d0579b81534f07b169488c4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Apr 2020 18:34:55 +0900 Subject: [PATCH 0513/1117] Fix horizontal drag not working --- osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs index f9faa262ed..6960d15f31 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs @@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Mania.UI { foreach (var column in stage.Columns) { - if (column.ReceivePositionalInputAt(screenSpacePosition)) + if (column.ReceivePositionalInputAt(new Vector2(screenSpacePosition.X, column.ScreenSpaceDrawQuad.Centre.Y))) { found = column; break; From f93291e25b4967edd658d3e309d33905684d5343 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Apr 2020 18:35:22 +0900 Subject: [PATCH 0514/1117] Remove unused override --- .../Blueprints/ManiaSelectionBlueprint.cs | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs index b03bf7c078..b8574b804e 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs @@ -45,25 +45,5 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints DrawableObject.AlwaysAlive = false; base.Hide(); } - - public override Vector2 GetInstantDelta(Vector2 screenSpacePosition) - { - var baseDelta = base.GetInstantDelta(screenSpacePosition); - - if (scrollingInfo.Direction.Value == ScrollingDirection.Down) - { - // The parent of DrawableObject is the scrolling hitobject container (SHOC). - // In the coordinate-space of the SHOC, the screen-space position at the hit target is equal to the height of the SHOC, - // but this is not what we want as it means a slight movement downwards results in a delta greater than the height of the SHOC. - // To get around this issue, the height of the SHOC is subtracted from the delta. - // - // Ideally this should be a _negative_ value in the case described above, however this code gives a _positive_ delta. - // This is intentional as the delta is added to the hitobject's position (see: ManiaSelectionHandler) and a negative delta would move them towards the top of the screen instead, - // which would cause the delta to get increasingly larger as additional movements are performed. - return new Vector2(baseDelta.X, baseDelta.Y - DrawableObject.Parent.DrawHeight); - } - - return baseDelta; - } } } From 7d54d4b800234bb017602dc49e8b2c4a5134193e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Apr 2020 18:35:37 +0900 Subject: [PATCH 0515/1117] Improve test scene --- .../TestSceneManiaHitObjectComposer.cs | 171 ++++++++++++++---- .../Edit/ManiaHitObjectComposer.cs | 2 + osu.Game/Tests/Visual/EditorClockTestScene.cs | 2 +- 3 files changed, 140 insertions(+), 35 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs index 180ceb94f4..4ce2424ffa 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs @@ -3,16 +3,23 @@ using System; using System.Collections.Generic; +using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Edit; using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Screens.Edit; using osu.Game.Tests.Visual; +using osuTK; +using osuTK.Input; namespace osu.Game.Rulesets.Mania.Tests { @@ -23,45 +30,141 @@ namespace osu.Game.Rulesets.Mania.Tests typeof(ManiaBlueprintContainer) }; - [Cached(typeof(EditorBeatmap))] - [Cached(typeof(IBeatSnapProvider))] - private readonly EditorBeatmap editorBeatmap; - - protected override Container Content { get; } - - public TestSceneManiaHitObjectComposer() - { - base.Content.Add(new Container - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - editorBeatmap = new EditorBeatmap(new ManiaBeatmap(new StageDefinition { Columns = 4 })) - { - BeatmapInfo = { Ruleset = new ManiaRuleset().RulesetInfo } - }, - Content = new Container - { - RelativeSizeAxes = Axes.Both, - } - }, - }); - - for (int i = 0; i < 10; i++) - { - editorBeatmap.Add(new Note { StartTime = 100 * i }); - } - } + private TestComposer composer; [SetUp] public void Setup() => Schedule(() => { - Children = new Drawable[] - { - new ManiaHitObjectComposer(new ManiaRuleset()) - }; - BeatDivisor.Value = 8; + Clock.Seek(0); + + Child = composer = new TestComposer { RelativeSizeAxes = Axes.Both }; }); + + [Test] + public void TestDragOffscreenSelectionVerticallyUpScroll() + { + DrawableHitObject lastObject = null; + Vector2 originalPosition = Vector2.Zero; + + AddStep("seek to last object", () => + { + lastObject = this.ChildrenOfType().Single(d => d.HitObject == composer.EditorBeatmap.HitObjects.Last()); + Clock.Seek(composer.EditorBeatmap.HitObjects.Last().StartTime); + }); + + AddStep("select all objects", () => composer.EditorBeatmap.SelectedHitObjects.AddRange(composer.EditorBeatmap.HitObjects)); + + AddStep("click last object", () => + { + originalPosition = lastObject.DrawPosition; + + InputManager.MoveMouseTo(lastObject); + InputManager.PressButton(MouseButton.Left); + }); + + AddStep("move mouse downwards", () => + { + InputManager.MoveMouseTo(lastObject, new Vector2(0, 20)); + InputManager.ReleaseButton(MouseButton.Left); + }); + + AddAssert("hitobjects not moved columns", () => composer.EditorBeatmap.HitObjects.All(h => ((ManiaHitObject)h).Column == 0)); + AddAssert("hitobjects moved downwards", () => lastObject.DrawPosition.Y - originalPosition.Y > 0); + AddAssert("hitobjects not moved too far", () => lastObject.DrawPosition.Y - originalPosition.Y < 50); + } + + [Test] + public void TestDragOffscreenSelectionVerticallyDownScroll() + { + DrawableHitObject lastObject = null; + Vector2 originalPosition = Vector2.Zero; + + AddStep("set down scroll", () => ((Bindable)composer.Composer.ScrollingInfo.Direction).Value = ScrollingDirection.Down); + + AddStep("seek to last object", () => + { + lastObject = this.ChildrenOfType().Single(d => d.HitObject == composer.EditorBeatmap.HitObjects.Last()); + Clock.Seek(composer.EditorBeatmap.HitObjects.Last().StartTime); + }); + + AddStep("select all objects", () => composer.EditorBeatmap.SelectedHitObjects.AddRange(composer.EditorBeatmap.HitObjects)); + + AddStep("click last object", () => + { + originalPosition = lastObject.DrawPosition; + + InputManager.MoveMouseTo(lastObject); + InputManager.PressButton(MouseButton.Left); + }); + + AddStep("move mouse upwards", () => + { + InputManager.MoveMouseTo(lastObject, new Vector2(0, -20)); + InputManager.ReleaseButton(MouseButton.Left); + }); + + AddAssert("hitobjects not moved columns", () => composer.EditorBeatmap.HitObjects.All(h => ((ManiaHitObject)h).Column == 0)); + AddAssert("hitobjects moved upwards", () => originalPosition.Y - lastObject.DrawPosition.Y > 0); + AddAssert("hitobjects not moved too far", () => originalPosition.Y - lastObject.DrawPosition.Y < 50); + } + + [Test] + public void TestDragOffscreenSelectionHorizontally() + { + DrawableHitObject lastObject = null; + Vector2 originalPosition = Vector2.Zero; + + AddStep("seek to last object", () => + { + lastObject = this.ChildrenOfType().Single(d => d.HitObject == composer.EditorBeatmap.HitObjects.Last()); + originalPosition = lastObject.DrawPosition; + + Clock.Seek(composer.EditorBeatmap.HitObjects.Last().StartTime); + }); + + AddStep("select all objects", () => composer.EditorBeatmap.SelectedHitObjects.AddRange(composer.EditorBeatmap.HitObjects)); + + AddStep("click last object", () => + { + InputManager.MoveMouseTo(lastObject); + InputManager.PressButton(MouseButton.Left); + }); + + AddStep("move mouse right", () => + { + InputManager.MoveMouseTo(lastObject, new Vector2(40, 0)); + InputManager.ReleaseButton(MouseButton.Left); + }); + + AddAssert("hitobjects moved columns", () => composer.EditorBeatmap.HitObjects.All(h => ((ManiaHitObject)h).Column == 1)); + + // Todo: They'll have moved vertically by half the height of a note. Probably a problem. + AddAssert("hitobjects not moved vertically", () => lastObject.DrawPosition.Y - originalPosition.Y < 10); + } + + private class TestComposer : CompositeDrawable + { + [Cached(typeof(EditorBeatmap))] + [Cached(typeof(IBeatSnapProvider))] + public readonly EditorBeatmap EditorBeatmap; + + public readonly ManiaHitObjectComposer Composer; + + public TestComposer() + { + InternalChildren = new Drawable[] + { + EditorBeatmap = new EditorBeatmap(new ManiaBeatmap(new StageDefinition { Columns = 4 })) + { + BeatmapInfo = { Ruleset = new ManiaRuleset().RulesetInfo } + }, + Composer = new ManiaHitObjectComposer(new ManiaRuleset()) + }; + + for (int i = 0; i < 10; i++) + EditorBeatmap.Add(new Note { StartTime = 100 * i }); + } + } } } diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index b415c9f0c9..fba80f92d2 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -40,6 +40,8 @@ namespace osu.Game.Rulesets.Mania.Edit public ManiaPlayfield Playfield => ((ManiaPlayfield)drawableRuleset.Playfield); + public IScrollingInfo ScrollingInfo => drawableRuleset.ScrollingInfo; + public int TotalColumns => Playfield.TotalColumns; public override (Vector2 position, double time) GetSnappedPosition(Vector2 position, double time) diff --git a/osu.Game/Tests/Visual/EditorClockTestScene.cs b/osu.Game/Tests/Visual/EditorClockTestScene.cs index 58a443ed3d..830e6ed363 100644 --- a/osu.Game/Tests/Visual/EditorClockTestScene.cs +++ b/osu.Game/Tests/Visual/EditorClockTestScene.cs @@ -15,7 +15,7 @@ namespace osu.Game.Tests.Visual /// Provides a clock, beat-divisor, and scrolling capability for test cases of editor components that /// are preferrably tested within the presence of a clock and seek controls. /// - public abstract class EditorClockTestScene : OsuTestScene + public abstract class EditorClockTestScene : OsuManualInputManagerTestScene { protected readonly BindableBeatDivisor BeatDivisor = new BindableBeatDivisor(); protected new readonly EditorClock Clock; From ff24a15760167b1db112239b6c3418870654f5a1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Apr 2020 18:36:24 +0900 Subject: [PATCH 0516/1117] Fix vertical drag in down-scroll scenarios --- osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index fba80f92d2..af465af16a 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -52,9 +52,9 @@ namespace osu.Game.Rulesets.Mania.Edit if (drawableRuleset.ScrollingInfo.Direction.Value == ScrollingDirection.Down) { - // When scrolling downwards, the position is _negative_ when the object's start time is after the current time (e.g. in the middle of the stage). - // However all scrolling algorithms upwards scrolling, meaning that a positive (inverse) position is expected in the same scenario. - targetPosition = -targetPosition; + // We're dealing with screen coordinates in which the position decreases towards the centre of the screen resulting in an increase in start time. + // The scrolling algorithm assumes a top anchor meaning an increase in time corresponds to an increase in position, so when scrolling downwards the coordinates need to be flipped. + targetPosition = hoc.DrawHeight - targetPosition; } double targetTime = drawableRuleset.ScrollingInfo.Algorithm.TimeAt(targetPosition, From db12fafc2c3187220488e6235acc4f93cfa97687 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Apr 2020 18:53:30 +0900 Subject: [PATCH 0517/1117] Update comment --- .../TestSceneManiaHitObjectComposer.cs | 5 +++-- osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs index 4ce2424ffa..a84fe83245 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs @@ -14,6 +14,7 @@ using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Edit; using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Screens.Edit; @@ -139,8 +140,8 @@ namespace osu.Game.Rulesets.Mania.Tests AddAssert("hitobjects moved columns", () => composer.EditorBeatmap.HitObjects.All(h => ((ManiaHitObject)h).Column == 1)); - // Todo: They'll have moved vertically by half the height of a note. Probably a problem. - AddAssert("hitobjects not moved vertically", () => lastObject.DrawPosition.Y - originalPosition.Y < 10); + // Todo: They'll move vertically by the height of a note since there's no snapping and the selection point is the middle of the note. + AddAssert("hitobjects not moved vertically", () => lastObject.DrawPosition.Y - originalPosition.Y <= DefaultNotePiece.NOTE_HEIGHT); } private class TestComposer : CompositeDrawable diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index af465af16a..dfa933baad 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -53,7 +53,8 @@ namespace osu.Game.Rulesets.Mania.Edit if (drawableRuleset.ScrollingInfo.Direction.Value == ScrollingDirection.Down) { // We're dealing with screen coordinates in which the position decreases towards the centre of the screen resulting in an increase in start time. - // The scrolling algorithm assumes a top anchor meaning an increase in time corresponds to an increase in position, so when scrolling downwards the coordinates need to be flipped. + // The scrolling algorithm instead assumes a top anchor meaning an increase in time corresponds to an increase in position, + // so when scrolling downwards the coordinates need to be flipped. targetPosition = hoc.DrawHeight - targetPosition; } From ff3928465c06a7e578afe1088a9b0a045ea2eca7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Apr 2020 18:55:58 +0900 Subject: [PATCH 0518/1117] Add xmldoc --- osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs index 6960d15f31..1af7d06998 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs @@ -87,8 +87,17 @@ namespace osu.Game.Rulesets.Mania.UI return found; } + /// + /// Retrieves a by index. + /// + /// The index of the column. + /// The corresponding to the given index. + /// If is less than 0 or greater than . public Column GetColumn(int index) { + if (index < 0 || index > TotalColumns - 1) + throw new ArgumentOutOfRangeException(nameof(index)); + foreach (var stage in stages) { if (index >= stage.Columns.Count) From 1aaab40228baa33ccb6cadadf07e84805fa48f66 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Apr 2020 19:23:33 +0900 Subject: [PATCH 0519/1117] Fix mods affecting mania scroll speed --- .../UI/DrawableManiaRuleset.cs | 25 ++++++++++++++++--- osu.Game/Rulesets/UI/DrawableRuleset.cs | 8 +++--- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs index 14cad39b04..39f3331fbb 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs @@ -2,7 +2,9 @@ // 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.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; @@ -48,6 +50,10 @@ namespace osu.Game.Rulesets.Mania.UI protected new ManiaRulesetConfigManager Config => (ManiaRulesetConfigManager)base.Config; private readonly Bindable configDirection = new Bindable(); + private readonly Bindable configTimeRange = new Bindable(); + + // Stores the current speed adjustment active in gameplay. + private readonly Track speedAdjustmentTrack = new TrackVirtual(1000); public DrawableManiaRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) : base(ruleset, beatmap, mods) @@ -58,6 +64,9 @@ namespace osu.Game.Rulesets.Mania.UI [BackgroundDependencyLoader] private void load() { + foreach (var mod in Mods.OfType()) + mod.ApplyToTrack(speedAdjustmentTrack); + bool isForCurrentRuleset = Beatmap.BeatmapInfo.Ruleset.Equals(Ruleset.RulesetInfo); foreach (var p in ControlPoints) @@ -76,7 +85,8 @@ namespace osu.Game.Rulesets.Mania.UI Config.BindWith(ManiaRulesetSetting.ScrollDirection, configDirection); configDirection.BindValueChanged(direction => Direction.Value = (ScrollingDirection)direction.NewValue, true); - Config.BindWith(ManiaRulesetSetting.ScrollTime, TimeRange); + Config.BindWith(ManiaRulesetSetting.ScrollTime, configTimeRange); + configTimeRange.BindValueChanged(_ => updateTimeRange()); } protected override void AdjustScrollSpeed(int amount) @@ -86,10 +96,19 @@ namespace osu.Game.Rulesets.Mania.UI private double relativeTimeRange { - get => MAX_TIME_RANGE / TimeRange.Value; - set => TimeRange.Value = MAX_TIME_RANGE / value; + get => MAX_TIME_RANGE / configTimeRange.Value; + set => configTimeRange.Value = MAX_TIME_RANGE / value; } + protected override void Update() + { + base.Update(); + + updateTimeRange(); + } + + private void updateTimeRange() => TimeRange.Value = configTimeRange.Value * speedAdjustmentTrack.AggregateTempo.Value * speedAdjustmentTrack.AggregateFrequency.Value; + /// /// Retrieves the column that intersects a screen-space position. /// diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index 5062c92afe..0a1c35c7c6 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -107,7 +107,7 @@ namespace osu.Game.Rulesets.UI /// The mods which are to be applied. /// [Cached(typeof(IReadOnlyList))] - private readonly IReadOnlyList mods; + protected readonly IReadOnlyList Mods; private FrameStabilityContainer frameStabilityContainer; @@ -129,7 +129,7 @@ namespace osu.Game.Rulesets.UI throw new ArgumentException($"{GetType()} expected the beatmap to contain hitobjects of type {typeof(TObject)}.", nameof(beatmap)); Beatmap = tBeatmap; - this.mods = mods?.ToArray() ?? Array.Empty(); + Mods = mods?.ToArray() ?? Array.Empty(); RelativeSizeAxes = Axes.Both; @@ -204,7 +204,7 @@ namespace osu.Game.Rulesets.UI .WithChild(ResumeOverlay))); } - applyRulesetMods(mods, config); + applyRulesetMods(Mods, config); loadObjects(cancellationToken); } @@ -224,7 +224,7 @@ namespace osu.Game.Rulesets.UI Playfield.PostProcess(); - foreach (var mod in mods.OfType()) + foreach (var mod in Mods.OfType()) mod.ApplyToDrawableHitObjects(Playfield.AllHitObjects); } From 7868c0dad5cff824fe5bf7c90c0a8d4dea71d5eb Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Apr 2020 20:15:56 +0900 Subject: [PATCH 0520/1117] Fix test case failures --- .../TestSceneManiaHitObjectComposer.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs index a84fe83245..286e3f6e50 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs @@ -119,8 +119,6 @@ namespace osu.Game.Rulesets.Mania.Tests AddStep("seek to last object", () => { lastObject = this.ChildrenOfType().Single(d => d.HitObject == composer.EditorBeatmap.HitObjects.Last()); - originalPosition = lastObject.DrawPosition; - Clock.Seek(composer.EditorBeatmap.HitObjects.Last().StartTime); }); @@ -128,13 +126,18 @@ namespace osu.Game.Rulesets.Mania.Tests AddStep("click last object", () => { + originalPosition = lastObject.DrawPosition; + InputManager.MoveMouseTo(lastObject); InputManager.PressButton(MouseButton.Left); }); AddStep("move mouse right", () => { - InputManager.MoveMouseTo(lastObject, new Vector2(40, 0)); + var firstColumn = composer.Composer.Playfield.GetColumn(0); + var secondColumn = composer.Composer.Playfield.GetColumn(1); + + InputManager.MoveMouseTo(lastObject, new Vector2(secondColumn.ScreenSpaceDrawQuad.Centre.X - firstColumn.ScreenSpaceDrawQuad.Centre.X + 1, 0)); InputManager.ReleaseButton(MouseButton.Left); }); From 119000f1ab2157762863743d25d111b936f6a4a5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 28 Apr 2020 21:43:35 +0900 Subject: [PATCH 0521/1117] Reduce database includes where possible --- osu.Game/Beatmaps/BeatmapManager.cs | 54 ++++++++++++++++++++-- osu.Game/Beatmaps/BeatmapStore.cs | 12 +++++ osu.Game/Overlays/MusicController.cs | 2 +- osu.Game/Screens/Menu/IntroScreen.cs | 2 +- osu.Game/Screens/Select/BeatmapCarousel.cs | 2 +- osu.Game/Screens/Select/SongSelect.cs | 2 +- 6 files changed, 67 insertions(+), 7 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 6542866936..5651d07566 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -246,6 +246,12 @@ namespace osu.Game.Beatmaps if (beatmapInfo?.BeatmapSet == null || beatmapInfo == DefaultBeatmap?.BeatmapInfo) return DefaultBeatmap; + if (beatmapInfo.BeatmapSet.Files == null) + { + var info = beatmapInfo; + beatmapInfo = QueryBeatmap(b => b.ID == info.ID); + } + lock (workingCache) { var working = workingCache.FirstOrDefault(w => w.BeatmapInfo?.ID == beatmapInfo.ID); @@ -287,13 +293,34 @@ namespace osu.Game.Beatmaps /// Returns a list of all usable s. ///
    /// A list of available . - public List GetAllUsableBeatmapSets() => GetAllUsableBeatmapSetsEnumerable().ToList(); + public List GetAllUsableBeatmapSets(IncludedDetails includes = IncludedDetails.All) => GetAllUsableBeatmapSetsEnumerable(includes).ToList(); /// - /// Returns a list of all usable s. + /// Returns a list of all usable s. Note that files are not populated. /// + /// The level of detail to include in the returned objects. /// A list of available . - public IQueryable GetAllUsableBeatmapSetsEnumerable() => beatmaps.ConsumableItems.Where(s => !s.DeletePending && !s.Protected); + public IQueryable GetAllUsableBeatmapSetsEnumerable(IncludedDetails includes) + { + IQueryable queryable; + + switch (includes) + { + case IncludedDetails.Minimal: + queryable = beatmaps.BeatmapSetsOverview; + break; + + case IncludedDetails.AllButFiles: + queryable = beatmaps.BeatmapSetsWithoutFiles; + break; + + default: + queryable = beatmaps.ConsumableItems; + break; + } + + return queryable.Where(s => !s.DeletePending && !s.Protected); + } /// /// Perform a lookup query on available s. @@ -482,4 +509,25 @@ namespace osu.Game.Beatmaps } } } + + /// + /// The level of detail to include in database results. + /// + public enum IncludedDetails + { + /// + /// Only include beatmap difficulties and set level metadata. + /// + Minimal, + + /// + /// Include all difficulties, rulesets, difficulty metadata but no files. + /// + AllButFiles, + + /// + /// Include everything. + /// + All + } } diff --git a/osu.Game/Beatmaps/BeatmapStore.cs b/osu.Game/Beatmaps/BeatmapStore.cs index a2279fdb14..642bafd2ac 100644 --- a/osu.Game/Beatmaps/BeatmapStore.cs +++ b/osu.Game/Beatmaps/BeatmapStore.cs @@ -87,6 +87,18 @@ namespace osu.Game.Beatmaps base.Purge(items, context); } + public IQueryable BeatmapSetsOverview => ContextFactory.Get().BeatmapSetInfo + .Include(s => s.Metadata) + .Include(s => s.Beatmaps) + .AsNoTracking(); + + public IQueryable BeatmapSetsWithoutFiles => ContextFactory.Get().BeatmapSetInfo + .Include(s => s.Metadata) + .Include(s => s.Beatmaps).ThenInclude(s => s.Ruleset) + .Include(s => s.Beatmaps).ThenInclude(b => b.BaseDifficulty) + .Include(s => s.Beatmaps).ThenInclude(b => b.Metadata) + .AsNoTracking(); + public IQueryable Beatmaps => ContextFactory.Get().BeatmapInfo .Include(b => b.BeatmapSet).ThenInclude(s => s.Metadata) diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 6d269aa944..c872f82b32 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -66,7 +66,7 @@ namespace osu.Game.Overlays beatmaps.ItemAdded += handleBeatmapAdded; beatmaps.ItemRemoved += handleBeatmapRemoved; - beatmapSets.AddRange(beatmaps.GetAllUsableBeatmapSets().OrderBy(_ => RNG.Next())); + beatmapSets.AddRange(beatmaps.GetAllUsableBeatmapSets(IncludedDetails.Minimal).OrderBy(_ => RNG.Next())); } protected override void LoadComplete() diff --git a/osu.Game/Screens/Menu/IntroScreen.cs b/osu.Game/Screens/Menu/IntroScreen.cs index 26455b1dbd..d2296573a6 100644 --- a/osu.Game/Screens/Menu/IntroScreen.cs +++ b/osu.Game/Screens/Menu/IntroScreen.cs @@ -73,7 +73,7 @@ namespace osu.Game.Screens.Menu if (!MenuMusic.Value) { - var sets = beatmaps.GetAllUsableBeatmapSets(); + var sets = beatmaps.GetAllUsableBeatmapSets(IncludedDetails.Minimal); if (sets.Count > 0) setInfo = beatmaps.QueryBeatmapSet(s => s.ID == sets[RNG.Next(0, sets.Count - 1)].ID); } diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index f989ab2787..5a4a03662a 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -169,7 +169,7 @@ namespace osu.Game.Screens.Select loadBeatmapSets(GetLoadableBeatmaps()); } - protected virtual IEnumerable GetLoadableBeatmaps() => beatmaps.GetAllUsableBeatmapSetsEnumerable(); + protected virtual IEnumerable GetLoadableBeatmaps() => beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.AllButFiles); public void RemoveBeatmapSet(BeatmapSetInfo beatmapSet) => Schedule(() => { diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 0d07a335cf..c07465ca44 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -286,7 +286,7 @@ namespace osu.Game.Screens.Select Schedule(() => { // if we have no beatmaps but osu-stable is found, let's prompt the user to import. - if (!beatmaps.GetAllUsableBeatmapSetsEnumerable().Any() && beatmaps.StableInstallationAvailable) + if (!beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.Minimal).Any() && beatmaps.StableInstallationAvailable) { dialogOverlay.Push(new ImportFromStablePopup(() => { From 902326e7ac175923a5a1dbcc40adcbdb218f8f29 Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Tue, 28 Apr 2020 22:24:19 +0200 Subject: [PATCH 0522/1117] Slight refactoring --- .idea/.idea.osu.Desktop/.idea/discord.xml | 9 ++++ .../Skinning/TestSceneDrawableTaikoMascot.cs | 41 +++++++------------ 2 files changed, 24 insertions(+), 26 deletions(-) create mode 100644 .idea/.idea.osu.Desktop/.idea/discord.xml diff --git a/.idea/.idea.osu.Desktop/.idea/discord.xml b/.idea/.idea.osu.Desktop/.idea/discord.xml new file mode 100644 index 0000000000..59b11d1d39 --- /dev/null +++ b/.idea/.idea.osu.Desktop/.idea/discord.xml @@ -0,0 +1,9 @@ + + + + + + + \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs index a0ab3e5c25..41d7156e7e 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs @@ -93,21 +93,11 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning AddStep("Collect playfields", collectPlayfields); AddStep("Collect mascots", collectMascots); - AddStep("Create hit (miss)", () => - { - foreach (var playfield in playfields) - addJudgement(playfield, HitResult.Miss); - }); + AddStep("Create hit (great)", () => addJudgement(HitResult.Miss)); + AddUntilStep("Wait for idle state", () => checkForState(TaikoMascotAnimationState.Fail)); - AddUntilStep("Wait for fail state", () => mascots.Where(d => d != null).All(d => d.State == TaikoMascotAnimationState.Fail)); - - AddStep("Create hit (great)", () => - { - foreach (var playfield in playfields) - addJudgement(playfield, HitResult.Great); - }); - - AddUntilStep("Wait for idle state", () => mascots.Where(d => d != null).All(d => d.State == TaikoMascotAnimationState.Idle)); + AddStep("Create hit (great)", () => addJudgement(HitResult.Great)); + AddUntilStep("Wait for idle state", () => checkForState(TaikoMascotAnimationState.Idle)); } [Test] @@ -134,15 +124,10 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning AddStep("Collect playfields", collectPlayfields); AddStep("Collect mascots", collectMascots); - AddUntilStep("Wait for fail state", () => mascots.Where(d => d != null).All(d => d.State == TaikoMascotAnimationState.Fail)); + AddUntilStep("Wait for idle state", () => checkForState(TaikoMascotAnimationState.Fail)); - AddStep("Create hit (great)", () => - { - foreach (var playfield in playfields) - addJudgement(playfield, HitResult.Great); - }); - - AddUntilStep("Wait for kiai state", () => mascots.Where(d => d != null).All(d => d.State == TaikoMascotAnimationState.Kiai)); + AddStep("Create hit (great)", () => addJudgement(HitResult.Great)); + AddUntilStep("Wait for idle state", () => checkForState(TaikoMascotAnimationState.Kiai)); } private void setBeatmap(bool kiai = false) @@ -190,18 +175,22 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning foreach (var playfield in playfields) { - var mascot = playfield.ChildrenOfType() + var mascot = playfield.ChildrenOfType() .SingleOrDefault(); - if (mascot != null) mascots.Add(mascot); + if (mascot != null) + mascots.Add(mascot); } } - private void addJudgement(TaikoPlayfield playfield, HitResult result) + private void addJudgement(HitResult result) { - playfield.OnNewResult(new DrawableHit(new Hit()), new JudgementResult(new HitObject(), new TaikoJudgement()) { Type = result }); + foreach (var playfield in playfields) + playfield.OnNewResult(new DrawableHit(new Hit()), new JudgementResult(new HitObject(), new TaikoJudgement()) { Type = result }); } + private bool checkForState(TaikoMascotAnimationState state) => mascots.All(d => d.State == state); + private class TestDrawableTaikoMascot : DrawableTaikoMascot { public TestDrawableTaikoMascot(TaikoMascotAnimationState startingState = TaikoMascotAnimationState.Idle) From 0d285ac0f40e8343eb4462389c9366177bd409b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 28 Apr 2020 23:17:52 +0200 Subject: [PATCH 0523/1117] Revert unrelated change to Rider configuration --- .idea/.idea.osu.Desktop/.idea/projectSettingsUpdater.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.idea/.idea.osu.Desktop/.idea/projectSettingsUpdater.xml b/.idea/.idea.osu.Desktop/.idea/projectSettingsUpdater.xml index 7515e76054..4bb9f4d2a0 100644 --- a/.idea/.idea.osu.Desktop/.idea/projectSettingsUpdater.xml +++ b/.idea/.idea.osu.Desktop/.idea/projectSettingsUpdater.xml @@ -1,6 +1,6 @@ - \ No newline at end of file From 24216b6600106282f68565b85677dd260ef186c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 28 Apr 2020 23:22:50 +0200 Subject: [PATCH 0524/1117] Remove unnecessary lists --- .../Skinning/TestSceneDrawableTaikoMascot.cs | 50 ++----------------- 1 file changed, 5 insertions(+), 45 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs index a0ab3e5c25..ae7dc71cc9 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs @@ -39,9 +39,8 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning private Bindable workingBeatmap; - private readonly List mascots = new List(); - private readonly List playfields = new List(); - private readonly List rulesets = new List(); + private IEnumerable mascots => this.ChildrenOfType(); + private IEnumerable playfields => this.ChildrenOfType(); [BackgroundDependencyLoader] private void load(Bindable beatmap) @@ -56,14 +55,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning AddStep("Create mascot (idle)", () => { - mascots.Clear(); - - SetContents(() => - { - var mascot = new TestDrawableTaikoMascot(); - mascots.Add(mascot); - return mascot; - }); + SetContents(() => new TestDrawableTaikoMascot()); }); AddStep("Clear state", () => setState(TaikoMascotAnimationState.Clear)); @@ -80,19 +72,13 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning AddStep("Create ruleset", () => { - rulesets.Clear(); SetContents(() => { var ruleset = new TaikoRuleset(); - var drawableRuleset = new DrawableTaikoRuleset(ruleset, workingBeatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo)); - rulesets.Add(drawableRuleset); - return drawableRuleset; + return new DrawableTaikoRuleset(ruleset, workingBeatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo)); }); }); - AddStep("Collect playfields", collectPlayfields); - AddStep("Collect mascots", collectMascots); - AddStep("Create hit (miss)", () => { foreach (var playfield in playfields) @@ -121,19 +107,13 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning { workingBeatmap.Value.Track.Start(); - rulesets.Clear(); SetContents(() => { var ruleset = new TaikoRuleset(); - var drawableRuleset = new DrawableTaikoRuleset(ruleset, workingBeatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo)); - rulesets.Add(drawableRuleset); - return drawableRuleset; + return new DrawableTaikoRuleset(ruleset, workingBeatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo)); }); }); - AddStep("Collect playfields", collectPlayfields); - AddStep("Collect mascots", collectMascots); - AddUntilStep("Wait for fail state", () => mascots.Where(d => d != null).All(d => d.State == TaikoMascotAnimationState.Fail)); AddStep("Create hit (great)", () => @@ -177,26 +157,6 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning mascot?.ShowState(state); } - private void collectPlayfields() - { - playfields.Clear(); - foreach (var ruleset in rulesets) - playfields.Add(ruleset.ChildrenOfType().Single()); - } - - private void collectMascots() - { - mascots.Clear(); - - foreach (var playfield in playfields) - { - var mascot = playfield.ChildrenOfType() - .SingleOrDefault(); - - if (mascot != null) mascots.Add(mascot); - } - } - private void addJudgement(TaikoPlayfield playfield, HitResult result) { playfield.OnNewResult(new DrawableHit(new Hit()), new JudgementResult(new HitObject(), new TaikoJudgement()) { Type = result }); From 15bbedca8796a4c91975592ae6005d211195449b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 28 Apr 2020 23:24:21 +0200 Subject: [PATCH 0525/1117] Remove unnecessary beatmap field --- .../Skinning/TestSceneDrawableTaikoMascot.cs | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs index ae7dc71cc9..8f45fec3f9 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; @@ -37,17 +36,9 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning TimeRange = { Value = 5000 }, }; - private Bindable workingBeatmap; - private IEnumerable mascots => this.ChildrenOfType(); private IEnumerable playfields => this.ChildrenOfType(); - [BackgroundDependencyLoader] - private void load(Bindable beatmap) - { - workingBeatmap = beatmap; - } - [Test] public void TestStateTextures() { @@ -75,7 +66,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning SetContents(() => { var ruleset = new TaikoRuleset(); - return new DrawableTaikoRuleset(ruleset, workingBeatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo)); + return new DrawableTaikoRuleset(ruleset, Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo)); }); }); @@ -101,16 +92,16 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning { AddStep("Set beatmap", () => setBeatmap(true)); - AddUntilStep("Wait for beatmap to be loaded", () => workingBeatmap.Value.Track.IsLoaded); + AddUntilStep("Wait for beatmap to be loaded", () => Beatmap.Value.Track.IsLoaded); AddStep("Create kiai ruleset", () => { - workingBeatmap.Value.Track.Start(); + Beatmap.Value.Track.Start(); SetContents(() => { var ruleset = new TaikoRuleset(); - return new DrawableTaikoRuleset(ruleset, workingBeatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo)); + return new DrawableTaikoRuleset(ruleset, Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo)); }); }); @@ -133,7 +124,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning if (kiai) controlPointInfo.Add(0, new EffectControlPoint { KiaiMode = true }); - workingBeatmap.Value = CreateWorkingBeatmap(new Beatmap + Beatmap.Value = CreateWorkingBeatmap(new Beatmap { HitObjects = new List { new Hit { Type = HitType.Centre } }, BeatmapInfo = new BeatmapInfo From e7e529ab99944f62bbc31f711295519950426b84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 28 Apr 2020 23:26:10 +0200 Subject: [PATCH 0526/1117] Remove unnecessary null checks --- .../Skinning/TestSceneDrawableTaikoMascot.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs index 8f45fec3f9..83bf64eb46 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs @@ -76,7 +76,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning addJudgement(playfield, HitResult.Miss); }); - AddUntilStep("Wait for fail state", () => mascots.Where(d => d != null).All(d => d.State == TaikoMascotAnimationState.Fail)); + AddUntilStep("Wait for fail state", () => mascots.All(d => d.State == TaikoMascotAnimationState.Fail)); AddStep("Create hit (great)", () => { @@ -84,7 +84,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning addJudgement(playfield, HitResult.Great); }); - AddUntilStep("Wait for idle state", () => mascots.Where(d => d != null).All(d => d.State == TaikoMascotAnimationState.Idle)); + AddUntilStep("Wait for idle state", () => mascots.All(d => d.State == TaikoMascotAnimationState.Idle)); } [Test] @@ -105,7 +105,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning }); }); - AddUntilStep("Wait for fail state", () => mascots.Where(d => d != null).All(d => d.State == TaikoMascotAnimationState.Fail)); + AddUntilStep("Wait for fail state", () => mascots.All(d => d.State == TaikoMascotAnimationState.Fail)); AddStep("Create hit (great)", () => { @@ -113,7 +113,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning addJudgement(playfield, HitResult.Great); }); - AddUntilStep("Wait for kiai state", () => mascots.Where(d => d != null).All(d => d.State == TaikoMascotAnimationState.Kiai)); + AddUntilStep("Wait for kiai state", () => mascots.All(d => d.State == TaikoMascotAnimationState.Kiai)); } private void setBeatmap(bool kiai = false) From d7b072dd6e90138a6607deff4c9943c019733cf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 29 Apr 2020 00:00:01 +0200 Subject: [PATCH 0527/1117] Fix post-merge test regression --- .../Skinning/TestSceneDrawableTaikoMascot.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs index 83bf64eb46..4eeeb52e2f 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs @@ -14,7 +14,6 @@ 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.Objects.Drawables; using osu.Game.Rulesets.Taiko.UI; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Tests.Visual; @@ -150,7 +149,10 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning private void addJudgement(TaikoPlayfield playfield, HitResult result) { - playfield.OnNewResult(new DrawableHit(new Hit()), new JudgementResult(new HitObject(), new TaikoJudgement()) { Type = result }); + var hit = new DrawableTestHit(new Hit(), result); + Add(hit); + + playfield.OnNewResult(hit, new JudgementResult(hit.HitObject, new TaikoJudgement()) { Type = result }); } private class TestDrawableTaikoMascot : DrawableTaikoMascot From 00918ecb6d6c47f6c131fca8991448825e9297e3 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 29 Apr 2020 04:43:49 +0300 Subject: [PATCH 0528/1117] Replace interval collection with a more-specific immutable component Covers all small changes into one commit: - Remove generics and use `double` type instead. - Make the component immutable and not enumerable for simplicity of it's worth. - Make the component more-specific (to time period tracking) - Apply small adjustments to the component --- osu.Game/Lists/IntervalList.cs | 111 -------------------------------- osu.Game/Utils/PeriodTracker.cs | 90 ++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 111 deletions(-) delete mode 100644 osu.Game/Lists/IntervalList.cs create mode 100644 osu.Game/Utils/PeriodTracker.cs diff --git a/osu.Game/Lists/IntervalList.cs b/osu.Game/Lists/IntervalList.cs deleted file mode 100644 index 580015bb96..0000000000 --- a/osu.Game/Lists/IntervalList.cs +++ /dev/null @@ -1,111 +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; -using System.Collections.Generic; -using osu.Framework.Lists; - -namespace osu.Game.Lists -{ - /// - /// Represents a list of intervals that can be used for whether a specific value falls into one of them. - /// - /// The type of interval values. - public class IntervalList : IEnumerable> - where T : struct, IConvertible - { - private static readonly IComparer type_comparer = Comparer.Default; - - private readonly SortedList> intervals = new SortedList>((x, y) => type_comparer.Compare(x.Start, y.Start)); - private int nearestIndex; - - public Interval this[int i] - { - get => intervals[i]; - set => intervals[i] = value; - } - - /// - /// Whether the provided value is in any interval added to this list. - /// - /// The value to check for. - public bool IsInAnyInterval(T value) - { - if (intervals.Count == 0) - return false; - - // Clamp the nearest index in case there were intervals - // removed from the list causing the index to go out of range. - nearestIndex = Math.Clamp(nearestIndex, 0, intervals.Count - 1); - - if (type_comparer.Compare(value, this[nearestIndex].End) > 0) - { - while (type_comparer.Compare(value, this[nearestIndex].End) > 0 && nearestIndex < intervals.Count - 1) - nearestIndex++; - } - else - { - while (type_comparer.Compare(value, this[nearestIndex].Start) < 0 && nearestIndex > 0) - nearestIndex--; - } - - var nearestInterval = this[nearestIndex]; - - return type_comparer.Compare(value, nearestInterval.Start) >= 0 && - type_comparer.Compare(value, nearestInterval.End) <= 0; - } - - /// - /// Adds a new interval to the list. - /// - /// The start value of the interval. - /// The end value of the interval. - public void Add(T start, T end) => Add(new Interval(start, end)); - - /// - /// Adds a new interval to the list - /// - /// The interval to add. - public void Add(Interval interval) => intervals.Add(interval); - - /// - /// Removes an existing interval from the list. - /// - /// The interval to remove. - /// Whether the provided interval exists in the list and has been removed. - public bool Remove(Interval interval) => intervals.Remove(interval); - - /// - /// Removes all intervals from the list. - /// - public void Clear() => intervals.Clear(); - - public IEnumerator> GetEnumerator() => intervals.GetEnumerator(); - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - } - - public readonly struct Interval - where T : struct, IConvertible - { - /// - /// The start value of this interval. - /// - public readonly T Start; - - /// - /// The end value of this interval. - /// - public readonly T End; - - public Interval(T start, T end) - { - if (Comparer.Default.Compare(start, end) >= 0) - throw new ArgumentException($"Invalid interval, {nameof(start)} must be less than {nameof(end)}", nameof(start)); - - Start = start; - End = end; - } - } -} diff --git a/osu.Game/Utils/PeriodTracker.cs b/osu.Game/Utils/PeriodTracker.cs new file mode 100644 index 0000000000..589f061c1d --- /dev/null +++ b/osu.Game/Utils/PeriodTracker.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. + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace osu.Game.Utils +{ + /// + /// Represents a tracking component used for whether a + /// specific time falls into any of the provided periods. + /// + public class PeriodTracker + { + private readonly List periods = new List(); + private int nearestIndex; + + /// + /// The list of periods to add to the tracker for using the required check methods. + /// + public IEnumerable Periods + { + set + { + var sortedValue = value?.ToList(); + sortedValue?.Sort(); + + if (sortedValue != null && periods.SequenceEqual(sortedValue)) + return; + + periods.Clear(); + nearestIndex = 0; + + if (value?.Any() != true) + return; + + periods.AddRange(sortedValue); + } + } + + /// + /// Whether the provided time is in any of the added periods. + /// + /// The time value to check for. + public bool Contains(double time) + { + if (periods.Count == 0) + return false; + + if (time > periods[nearestIndex].End) + { + while (time > periods[nearestIndex].End && nearestIndex < periods.Count - 1) + nearestIndex++; + } + else + { + while (time < periods[nearestIndex].Start && nearestIndex > 0) + nearestIndex--; + } + + var nearest = periods[nearestIndex]; + return time >= nearest.Start && time <= nearest.End; + } + } + + public readonly struct Period : IComparable + { + /// + /// The start time of this period. + /// + public readonly double Start; + + /// + /// The end time of this period. + /// + public readonly double End; + + public Period(double start, double end) + { + if (start >= end) + throw new ArgumentException($"Invalid period provided, {nameof(start)} must be less than {nameof(end)}", nameof(start)); + + Start = start; + End = end; + } + + public int CompareTo(Period other) => Start.CompareTo(other.Start); + } +} From 587f946dc63ba8315b8f90b8b1cd9b5d42a9fc06 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 29 Apr 2020 04:58:08 +0300 Subject: [PATCH 0529/1117] Adjust, simplify and make the test code more flexible to some degree --- osu.Game.Tests/Lists/IntervalListTest.cs | 115 ------------------ osu.Game.Tests/NonVisual/PeriodTrackerTest.cs | 103 ++++++++++++++++ 2 files changed, 103 insertions(+), 115 deletions(-) delete mode 100644 osu.Game.Tests/Lists/IntervalListTest.cs create mode 100644 osu.Game.Tests/NonVisual/PeriodTrackerTest.cs diff --git a/osu.Game.Tests/Lists/IntervalListTest.cs b/osu.Game.Tests/Lists/IntervalListTest.cs deleted file mode 100644 index 0958f0fa7c..0000000000 --- a/osu.Game.Tests/Lists/IntervalListTest.cs +++ /dev/null @@ -1,115 +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 NUnit.Framework; -using osu.Game.Lists; - -namespace osu.Game.Tests.Lists -{ - [TestFixture] - public class IntervalListTest - { - // this is intended to be unordered to test adding intervals in unordered way. - private static readonly (double, double)[] test_intervals = - { - (-9.1d, -8.3d), - (-3.4d, 2.1d), - (9.0d, 50.0d), - (5.25d, 10.50d), - }; - - [Test] - public void TestCheckValueInsideSingleInterval() - { - var list = new IntervalList { { 1.0d, 2.0d } }; - - Assert.IsTrue(list.IsInAnyInterval(1.0d)); - Assert.IsTrue(list.IsInAnyInterval(1.5d)); - Assert.IsTrue(list.IsInAnyInterval(2.0d)); - } - - [Test] - public void TestCheckValuesInsideIntervals() - { - var list = new IntervalList(); - - foreach (var (start, end) in test_intervals) - list.Add(start, end); - - Assert.IsTrue(list.IsInAnyInterval(-8.75d)); - Assert.IsTrue(list.IsInAnyInterval(1.0d)); - Assert.IsTrue(list.IsInAnyInterval(7.89d)); - Assert.IsTrue(list.IsInAnyInterval(9.8d)); - Assert.IsTrue(list.IsInAnyInterval(15.83d)); - } - - [Test] - public void TestCheckValuesInRandomOrder() - { - var list = new IntervalList(); - - foreach (var (start, end) in test_intervals) - list.Add(start, end); - - Assert.IsTrue(list.IsInAnyInterval(9.8d)); - Assert.IsTrue(list.IsInAnyInterval(7.89d)); - Assert.IsTrue(list.IsInAnyInterval(1.0d)); - Assert.IsTrue(list.IsInAnyInterval(15.83d)); - Assert.IsTrue(list.IsInAnyInterval(-8.75d)); - } - - [Test] - public void TestCheckValuesOutOfIntervals() - { - var list = new IntervalList(); - - foreach (var (start, end) in test_intervals) - list.Add(start, end); - - Assert.IsFalse(list.IsInAnyInterval(-9.2d)); - Assert.IsFalse(list.IsInAnyInterval(2.2d)); - Assert.IsFalse(list.IsInAnyInterval(5.15d)); - Assert.IsFalse(list.IsInAnyInterval(51.2d)); - } - - [Test] - public void TestCheckValueAfterRemovedInterval() - { - var list = new IntervalList { { 50, 100 }, { 150, 200 }, { 250, 300 } }; - - Assert.IsTrue(list.IsInAnyInterval(75)); - Assert.IsTrue(list.IsInAnyInterval(175)); - Assert.IsTrue(list.IsInAnyInterval(275)); - - list.Remove(list[1]); - - Assert.IsFalse(list.IsInAnyInterval(175)); - Assert.IsTrue(list.IsInAnyInterval(75)); - Assert.IsTrue(list.IsInAnyInterval(275)); - } - - [Test] - public void TestCheckValueAfterAddedInterval() - { - var list = new IntervalList { { 50, 100 }, { 250, 300 } }; - - Assert.IsFalse(list.IsInAnyInterval(175)); - Assert.IsTrue(list.IsInAnyInterval(75)); - Assert.IsTrue(list.IsInAnyInterval(275)); - - list.Add(150, 200); - - Assert.IsTrue(list.IsInAnyInterval(175)); - } - - [Test] - public void TestReversedIntervalThrows() - { - var list = new IntervalList(); - - Assert.Throws(() => list.Add(50, 25)); - Assert.Throws(() => list.Add(new Interval(50, 25))); - } - } -} diff --git a/osu.Game.Tests/NonVisual/PeriodTrackerTest.cs b/osu.Game.Tests/NonVisual/PeriodTrackerTest.cs new file mode 100644 index 0000000000..39eea2b386 --- /dev/null +++ b/osu.Game.Tests/NonVisual/PeriodTrackerTest.cs @@ -0,0 +1,103 @@ +// 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.Utils; +using osu.Game.Utils; + +namespace osu.Game.Tests.NonVisual +{ + [TestFixture] + public class PeriodTrackerTest + { + private static readonly Period[] test_single_period = { new Period(1.0, 2.0) }; + + // this is intended to be unordered to test adding periods in unordered way. + private static readonly Period[] test_periods = + { + new Period(-9.1, -8.3), + new Period(-3.4, 2.1), + new Period(9.0, 50.0), + new Period(5.25, 10.50) + }; + + [Test] + public void TestCheckValueInsideSinglePeriod() + { + var tracker = new PeriodTracker { Periods = test_single_period }; + + var period = test_single_period.Single(); + Assert.IsTrue(tracker.Contains(period.Start)); + Assert.IsTrue(tracker.Contains(getMidTime(period))); + Assert.IsTrue(tracker.Contains(period.End)); + } + + [Test] + public void TestCheckValuesInsidePeriods() + { + var tracker = new PeriodTracker { Periods = test_periods }; + + foreach (var period in test_periods) + Assert.IsTrue(tracker.Contains(getMidTime(period))); + } + + [Test] + public void TestCheckValuesInRandomOrder() + { + var tracker = new PeriodTracker { Periods = test_periods }; + + foreach (var period in test_periods.OrderBy(_ => RNG.Next())) + Assert.IsTrue(tracker.Contains(getMidTime(period))); + } + + [Test] + public void TestCheckValuesOutOfPeriods() + { + var tracker = new PeriodTracker + { + Periods = new[] + { + new Period(1.0, 2.0), + new Period(3.0, 4.0) + } + }; + + Assert.IsFalse(tracker.Contains(0.9), "Time before first period is being considered inside"); + + Assert.IsFalse(tracker.Contains(2.1), "Time right after first period is being considered inside"); + Assert.IsFalse(tracker.Contains(2.9), "Time right before second period is being considered inside"); + + Assert.IsFalse(tracker.Contains(4.1), "Time after last period is being considered inside"); + } + + [Test] + public void TestNullRemovesExistingPeriods() + { + var tracker = new PeriodTracker { Periods = test_single_period }; + + var period = test_single_period.Single(); + Assert.IsTrue(tracker.Contains(getMidTime(period))); + + tracker.Periods = null; + Assert.IsFalse(tracker.Contains(getMidTime(period))); + } + + [Test] + public void TestReversedPeriodHandling() + { + var tracker = new PeriodTracker(); + + Assert.Throws(() => + { + tracker.Periods = new[] + { + new Period(2.0, 1.0) + }; + }); + } + + private double getMidTime(Period period) => period.Start + (period.End - period.Start) / 2; + } +} From 8d899f4e7742e93b8afa0432e865685e3b92e71f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 29 Apr 2020 05:07:58 +0300 Subject: [PATCH 0530/1117] Apply changes to the BreakTracker and more adjustment --- osu.Game/Screens/Play/BreakTracker.cs | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/osu.Game/Screens/Play/BreakTracker.cs b/osu.Game/Screens/Play/BreakTracker.cs index c2eb069ee6..e30c0c6dec 100644 --- a/osu.Game/Screens/Play/BreakTracker.cs +++ b/osu.Game/Screens/Play/BreakTracker.cs @@ -2,20 +2,22 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Beatmaps.Timing; -using osu.Game.Lists; using osu.Game.Rulesets.Scoring; +using osu.Game.Utils; namespace osu.Game.Screens.Play { public class BreakTracker : Component { private readonly ScoreProcessor scoreProcessor; - private readonly double gameplayStartTime; + private readonly PeriodTracker tracker = new PeriodTracker(); + /// /// Whether the gameplay is currently in a break. /// @@ -23,22 +25,14 @@ namespace osu.Game.Screens.Play private readonly BindableBool isBreakTime = new BindableBool(); - private readonly IntervalList breakIntervals = new IntervalList(); - public IReadOnlyList Breaks { set { isBreakTime.Value = false; - breakIntervals.Clear(); - foreach (var b in value) - { - if (!b.HasEffect) - continue; - - breakIntervals.Add(b.StartTime, b.EndTime - BreakOverlay.BREAK_FADE_DURATION); - } + tracker.Periods = value?.Where(b => b.HasEffect) + .Select(b => new Period(b.StartTime, b.EndTime - BreakOverlay.BREAK_FADE_DURATION)); } } @@ -54,7 +48,7 @@ namespace osu.Game.Screens.Play var time = Clock.CurrentTime; - isBreakTime.Value = breakIntervals.IsInAnyInterval(time) + isBreakTime.Value = tracker.Contains(time) || time < gameplayStartTime || scoreProcessor?.HasCompleted == true; } From 6e76e5900a4d965eddecb7dc2223d47cf8d2c38b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 29 Apr 2020 05:08:38 +0300 Subject: [PATCH 0531/1117] Rename is-in-any check method to a more legible name --- osu.Game.Tests/NonVisual/PeriodTrackerTest.cs | 22 +++++++++---------- osu.Game/Screens/Play/BreakTracker.cs | 2 +- osu.Game/Utils/PeriodTracker.cs | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/NonVisual/PeriodTrackerTest.cs b/osu.Game.Tests/NonVisual/PeriodTrackerTest.cs index 39eea2b386..f033672576 100644 --- a/osu.Game.Tests/NonVisual/PeriodTrackerTest.cs +++ b/osu.Game.Tests/NonVisual/PeriodTrackerTest.cs @@ -29,9 +29,9 @@ namespace osu.Game.Tests.NonVisual var tracker = new PeriodTracker { Periods = test_single_period }; var period = test_single_period.Single(); - Assert.IsTrue(tracker.Contains(period.Start)); - Assert.IsTrue(tracker.Contains(getMidTime(period))); - Assert.IsTrue(tracker.Contains(period.End)); + Assert.IsTrue(tracker.IsInAny(period.Start)); + Assert.IsTrue(tracker.IsInAny(getMidTime(period))); + Assert.IsTrue(tracker.IsInAny(period.End)); } [Test] @@ -40,7 +40,7 @@ namespace osu.Game.Tests.NonVisual var tracker = new PeriodTracker { Periods = test_periods }; foreach (var period in test_periods) - Assert.IsTrue(tracker.Contains(getMidTime(period))); + Assert.IsTrue(tracker.IsInAny(getMidTime(period))); } [Test] @@ -49,7 +49,7 @@ namespace osu.Game.Tests.NonVisual var tracker = new PeriodTracker { Periods = test_periods }; foreach (var period in test_periods.OrderBy(_ => RNG.Next())) - Assert.IsTrue(tracker.Contains(getMidTime(period))); + Assert.IsTrue(tracker.IsInAny(getMidTime(period))); } [Test] @@ -64,12 +64,12 @@ namespace osu.Game.Tests.NonVisual } }; - Assert.IsFalse(tracker.Contains(0.9), "Time before first period is being considered inside"); + Assert.IsFalse(tracker.IsInAny(0.9), "Time before first period is being considered inside"); - Assert.IsFalse(tracker.Contains(2.1), "Time right after first period is being considered inside"); - Assert.IsFalse(tracker.Contains(2.9), "Time right before second period is being considered inside"); + Assert.IsFalse(tracker.IsInAny(2.1), "Time right after first period is being considered inside"); + Assert.IsFalse(tracker.IsInAny(2.9), "Time right before second period is being considered inside"); - Assert.IsFalse(tracker.Contains(4.1), "Time after last period is being considered inside"); + Assert.IsFalse(tracker.IsInAny(4.1), "Time after last period is being considered inside"); } [Test] @@ -78,10 +78,10 @@ namespace osu.Game.Tests.NonVisual var tracker = new PeriodTracker { Periods = test_single_period }; var period = test_single_period.Single(); - Assert.IsTrue(tracker.Contains(getMidTime(period))); + Assert.IsTrue(tracker.IsInAny(getMidTime(period))); tracker.Periods = null; - Assert.IsFalse(tracker.Contains(getMidTime(period))); + Assert.IsFalse(tracker.IsInAny(getMidTime(period))); } [Test] diff --git a/osu.Game/Screens/Play/BreakTracker.cs b/osu.Game/Screens/Play/BreakTracker.cs index e30c0c6dec..eb77cb9369 100644 --- a/osu.Game/Screens/Play/BreakTracker.cs +++ b/osu.Game/Screens/Play/BreakTracker.cs @@ -48,7 +48,7 @@ namespace osu.Game.Screens.Play var time = Clock.CurrentTime; - isBreakTime.Value = tracker.Contains(time) + isBreakTime.Value = tracker.IsInAny(time) || time < gameplayStartTime || scoreProcessor?.HasCompleted == true; } diff --git a/osu.Game/Utils/PeriodTracker.cs b/osu.Game/Utils/PeriodTracker.cs index 589f061c1d..49b372bb27 100644 --- a/osu.Game/Utils/PeriodTracker.cs +++ b/osu.Game/Utils/PeriodTracker.cs @@ -43,7 +43,7 @@ namespace osu.Game.Utils /// Whether the provided time is in any of the added periods. /// /// The time value to check for. - public bool Contains(double time) + public bool IsInAny(double time) { if (periods.Count == 0) return false; From 024f10a494f124e632652cae331bbcce1dbf3c39 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 29 Apr 2020 13:24:31 +0900 Subject: [PATCH 0532/1117] Use non-generic bindable 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/UI/DrawableManiaRuleset.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs index 39f3331fbb..fa26e6d713 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs @@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Mania.UI protected new ManiaRulesetConfigManager Config => (ManiaRulesetConfigManager)base.Config; private readonly Bindable configDirection = new Bindable(); - private readonly Bindable configTimeRange = new Bindable(); + private readonly Bindable configTimeRange = new BindableDouble(); // Stores the current speed adjustment active in gameplay. private readonly Track speedAdjustmentTrack = new TrackVirtual(1000); From 0c95d11fdb0db19fc1610774961ffe376241d3e5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 29 Apr 2020 13:27:33 +0900 Subject: [PATCH 0533/1117] Remove unnecessary value change binding --- osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs index fa26e6d713..00fa68d088 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs @@ -86,7 +86,6 @@ namespace osu.Game.Rulesets.Mania.UI configDirection.BindValueChanged(direction => Direction.Value = (ScrollingDirection)direction.NewValue, true); Config.BindWith(ManiaRulesetSetting.ScrollTime, configTimeRange); - configTimeRange.BindValueChanged(_ => updateTimeRange()); } protected override void AdjustScrollSpeed(int amount) From 4f332ace1426b1c6840d0b4af3aad2e4a8e58e14 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 29 Apr 2020 14:27:21 +0900 Subject: [PATCH 0534/1117] Use 0 length --- osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs index 00fa68d088..f3f843f366 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs @@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Mania.UI private readonly Bindable configTimeRange = new BindableDouble(); // Stores the current speed adjustment active in gameplay. - private readonly Track speedAdjustmentTrack = new TrackVirtual(1000); + private readonly Track speedAdjustmentTrack = new TrackVirtual(0); public DrawableManiaRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) : base(ruleset, beatmap, mods) From c73d45bc01de6d2d2175c028400bb4c6d727c2f4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 29 Apr 2020 15:23:28 +0900 Subject: [PATCH 0535/1117] Reduce initial channel load overhead by only loading history on active channel --- osu.Game/Online/Chat/ChannelManager.cs | 12 ++++++------ osu.Game/Overlays/Chat/DrawableChannel.cs | 8 ++++++++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 822f628dd2..53872ddcba 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -93,6 +93,12 @@ namespace osu.Game.Online.Chat { if (!(e.NewValue is ChannelSelectorTabItem.ChannelSelectorTabChannel)) JoinChannel(e.NewValue); + + if (e.NewValue?.MessagesLoaded == false) + { + // let's fetch a small number of messages to bring us up-to-date with the backlog. + fetchInitalMessages(e.NewValue); + } } /// @@ -375,12 +381,6 @@ namespace osu.Game.Online.Chat if (CurrentChannel.Value == null) CurrentChannel.Value = channel; - if (!channel.MessagesLoaded) - { - // let's fetch a small number of messages to bring us up-to-date with the backlog. - fetchInitalMessages(channel); - } - return channel; } diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs index 6019657cf0..d63faebae4 100644 --- a/osu.Game/Overlays/Chat/DrawableChannel.cs +++ b/osu.Game/Overlays/Chat/DrawableChannel.cs @@ -105,6 +105,14 @@ namespace osu.Game.Overlays.Chat private void newMessagesArrived(IEnumerable newMessages) { + if (newMessages.Min(m => m.Id) < chatLines.Max(c => c.Message.Id)) + { + // there is a case (on initial population) that we may receive past messages and need to reorder. + // easiest way is to just combine messages and recreate drawables (less worrying about day separators etc.) + newMessages = newMessages.Concat(chatLines.Select(c => c.Message)).OrderBy(m => m.Id).ToList(); + ChatLineFlow.Clear(); + } + bool shouldScrollToEnd = scroll.IsScrolledToEnd(10) || !chatLines.Any() || newMessages.Any(m => m is LocalMessage); // Add up to last Channel.MAX_HISTORY messages From d1ec99ffd9c790bf57f10a9592bd9e06b7b4df53 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 29 Apr 2020 16:51:22 +0900 Subject: [PATCH 0536/1117] Further improve beatmap carousel load performance by avoiding incorrect query construction --- osu.Game/Beatmaps/BeatmapManager.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 5651d07566..b8dfac0342 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -300,7 +300,7 @@ namespace osu.Game.Beatmaps /// /// The level of detail to include in the returned objects. /// A list of available . - public IQueryable GetAllUsableBeatmapSetsEnumerable(IncludedDetails includes) + public IEnumerable GetAllUsableBeatmapSetsEnumerable(IncludedDetails includes) { IQueryable queryable; @@ -319,7 +319,10 @@ namespace osu.Game.Beatmaps break; } - return queryable.Where(s => !s.DeletePending && !s.Protected); + // AsEnumerable used here to avoid applying the WHERE in sql. When done so, ef core 2.x uses an incorrect ORDER BY + // clause which causes queries to take 5-10x longer. + // TODO: remove if upgrading to EF core 3.x. + return queryable.AsEnumerable().Where(s => !s.DeletePending && !s.Protected); } /// From 4a101ca7151cbb1bc75bb0ce5143cc863e7aa265 Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Wed, 29 Apr 2020 10:46:32 +0200 Subject: [PATCH 0537/1117] Revert project config file change --- .idea/.idea.osu.Desktop/.idea/projectSettingsUpdater.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.idea/.idea.osu.Desktop/.idea/projectSettingsUpdater.xml b/.idea/.idea.osu.Desktop/.idea/projectSettingsUpdater.xml index 4bb9f4d2a0..7515e76054 100644 --- a/.idea/.idea.osu.Desktop/.idea/projectSettingsUpdater.xml +++ b/.idea/.idea.osu.Desktop/.idea/projectSettingsUpdater.xml @@ -1,6 +1,6 @@ - \ No newline at end of file From 48733a7e2f5c4d0d02666950e5d61fe04d435f25 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 29 Apr 2020 17:53:25 +0900 Subject: [PATCH 0538/1117] Change taiko hit explosion animation to match stable for skins --- .../Skinning/LegacyHitExplosion.cs | 10 ++++++++-- .../UI/DefaultHitExplosion.cs | 3 +++ osu.Game.Rulesets.Taiko/UI/HitExplosion.cs | 16 +++++++--------- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs b/osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs index d29b574866..42d4a34b9d 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs @@ -22,8 +22,14 @@ namespace osu.Game.Rulesets.Taiko.Skinning { base.LoadComplete(); - this.FadeIn(120); - this.ScaleTo(0.6f).Then().ScaleTo(1, 240, Easing.OutElastic); + const double animation_time = 120; + + this.FadeInFromZero(animation_time).Then().FadeOut(animation_time * 1.5); + + this.ScaleTo(0.6f) + .Then().ScaleTo(1.1f, animation_time * 0.8) + .Then().ScaleTo(0.9f, animation_time * 0.4) + .Then().ScaleTo(1f, animation_time * 0.2); } } } diff --git a/osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs index aa444d0494..a0ca5f1c39 100644 --- a/osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs @@ -49,6 +49,9 @@ namespace osu.Game.Rulesets.Taiko.UI base.LoadComplete(); this.ScaleTo(3f, 1000, Easing.OutQuint); + this.FadeOut(500); + + Expire(true); } } } diff --git a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs index 35a54d6ea7..f0585b9c50 100644 --- a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs @@ -23,6 +23,12 @@ namespace osu.Game.Rulesets.Taiko.UI [Cached(typeof(DrawableHitObject))] public readonly DrawableHitObject JudgedObject; + private SkinnableDrawable skinnable; + + public override double LifetimeStart => skinnable.Drawable.LifetimeStart; + + public override double LifetimeEnd => skinnable.Drawable.LifetimeEnd; + public HitExplosion(DrawableHitObject judgedObject) { JudgedObject = judgedObject; @@ -39,7 +45,7 @@ namespace osu.Game.Rulesets.Taiko.UI [BackgroundDependencyLoader] private void load() { - Child = new SkinnableDrawable(new TaikoSkinComponent(getComponentName(JudgedObject.Result?.Type ?? HitResult.Great)), _ => new DefaultHitExplosion()); + Child = skinnable = new SkinnableDrawable(new TaikoSkinComponent(getComponentName(JudgedObject.Result?.Type ?? HitResult.Great)), _ => new DefaultHitExplosion()); } private TaikoSkinComponents getComponentName(HitResult resultType) @@ -59,14 +65,6 @@ namespace osu.Game.Rulesets.Taiko.UI throw new ArgumentOutOfRangeException(nameof(resultType), "Invalid result type"); } - protected override void LoadComplete() - { - base.LoadComplete(); - - this.FadeOut(500); - Expire(true); - } - /// /// Transforms this hit explosion to visualise a secondary hit. /// From 511f7aeb28084553d2d60fb035aa4c296f856df8 Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Wed, 29 Apr 2020 10:55:39 +0200 Subject: [PATCH 0539/1117] Remove rider plugin config --- .idea/.idea.osu.Desktop/.idea/discord.xml | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 .idea/.idea.osu.Desktop/.idea/discord.xml diff --git a/.idea/.idea.osu.Desktop/.idea/discord.xml b/.idea/.idea.osu.Desktop/.idea/discord.xml deleted file mode 100644 index 59b11d1d39..0000000000 --- a/.idea/.idea.osu.Desktop/.idea/discord.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - \ No newline at end of file From 43e768240f8a1dcba8179efef6dcd09c8f150934 Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Wed, 29 Apr 2020 10:57:07 +0200 Subject: [PATCH 0540/1117] Revert "Revert project config file change" This reverts commit 4a101ca7151cbb1bc75bb0ce5143cc863e7aa265. --- .idea/.idea.osu.Desktop/.idea/projectSettingsUpdater.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.idea/.idea.osu.Desktop/.idea/projectSettingsUpdater.xml b/.idea/.idea.osu.Desktop/.idea/projectSettingsUpdater.xml index 7515e76054..4bb9f4d2a0 100644 --- a/.idea/.idea.osu.Desktop/.idea/projectSettingsUpdater.xml +++ b/.idea/.idea.osu.Desktop/.idea/projectSettingsUpdater.xml @@ -1,6 +1,6 @@ - \ No newline at end of file From 6e2ed0c4f3f4389e32e84d548262b5037daa015e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 29 Apr 2020 20:28:46 +0200 Subject: [PATCH 0541/1117] Refactor mascot to only contain state transitions --- .../Skinning/TestSceneDrawableTaikoMascot.cs | 47 ++++---- .../UI/DrawableTaikoMascot.cs | 109 +++++++----------- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 8 +- 3 files changed, 67 insertions(+), 97 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs index 2966c90b5e..f37c723a36 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; @@ -41,26 +42,25 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning [Test] public void TestStateTextures() { - AddStep("Set beatmap", () => setBeatmap()); + AddStep("set beatmap", () => setBeatmap()); - AddStep("Create mascot (idle)", () => + AddStep("create mascot", () => { SetContents(() => new TestDrawableTaikoMascot()); }); - AddStep("Clear state", () => setState(TaikoMascotAnimationState.Clear)); - - AddStep("Kiai state", () => setState(TaikoMascotAnimationState.Kiai)); - - AddStep("Fail state", () => setState(TaikoMascotAnimationState.Fail)); + AddStep("clear state", () => setState(TaikoMascotAnimationState.Clear)); + AddStep("kiai state", () => setState(TaikoMascotAnimationState.Kiai)); + AddStep("fail state", () => setState(TaikoMascotAnimationState.Fail)); + AddStep("idle state", () => setState(TaikoMascotAnimationState.Idle)); } [Test] public void TestPlayfield() { - AddStep("Set beatmap", () => setBeatmap()); + AddStep("set beatmap", () => setBeatmap()); - AddStep("Create ruleset", () => + AddStep("create drawable ruleset", () => { SetContents(() => { @@ -69,21 +69,21 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning }); }); - AddStep("Create hit (great)", () => addJudgement(HitResult.Miss)); - AddUntilStep("Wait for idle state", () => checkForState(TaikoMascotAnimationState.Fail)); + AddStep("new judgement (miss)", () => addJudgement(HitResult.Miss)); + AddUntilStep("wait for fail state", () => assertState(TaikoMascotAnimationState.Fail)); - AddStep("Create hit (great)", () => addJudgement(HitResult.Great)); - AddUntilStep("Wait for idle state", () => checkForState(TaikoMascotAnimationState.Idle)); + AddStep("new judgement (great)", () => addJudgement(HitResult.Great)); + AddUntilStep("wait for idle state", () => assertState(TaikoMascotAnimationState.Idle)); } [Test] public void TestKiai() { - AddStep("Set beatmap", () => setBeatmap(true)); + AddStep("set beatmap", () => setBeatmap(true)); - AddUntilStep("Wait for beatmap to be loaded", () => Beatmap.Value.Track.IsLoaded); + AddUntilStep("wait for beatmap to be loaded", () => Beatmap.Value.Track.IsLoaded); - AddStep("Create kiai ruleset", () => + AddStep("create drawable ruleset", () => { Beatmap.Value.Track.Start(); @@ -94,10 +94,10 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning }); }); - AddUntilStep("Wait for idle state", () => checkForState(TaikoMascotAnimationState.Fail)); + AddUntilStep("wait for fail state", () => assertState(TaikoMascotAnimationState.Fail)); - AddStep("Create hit (great)", () => addJudgement(HitResult.Great)); - AddUntilStep("Wait for kiai state", () => checkForState(TaikoMascotAnimationState.Kiai)); + AddStep("new judgement (great)", () => addJudgement(HitResult.Great)); + AddUntilStep("wait for kiai state", () => assertState(TaikoMascotAnimationState.Kiai)); } private void setBeatmap(bool kiai = false) @@ -129,7 +129,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning private void setState(TaikoMascotAnimationState state) { foreach (var mascot in mascots) - mascot?.ShowState(state); + mascot.State.Value = state; } private void addJudgement(HitResult result) @@ -143,7 +143,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning } } - private bool checkForState(TaikoMascotAnimationState state) => mascots.All(d => d.State == state); + private bool assertState(TaikoMascotAnimationState state) => mascots.All(d => d.State.Value == state); private class TestDrawableTaikoMascot : DrawableTaikoMascot { @@ -152,10 +152,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning { } - protected override TaikoMascotAnimationState GetFinalAnimationState(EffectControlPoint effectPoint, TaikoMascotAnimationState playfieldState) - { - return State; - } + public new Bindable State => base.State; } } } diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs index 7c4dfe2da7..be744de5f4 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs @@ -1,29 +1,36 @@ // 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.Audio.Track; +using osu.Framework.Bindables; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics.Containers; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Taiko.UI { public class DrawableTaikoMascot : BeatSyncedContainer { - private TaikoMascotTextureAnimation idleDrawable, clearDrawable, kiaiDrawable, failDrawable; - private EffectControlPoint lastEffectControlPoint; - private TaikoMascotAnimationState playfieldState; + protected Bindable State { get; } - public TaikoMascotAnimationState State { get; private set; } + private readonly Dictionary animations; + private Drawable currentAnimation; + + private bool lastHitMissed; + private bool kiaiMode; public DrawableTaikoMascot(TaikoMascotAnimationState startingState = TaikoMascotAnimationState.Idle) { RelativeSizeAxes = Axes.Both; - State = startingState; + State = new Bindable(startingState); + animations = new Dictionary(); } [BackgroundDependencyLoader] @@ -31,81 +38,53 @@ namespace osu.Game.Rulesets.Taiko.UI { InternalChildren = new[] { - idleDrawable = new TaikoMascotTextureAnimation(TaikoMascotAnimationState.Idle), - clearDrawable = new TaikoMascotTextureAnimation(TaikoMascotAnimationState.Clear), - kiaiDrawable = new TaikoMascotTextureAnimation(TaikoMascotAnimationState.Kiai), - failDrawable = new TaikoMascotTextureAnimation(TaikoMascotAnimationState.Fail), + animations[TaikoMascotAnimationState.Idle] = new TaikoMascotTextureAnimation(TaikoMascotAnimationState.Idle), + animations[TaikoMascotAnimationState.Clear] = new TaikoMascotTextureAnimation(TaikoMascotAnimationState.Clear), + animations[TaikoMascotAnimationState.Kiai] = new TaikoMascotTextureAnimation(TaikoMascotAnimationState.Kiai), + animations[TaikoMascotAnimationState.Fail] = new TaikoMascotTextureAnimation(TaikoMascotAnimationState.Fail), }; - ShowState(State); + updateState(); } - public void ShowState(TaikoMascotAnimationState state) + protected override void LoadComplete() { - foreach (var child in InternalChildren) - child.Hide(); + base.LoadComplete(); - State = state; - - var drawable = getStateDrawable(State); - drawable.Show(); + animations.Values.ForEach(animation => animation.Hide()); + State.BindValueChanged(mascotStateChanged, true); } - /// - /// Sets the playfield state used for determining the final state. - /// - /// - /// If you're looking to change the state manually, please look at . - /// - public void SetPlayfieldState(TaikoMascotAnimationState state) + public void OnNewResult(JudgementResult result) { - playfieldState = state; - - if (lastEffectControlPoint != null) - ShowState(GetFinalAnimationState(lastEffectControlPoint, playfieldState)); - } - - private TaikoMascotTextureAnimation getStateDrawable(TaikoMascotAnimationState state) - { - switch (state) - { - case TaikoMascotAnimationState.Idle: - return idleDrawable; - - case TaikoMascotAnimationState.Clear: - return clearDrawable; - - case TaikoMascotAnimationState.Kiai: - return kiaiDrawable; - - case TaikoMascotAnimationState.Fail: - return failDrawable; - - default: - throw new ArgumentOutOfRangeException(nameof(state), $"There's no animation available for state {state}"); - } - } - - protected virtual TaikoMascotAnimationState GetFinalAnimationState(EffectControlPoint effectPoint, TaikoMascotAnimationState playfieldState) - { - if (playfieldState == TaikoMascotAnimationState.Fail) - return playfieldState; - - return effectPoint.KiaiMode ? TaikoMascotAnimationState.Kiai : TaikoMascotAnimationState.Idle; + lastHitMissed = result.Type == HitResult.Miss && result.Judgement.AffectsCombo; + updateState(); } protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) { - base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); + kiaiMode = effectPoint.KiaiMode; + updateState(); + } - var state = GetFinalAnimationState(lastEffectControlPoint = effectPoint, playfieldState); - ShowState(state); + private void updateState() + { + State.Value = getNextState(); + } - if (state == TaikoMascotAnimationState.Clear) - return; + private TaikoMascotAnimationState getNextState() + { + if (lastHitMissed) + return TaikoMascotAnimationState.Fail; - var drawable = getStateDrawable(state); - drawable.Move(); + return kiaiMode ? TaikoMascotAnimationState.Kiai : TaikoMascotAnimationState.Idle; + } + + private void mascotStateChanged(ValueChangedEvent state) + { + currentAnimation?.Hide(); + currentAnimation = animations[state.NewValue]; + currentAnimation.Show(); } } } diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index c6e867a5d9..084a11d523 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -15,7 +15,6 @@ using osu.Game.Rulesets.Taiko.Objects.Drawables; using osu.Game.Rulesets.Taiko.Judgements; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Skinning; -using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Taiko.UI { @@ -216,12 +215,7 @@ namespace osu.Game.Rulesets.Taiko.UI if (mascotDrawable.Drawable is DrawableTaikoMascot mascot) { - var miss = result.Type == HitResult.Miss; - - if (miss && judgedObject.HitObject is StrongHitObject) - miss = result.Judgement.AffectsCombo; - - mascot.SetPlayfieldState(miss ? TaikoMascotAnimationState.Fail : TaikoMascotAnimationState.Idle); + mascot.OnNewResult(result); } } From e81d33dcec78ce816fc823214ea99a0e524c69da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 29 Apr 2020 21:27:02 +0200 Subject: [PATCH 0542/1117] Refactor mascot animations to split logic paths --- .../Skinning/TestSceneDrawableTaikoMascot.cs | 35 ++---- .../UI/DrawableTaikoMascot.cs | 19 +-- .../UI/TaikoMascotAnimation.cs | 116 ++++++++++++++++++ .../UI/TaikoMascotTextureAnimation.cs | 109 ---------------- 4 files changed, 133 insertions(+), 146 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs delete mode 100644 osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs index f37c723a36..28065c401c 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; @@ -27,6 +26,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(DrawableTaikoMascot), + typeof(TaikoMascotAnimation) }).ToList(); [Cached(typeof(IScrollingInfo))] @@ -36,23 +36,18 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning TimeRange = { Value = 5000 }, }; - private IEnumerable mascots => this.ChildrenOfType(); + private IEnumerable mascots => this.ChildrenOfType(); private IEnumerable playfields => this.ChildrenOfType(); [Test] - public void TestStateTextures() + public void TestStateAnimations() { AddStep("set beatmap", () => setBeatmap()); - AddStep("create mascot", () => - { - SetContents(() => new TestDrawableTaikoMascot()); - }); - - AddStep("clear state", () => setState(TaikoMascotAnimationState.Clear)); - AddStep("kiai state", () => setState(TaikoMascotAnimationState.Kiai)); - AddStep("fail state", () => setState(TaikoMascotAnimationState.Fail)); - AddStep("idle state", () => setState(TaikoMascotAnimationState.Idle)); + AddStep("clear state", () => SetContents(() => new TaikoMascotAnimation(TaikoMascotAnimationState.Clear))); + AddStep("idle state", () => SetContents(() => new TaikoMascotAnimation(TaikoMascotAnimationState.Idle))); + AddStep("kiai state", () => SetContents(() => new TaikoMascotAnimation(TaikoMascotAnimationState.Kiai))); + AddStep("fail state", () => SetContents(() => new TaikoMascotAnimation(TaikoMascotAnimationState.Fail))); } [Test] @@ -126,12 +121,6 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning }); } - private void setState(TaikoMascotAnimationState state) - { - foreach (var mascot in mascots) - mascot.State.Value = state; - } - private void addJudgement(HitResult result) { foreach (var playfield in playfields) @@ -144,15 +133,5 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning } private bool assertState(TaikoMascotAnimationState state) => mascots.All(d => d.State.Value == state); - - private class TestDrawableTaikoMascot : DrawableTaikoMascot - { - public TestDrawableTaikoMascot(TaikoMascotAnimationState startingState = TaikoMascotAnimationState.Idle) - : base(startingState) - { - } - - public new Bindable State => base.State; - } } } diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs index be744de5f4..bfc1d958c2 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs @@ -17,9 +17,10 @@ namespace osu.Game.Rulesets.Taiko.UI { public class DrawableTaikoMascot : BeatSyncedContainer { - protected Bindable State { get; } + public IBindable State => state; - private readonly Dictionary animations; + private readonly Bindable state; + private readonly Dictionary animations; private Drawable currentAnimation; private bool lastHitMissed; @@ -29,8 +30,8 @@ namespace osu.Game.Rulesets.Taiko.UI { RelativeSizeAxes = Axes.Both; - State = new Bindable(startingState); - animations = new Dictionary(); + state = new Bindable(startingState); + animations = new Dictionary(); } [BackgroundDependencyLoader] @@ -38,10 +39,10 @@ namespace osu.Game.Rulesets.Taiko.UI { InternalChildren = new[] { - animations[TaikoMascotAnimationState.Idle] = new TaikoMascotTextureAnimation(TaikoMascotAnimationState.Idle), - animations[TaikoMascotAnimationState.Clear] = new TaikoMascotTextureAnimation(TaikoMascotAnimationState.Clear), - animations[TaikoMascotAnimationState.Kiai] = new TaikoMascotTextureAnimation(TaikoMascotAnimationState.Kiai), - animations[TaikoMascotAnimationState.Fail] = new TaikoMascotTextureAnimation(TaikoMascotAnimationState.Fail), + animations[TaikoMascotAnimationState.Idle] = new TaikoMascotAnimation(TaikoMascotAnimationState.Idle), + animations[TaikoMascotAnimationState.Clear] = new TaikoMascotAnimation(TaikoMascotAnimationState.Clear), + animations[TaikoMascotAnimationState.Kiai] = new TaikoMascotAnimation(TaikoMascotAnimationState.Kiai), + animations[TaikoMascotAnimationState.Fail] = new TaikoMascotAnimation(TaikoMascotAnimationState.Fail), }; updateState(); @@ -69,7 +70,7 @@ namespace osu.Game.Rulesets.Taiko.UI private void updateState() { - State.Value = getNextState(); + state.Value = getNextState(); } private TaikoMascotAnimationState getNextState() diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs b/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs new file mode 100644 index 0000000000..1e289c1a74 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs @@ -0,0 +1,116 @@ +// 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.Audio.Track; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Animations; +using osu.Framework.Graphics.Textures; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Graphics.Containers; +using osu.Game.Skinning; + +namespace osu.Game.Rulesets.Taiko.UI +{ + public sealed class TaikoMascotAnimation : BeatSyncedContainer + { + private readonly TextureAnimation textureAnimation; + + private int currentFrame; + + public TaikoMascotAnimation(TaikoMascotAnimationState state) + { + InternalChild = textureAnimation = createTextureAnimation(state).With(animation => + { + animation.Origin = animation.Anchor = Anchor.BottomLeft; + RelativeSizeAxes = Axes.Both; + }); + + RelativeSizeAxes = Axes.Both; + Origin = Anchor = Anchor.BottomLeft; + } + + protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) + { + // assume that if the animation is playing on its own, it's independent from the beat and doesn't need to be touched. + if (textureAnimation.FrameCount == 0 || textureAnimation.IsPlaying) + return; + + textureAnimation.GotoFrame(currentFrame); + currentFrame = (currentFrame + 1) % textureAnimation.FrameCount; + } + + private static TextureAnimation createTextureAnimation(TaikoMascotAnimationState state) + { + switch (state) + { + case TaikoMascotAnimationState.Clear: + return new ClearMascotTextureAnimation(); + + case TaikoMascotAnimationState.Idle: + case TaikoMascotAnimationState.Kiai: + case TaikoMascotAnimationState.Fail: + return new ManualMascotTextureAnimation(state); + + default: + throw new ArgumentOutOfRangeException(nameof(state), $"Mascot animations for state {state} are not supported"); + } + } + + private class ManualMascotTextureAnimation : TextureAnimation + { + private readonly TaikoMascotAnimationState state; + + public ManualMascotTextureAnimation(TaikoMascotAnimationState state) + { + this.state = state; + + IsPlaying = false; + } + + [BackgroundDependencyLoader] + private void load(ISkinSource skin) + { + for (int frameIndex = 0; true; frameIndex++) + { + var texture = getAnimationFrame(skin, state, frameIndex); + + if (texture == null) + break; + + AddFrame(texture); + } + } + } + + private class ClearMascotTextureAnimation : TextureAnimation + { + private const float clear_animation_speed = 1000 / 10f; + + private static readonly int[] clear_animation_sequence = { 0, 1, 2, 3, 4, 5, 6, 5, 6, 5, 4, 3, 2, 1, 0 }; + + public ClearMascotTextureAnimation() + { + DefaultFrameLength = clear_animation_speed; + } + + [BackgroundDependencyLoader] + private void load(ISkinSource skin) + { + foreach (var frameIndex in clear_animation_sequence) + { + var texture = getAnimationFrame(skin, TaikoMascotAnimationState.Clear, frameIndex); + + if (texture == null) + continue; + + AddFrame(texture); + } + } + } + + private static Texture getAnimationFrame(ISkinSource skin, TaikoMascotAnimationState state, int frameIndex) + => skin.GetTexture($"pippidon{state.ToString().ToLower()}{frameIndex}"); + } +} diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs b/osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs deleted file mode 100644 index 080d30c3f2..0000000000 --- a/osu.Game.Rulesets.Taiko/UI/TaikoMascotTextureAnimation.cs +++ /dev/null @@ -1,109 +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; -using osu.Framework.Graphics.Animations; -using osu.Game.Skinning; - -namespace osu.Game.Rulesets.Taiko.UI -{ - public sealed class TaikoMascotTextureAnimation : TextureAnimation - { - private const float clear_animation_speed = 1000 / 10f; - private static readonly int[] clear_animation_sequence = { 0, 1, 2, 3, 4, 5, 6, 5, 6, 5, 4, 3, 2, 1, 0 }; - private int currentFrame; - - public TaikoMascotAnimationState State { get; } - - public TaikoMascotTextureAnimation(TaikoMascotAnimationState state) - : base(true) - { - State = state; - - // We're animating on beat if it's not the clear animation - if (state == TaikoMascotAnimationState.Clear) - DefaultFrameLength = clear_animation_speed; - else - this.Stop(); - - Origin = Anchor.BottomLeft; - Anchor = Anchor.BottomLeft; - } - - [BackgroundDependencyLoader] - private void load(ISkinSource skin) - { - if (State == TaikoMascotAnimationState.Clear) - { - foreach (var textureIndex in clear_animation_sequence) - { - if (!addFrame(skin, textureIndex)) - break; - } - } - else - { - for (int i = 0; true; i++) - { - if (!addFrame(skin, i)) - break; - } - } - } - - private bool addFrame(ISkinSource skin, int textureIndex) - { - var textureName = getStateTextureName(textureIndex); - var texture = skin.GetTexture(textureName); - - if (texture == null) - return false; - - AddFrame(texture); - - return true; - } - - /// - /// Advances the current frame by one. - /// - public void Move() - { - // Check whether there are frames before causing a crash. - if (FrameCount == 0) - return; - - if (currentFrame >= FrameCount) - currentFrame = 0; - - GotoFrame(currentFrame); - - currentFrame += 1; - } - - private string getStateTextureName(int i) => $"pippidon{getStateString(State)}{i}"; - - private string getStateString(TaikoMascotAnimationState state) - { - switch (state) - { - case TaikoMascotAnimationState.Clear: - return "clear"; - - case TaikoMascotAnimationState.Fail: - return "fail"; - - case TaikoMascotAnimationState.Idle: - return "idle"; - - case TaikoMascotAnimationState.Kiai: - return "kiai"; - - default: - throw new ArgumentOutOfRangeException(nameof(state), $"There's no case for animation state {state} available"); - } - } - } -} From 9d6720e7e6b11f327b56c6135c46bd6711f5580b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 29 Apr 2020 21:30:13 +0200 Subject: [PATCH 0543/1117] Scope up parameter --- osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs b/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs index 1e289c1a74..b9af8f0106 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs @@ -110,7 +110,7 @@ namespace osu.Game.Rulesets.Taiko.UI } } - private static Texture getAnimationFrame(ISkinSource skin, TaikoMascotAnimationState state, int frameIndex) + private static Texture getAnimationFrame(ISkin skin, TaikoMascotAnimationState state, int frameIndex) => skin.GetTexture($"pippidon{state.ToString().ToLower()}{frameIndex}"); } } From 47b040b7d8888283497858e4be2734bc2a6c5341 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 29 Apr 2020 21:42:28 +0200 Subject: [PATCH 0544/1117] Cover strong hit miss exemption in tests --- .../Skinning/TestSceneDrawableTaikoMascot.cs | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs index 28065c401c..81ea9c0755 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs @@ -64,11 +64,14 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning }); }); - AddStep("new judgement (miss)", () => addJudgement(HitResult.Miss)); - AddUntilStep("wait for fail state", () => assertState(TaikoMascotAnimationState.Fail)); + AddStep("miss result for normal hit", () => addJudgement(HitResult.Miss, new TaikoJudgement())); + AddUntilStep("state is fail", () => assertState(TaikoMascotAnimationState.Fail)); - AddStep("new judgement (great)", () => addJudgement(HitResult.Great)); - AddUntilStep("wait for idle state", () => assertState(TaikoMascotAnimationState.Idle)); + AddStep("great result for normal hit", () => addJudgement(HitResult.Great, new TaikoJudgement())); + AddUntilStep("state is idle", () => assertState(TaikoMascotAnimationState.Idle)); + + AddStep("miss result for strong hit", () => addJudgement(HitResult.Miss, new TaikoStrongJudgement())); + AddAssert("state remains idle", () => assertState(TaikoMascotAnimationState.Idle)); } [Test] @@ -89,10 +92,10 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning }); }); - AddUntilStep("wait for fail state", () => assertState(TaikoMascotAnimationState.Fail)); + AddUntilStep("state is fail", () => assertState(TaikoMascotAnimationState.Fail)); - AddStep("new judgement (great)", () => addJudgement(HitResult.Great)); - AddUntilStep("wait for kiai state", () => assertState(TaikoMascotAnimationState.Kiai)); + AddStep("great result for normal hit", () => addJudgement(HitResult.Great, new TaikoJudgement())); + AddUntilStep("state is kiai", () => assertState(TaikoMascotAnimationState.Kiai)); } private void setBeatmap(bool kiai = false) @@ -121,14 +124,14 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning }); } - private void addJudgement(HitResult result) + private void addJudgement(HitResult result, Judgement judgement) { foreach (var playfield in playfields) { var hit = new DrawableTestHit(new Hit(), result); Add(hit); - playfield.OnNewResult(hit, new JudgementResult(hit.HitObject, new TaikoJudgement()) { Type = result }); + playfield.OnNewResult(hit, new JudgementResult(hit.HitObject, judgement) { Type = result }); } } From 0d917ca339fbc6783587913a7190eab9733add91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 29 Apr 2020 21:52:09 +0200 Subject: [PATCH 0545/1117] Ensure correct behaviour for clear animation --- osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs b/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs index b9af8f0106..ee1389147d 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs @@ -103,7 +103,8 @@ namespace osu.Game.Rulesets.Taiko.UI var texture = getAnimationFrame(skin, TaikoMascotAnimationState.Clear, frameIndex); if (texture == null) - continue; + // as per https://osu.ppy.sh/help/wiki/Skinning/osu!taiko#pippidon + break; AddFrame(texture); } From b0e97793b6bea128746817734051055dcb484dfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 30 Apr 2020 00:14:27 +0200 Subject: [PATCH 0546/1117] Implement transitions into and from clear state --- .../Skinning/TestSceneDrawableTaikoMascot.cs | 40 ++++++++++++++++--- .../UI/DrawableTaikoMascot.cs | 16 ++++---- .../UI/TaikoMascotAnimation.cs | 10 +++++ 3 files changed, 53 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs index 81ea9c0755..0018899769 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs @@ -6,6 +6,8 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; @@ -50,6 +52,31 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning AddStep("fail state", () => SetContents(() => new TaikoMascotAnimation(TaikoMascotAnimationState.Fail))); } + [Test] + public void TestClearStateTransition() + { + AddStep("set beatmap", () => setBeatmap()); + + // the bindables need to be independent for each content cell to prevent interference, + // as if some of the skins don't implement the animation they'll immediately revert to the previous state from the clear state. + var states = new List>(); + + AddStep("create mascot", () => SetContents(() => + { + var state = new Bindable(TaikoMascotAnimationState.Clear); + states.Add(state); + return new DrawableTaikoMascot { State = { BindTarget = state } }; + })); + + AddStep("set clear state", () => states.ForEach(state => state.Value = TaikoMascotAnimationState.Clear)); + AddStep("miss", () => mascots.ForEach(mascot => mascot.OnNewResult(new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Miss }))); + AddAssert("skins with animations remain in clear state", () => mascots.Any(mascot => mascot.State.Value == TaikoMascotAnimationState.Clear)); + AddUntilStep("state reverts to fail", () => someMascotsIn(TaikoMascotAnimationState.Fail)); + + AddStep("set clear state again", () => states.ForEach(state => state.Value = TaikoMascotAnimationState.Clear)); + AddAssert("skins with animations change to clear", () => someMascotsIn(TaikoMascotAnimationState.Clear)); + } + [Test] public void TestPlayfield() { @@ -65,13 +92,13 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning }); AddStep("miss result for normal hit", () => addJudgement(HitResult.Miss, new TaikoJudgement())); - AddUntilStep("state is fail", () => assertState(TaikoMascotAnimationState.Fail)); + AddUntilStep("state is fail", () => allMascotsIn(TaikoMascotAnimationState.Fail)); AddStep("great result for normal hit", () => addJudgement(HitResult.Great, new TaikoJudgement())); - AddUntilStep("state is idle", () => assertState(TaikoMascotAnimationState.Idle)); + AddUntilStep("state is idle", () => allMascotsIn(TaikoMascotAnimationState.Idle)); AddStep("miss result for strong hit", () => addJudgement(HitResult.Miss, new TaikoStrongJudgement())); - AddAssert("state remains idle", () => assertState(TaikoMascotAnimationState.Idle)); + AddAssert("state remains idle", () => allMascotsIn(TaikoMascotAnimationState.Idle)); } [Test] @@ -92,10 +119,10 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning }); }); - AddUntilStep("state is fail", () => assertState(TaikoMascotAnimationState.Fail)); + AddUntilStep("state is fail", () => allMascotsIn(TaikoMascotAnimationState.Fail)); AddStep("great result for normal hit", () => addJudgement(HitResult.Great, new TaikoJudgement())); - AddUntilStep("state is kiai", () => assertState(TaikoMascotAnimationState.Kiai)); + AddUntilStep("state is kiai", () => allMascotsIn(TaikoMascotAnimationState.Kiai)); } private void setBeatmap(bool kiai = false) @@ -135,6 +162,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning } } - private bool assertState(TaikoMascotAnimationState state) => mascots.All(d => d.State.Value == state); + private bool allMascotsIn(TaikoMascotAnimationState state) => mascots.All(d => d.State.Value == state); + private bool someMascotsIn(TaikoMascotAnimationState state) => mascots.Any(d => d.State.Value == state); } } diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs index bfc1d958c2..f4bc841c15 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Taiko.UI private readonly Bindable state; private readonly Dictionary animations; - private Drawable currentAnimation; + private TaikoMascotAnimation currentAnimation; private bool lastHitMissed; private bool kiaiMode; @@ -44,8 +44,6 @@ namespace osu.Game.Rulesets.Taiko.UI animations[TaikoMascotAnimationState.Kiai] = new TaikoMascotAnimation(TaikoMascotAnimationState.Kiai), animations[TaikoMascotAnimationState.Fail] = new TaikoMascotAnimation(TaikoMascotAnimationState.Fail), }; - - updateState(); } protected override void LoadComplete() @@ -53,28 +51,32 @@ namespace osu.Game.Rulesets.Taiko.UI base.LoadComplete(); animations.Values.ForEach(animation => animation.Hide()); - State.BindValueChanged(mascotStateChanged, true); + state.BindValueChanged(mascotStateChanged, true); } public void OnNewResult(JudgementResult result) { lastHitMissed = result.Type == HitResult.Miss && result.Judgement.AffectsCombo; - updateState(); } protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) { kiaiMode = effectPoint.KiaiMode; - updateState(); } - private void updateState() + protected override void Update() { + base.Update(); state.Value = getNextState(); } private TaikoMascotAnimationState getNextState() { + // don't change state if current animation is playing + // (used for clear state - others are manually animated on new beats) + if (currentAnimation != null && !currentAnimation.Completed) + return state.Value; + if (lastHitMissed) return TaikoMascotAnimationState.Fail; diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs b/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs index ee1389147d..165e00cc73 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs @@ -29,6 +29,15 @@ namespace osu.Game.Rulesets.Taiko.UI RelativeSizeAxes = Axes.Both; Origin = Anchor = Anchor.BottomLeft; + AlwaysPresent = true; + } + + public bool Completed => !textureAnimation.IsPlaying || textureAnimation.PlaybackPosition >= textureAnimation.Duration; + + public override void Show() + { + base.Show(); + textureAnimation.Seek(0); } protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) @@ -93,6 +102,7 @@ namespace osu.Game.Rulesets.Taiko.UI public ClearMascotTextureAnimation() { DefaultFrameLength = clear_animation_speed; + Loop = false; } [BackgroundDependencyLoader] From 783dc58ef0f600073ef115f4e4868902f96473ea Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Apr 2020 09:41:56 +0900 Subject: [PATCH 0547/1117] Move taiko additive blending locally to avoid applying to legacy skins --- osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs | 2 ++ osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs | 2 ++ osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 3 --- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs b/osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs index 42d4a34b9d..c44da9ce1e 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs @@ -16,6 +16,8 @@ namespace osu.Game.Rulesets.Taiko.Skinning Origin = Anchor.Centre; AutoSizeAxes = Axes.Both; + + Blending = BlendingParameters.Additive; } protected override void LoadComplete() diff --git a/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs index 3a307bb3bb..067d390894 100644 --- a/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs @@ -31,6 +31,8 @@ namespace osu.Game.Rulesets.Taiko.UI RelativeSizeAxes = Axes.Both; Size = new Vector2(TaikoHitObject.DEFAULT_SIZE, 1); + Blending = BlendingParameters.Additive; + Masking = true; Alpha = 0.25f; diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 6a78c0a1fb..5c763cb332 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -68,7 +68,6 @@ namespace osu.Game.Rulesets.Taiko.UI hitExplosionContainer = new Container { RelativeSizeAxes = Axes.Both, - Blending = BlendingParameters.Additive, }, HitTarget = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.HitTarget), _ => new TaikoHitTarget()) { @@ -100,13 +99,11 @@ namespace osu.Game.Rulesets.Taiko.UI Name = "Kiai hit explosions", RelativeSizeAxes = Axes.Both, FillMode = FillMode.Fit, - Blending = BlendingParameters.Additive }, judgementContainer = new JudgementContainer { Name = "Judgements", RelativeSizeAxes = Axes.Y, - Blending = BlendingParameters.Additive }, } }, From 49a98fde7374055a8b2aa5286b733ec3673f55ad Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Apr 2020 09:57:14 +0900 Subject: [PATCH 0548/1117] Move to non-legacy class --- osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs | 2 -- osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs b/osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs index c44da9ce1e..42d4a34b9d 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs @@ -16,8 +16,6 @@ namespace osu.Game.Rulesets.Taiko.Skinning Origin = Anchor.Centre; AutoSizeAxes = Axes.Both; - - Blending = BlendingParameters.Additive; } protected override void LoadComplete() diff --git a/osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs index a0ca5f1c39..9943a58e3e 100644 --- a/osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs @@ -26,6 +26,8 @@ namespace osu.Game.Rulesets.Taiko.UI BorderColour = Color4.White; BorderThickness = 1; + Blending = BlendingParameters.Additive; + Alpha = 0.15f; Masking = true; From cf4e79cf38490ecb85f213b154dc7eef3ffb14ab Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Apr 2020 11:51:06 +0900 Subject: [PATCH 0549/1117] Show loading spinner when carousel is not ready to be displayed --- .../SongSelect/TestScenePlaySongSelect.cs | 1 + osu.Game/Screens/Select/SongSelect.cs | 33 +++++++++++-------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 39e04ed39a..aed8e19fb2 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -797,6 +797,7 @@ namespace osu.Game.Tests.Visual.SongSelect { AddStep("create song select", () => LoadScreen(songSelect = new TestSongSelect())); AddUntilStep("wait for present", () => songSelect.IsCurrentScreen()); + AddUntilStep("wait for carousel loaded", () => songSelect.Carousel.IsAlive); } private void addManyTestMaps() diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index c07465ca44..6b896694ea 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -34,6 +34,7 @@ using System.Linq; using System.Threading.Tasks; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; +using osu.Game.Graphics.UserInterface; using osu.Game.Scoring; namespace osu.Game.Screens.Select @@ -92,6 +93,8 @@ namespace osu.Game.Screens.Select private SampleChannel sampleChangeDifficulty; private SampleChannel sampleChangeBeatmap; + private Container carouselContainer; + protected BeatmapDetailArea BeatmapDetails { get; private set; } private readonly Bindable decoupledRuleset = new Bindable(); @@ -105,9 +108,22 @@ namespace osu.Game.Screens.Select // initial value transfer is required for FilterControl (it uses our re-cached bindables in its async load for the initial filter). transferRulesetValue(); + LoadComponentAsync(Carousel = new BeatmapCarousel + { + AllowSelection = false, // delay any selection until our bindables are ready to make a good choice. + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + RelativeSizeAxes = Axes.Both, + BleedTop = FilterControl.HEIGHT, + BleedBottom = Footer.HEIGHT, + SelectionChanged = updateSelectedBeatmap, + BeatmapSetsChanged = carouselBeatmapsLoaded, + GetRecommendedBeatmap = (recommender = new DifficultyRecommender()).GetRecommendedBeatmap, + }, c => carouselContainer.Child = c); + AddRangeInternal(new Drawable[] { - recommender = new DifficultyRecommender(), + recommender, new ResetScrollContainer(() => Carousel.ScrollToSelected()) { RelativeSizeAxes = Axes.Y, @@ -139,7 +155,7 @@ namespace osu.Game.Screens.Select Padding = new MarginPadding { Right = -150 }, }, }, - new Container + carouselContainer = new Container { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding @@ -147,18 +163,7 @@ namespace osu.Game.Screens.Select Top = FilterControl.HEIGHT, Bottom = Footer.HEIGHT }, - Child = Carousel = new BeatmapCarousel - { - AllowSelection = false, // delay any selection until our bindables are ready to make a good choice. - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - RelativeSizeAxes = Axes.Both, - BleedTop = FilterControl.HEIGHT, - BleedBottom = Footer.HEIGHT, - SelectionChanged = updateSelectedBeatmap, - BeatmapSetsChanged = carouselBeatmapsLoaded, - GetRecommendedBeatmap = recommender.GetRecommendedBeatmap, - }, + Child = new LoadingSpinner(true) { State = { Value = Visibility.Visible } } } }, } From 21c6ac8c43725f8739dcaea3a2b6105e7feab142 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Apr 2020 12:12:28 +0900 Subject: [PATCH 0550/1117] Allow closing the game during intro --- osu.Game/OsuGame.cs | 5 +---- osu.Game/Screens/Menu/IntroScreen.cs | 2 -- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index f5f7d0cef4..8e62819c95 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -914,10 +914,7 @@ namespace osu.Game if (ScreenStack.CurrentScreen is Loader) return false; - if (introScreen == null) - return true; - - if (!introScreen.DidLoadMenu || !(ScreenStack.CurrentScreen is IntroScreen)) + if (introScreen.DidLoadMenu && !(ScreenStack.CurrentScreen is IntroScreen)) { Scheduler.Add(introScreen.MakeCurrent); return true; diff --git a/osu.Game/Screens/Menu/IntroScreen.cs b/osu.Game/Screens/Menu/IntroScreen.cs index d2296573a6..736202ee52 100644 --- a/osu.Game/Screens/Menu/IntroScreen.cs +++ b/osu.Game/Screens/Menu/IntroScreen.cs @@ -96,8 +96,6 @@ namespace osu.Game.Screens.Menu Track = introBeatmap.Track; } - public override bool OnExiting(IScreen next) => !DidLoadMenu; - public override void OnResuming(IScreen last) { this.FadeIn(300); From 48af4d4eb4fc3ef8abf757ab512211defddee949 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Apr 2020 16:18:15 +0900 Subject: [PATCH 0551/1117] Fix skinned taiko hit explosions not being removed on rewind --- osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs b/osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs index 42d4a34b9d..b5ec2e8def 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs @@ -30,6 +30,8 @@ namespace osu.Game.Rulesets.Taiko.Skinning .Then().ScaleTo(1.1f, animation_time * 0.8) .Then().ScaleTo(0.9f, animation_time * 0.4) .Then().ScaleTo(1f, animation_time * 0.2); + + Expire(true); } } } From d0a8c0fa71bfe372a535380d258eb259b7f60dd4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Apr 2020 16:42:38 +0900 Subject: [PATCH 0552/1117] Add kiai support to osu!taiko skinned playfields --- .../Skinning/TestSceneTaikoPlayfield.cs | 18 ++++++ .../TaikoLegacyPlayfieldBackgroundRight.cs | 57 +++++++++++++++++++ .../Skinning/TaikoLegacySkinTransformer.cs | 9 +-- osu.Game/Beatmaps/WorkingBeatmap.cs | 4 +- osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs | 4 ++ 5 files changed, 82 insertions(+), 10 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko/Skinning/TaikoLegacyPlayfieldBackgroundRight.cs diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs index 16b3c036a3..ae5dd1e622 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs @@ -9,6 +9,7 @@ using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Testing; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Taiko.Beatmaps; using osu.Game.Rulesets.Taiko.Skinning; using osu.Game.Rulesets.Taiko.UI; using osu.Game.Rulesets.UI.Scrolling; @@ -34,6 +35,18 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning public TestSceneTaikoPlayfield() { + TaikoBeatmap beatmap; + bool kiai = false; + + AddStep("set beatmap", () => + { + Beatmap.Value = CreateWorkingBeatmap(beatmap = new TaikoBeatmap()); + + beatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = 1000 }); + + Beatmap.Value.Track.Start(); + }); + AddStep("Load playfield", () => SetContents(() => new TaikoPlayfield(new ControlPointInfo()) { Anchor = Anchor.CentreLeft, @@ -41,6 +54,11 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning })); AddRepeatStep("change height", () => this.ChildrenOfType().ForEach(p => p.Height = Math.Max(0.2f, (p.Height + 0.2f) % 1f)), 50); + + AddStep("Toggle kiai", () => + { + Beatmap.Value.Beatmap.ControlPointInfo.Add(0, new EffectControlPoint { KiaiMode = (kiai = !kiai) }); + }); } } } diff --git a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacyPlayfieldBackgroundRight.cs b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacyPlayfieldBackgroundRight.cs new file mode 100644 index 0000000000..7508c75231 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacyPlayfieldBackgroundRight.cs @@ -0,0 +1,57 @@ +// 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.Audio.Track; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Graphics.Containers; +using osu.Game.Skinning; +using osuTK; + +namespace osu.Game.Rulesets.Taiko.Skinning +{ + public class TaikoLegacyPlayfieldBackgroundRight : BeatSyncedContainer + { + private Sprite kiai; + + private bool kiaiDisplayed; + + [BackgroundDependencyLoader] + private void load(ISkinSource skin) + { + RelativeSizeAxes = Axes.Both; + + InternalChildren = new Drawable[] + { + new Sprite + { + Texture = skin.GetTexture("taiko-bar-right"), + RelativeSizeAxes = Axes.Both, + Size = Vector2.One, + }, + kiai = new Sprite + { + Texture = skin.GetTexture("taiko-bar-right-glow"), + RelativeSizeAxes = Axes.Both, + Size = Vector2.One, + Alpha = 0, + } + }; + } + + protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) + { + base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); + + if (effectPoint.KiaiMode != kiaiDisplayed) + { + kiaiDisplayed = effectPoint.KiaiMode; + + kiai.ClearTransforms(); + kiai.FadeTo(kiaiDisplayed ? 1 : 0, 200); + } + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs index f0df612e18..5dfc7ec0df 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs @@ -9,7 +9,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; using osu.Game.Audio; using osu.Game.Skinning; -using osuTK; namespace osu.Game.Rulesets.Taiko.Skinning { @@ -60,13 +59,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning case TaikoSkinComponents.PlayfieldBackgroundRight: if (GetTexture("taiko-bar-right") != null) - { - return this.GetAnimation("taiko-bar-right", false, false).With(d => - { - d.RelativeSizeAxes = Axes.Both; - d.Size = Vector2.One; - }); - } + return new TaikoLegacyPlayfieldBackgroundRight(); return null; diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index f30340956a..d2804bdc05 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -197,7 +197,7 @@ namespace osu.Game.Beatmaps public override string ToString() => BeatmapInfo.ToString(); - public bool BeatmapLoaded => beatmapLoadTask?.IsCompleted ?? false; + public virtual bool BeatmapLoaded => beatmapLoadTask?.IsCompleted ?? false; public IBeatmap Beatmap { @@ -233,7 +233,7 @@ namespace osu.Game.Beatmaps protected abstract Texture GetBackground(); private readonly RecyclableLazy background; - public bool TrackLoaded => track.IsResultAvailable; + public virtual bool TrackLoaded => track.IsResultAvailable; public Track Track => track.Value; protected abstract Track GetTrack(); private RecyclableLazy track; diff --git a/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs b/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs index 8f8afb87d4..cdf9170701 100644 --- a/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs +++ b/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs @@ -27,6 +27,10 @@ namespace osu.Game.Tests.Beatmaps this.storyboard = storyboard; } + public override bool TrackLoaded => true; + + public override bool BeatmapLoaded => true; + protected override IBeatmap GetBeatmap() => beatmap; protected override Storyboard GetStoryboard() => storyboard ?? base.GetStoryboard(); From 9bec42bc7ee8e59a1e9faebdc9388350259a7212 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Apr 2020 20:03:46 +0900 Subject: [PATCH 0553/1117] Fix mania crashing on undo/redo --- osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs b/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs index 17eba87076..45a0a5c485 100644 --- a/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs +++ b/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs @@ -84,7 +84,11 @@ namespace osu.Game.Screens.Edit { using (var stream = new MemoryStream(state)) using (var reader = new LineBufferedReader(stream, true)) - return new PassThroughWorkingBeatmap(Decoder.GetDecoder(reader).Decode(reader)).GetPlayableBeatmap(editorBeatmap.BeatmapInfo.Ruleset); + { + var decoded = Decoder.GetDecoder(reader).Decode(reader); + decoded.BeatmapInfo.Ruleset = editorBeatmap.BeatmapInfo.Ruleset; + return new PassThroughWorkingBeatmap(decoded).GetPlayableBeatmap(editorBeatmap.BeatmapInfo.Ruleset); + } } private class PassThroughWorkingBeatmap : WorkingBeatmap From c96bc5c51cdfe0f607ebd9925ab160358fb10a81 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Apr 2020 20:39:41 +0900 Subject: [PATCH 0554/1117] Fix undo/redo behaving poorly with simultaneous objects --- .../Editing/LegacyEditorBeatmapPatcherTest.cs | 25 +++++++++++++++++++ osu.Game/Screens/Edit/EditorBeatmap.cs | 20 ++++++++++++--- .../Edit/LegacyEditorBeatmapPatcher.cs | 6 +++-- 3 files changed, 45 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Editing/LegacyEditorBeatmapPatcherTest.cs b/osu.Game.Tests/Editing/LegacyEditorBeatmapPatcherTest.cs index a3ab677d96..ff17f23d50 100644 --- a/osu.Game.Tests/Editing/LegacyEditorBeatmapPatcherTest.cs +++ b/osu.Game.Tests/Editing/LegacyEditorBeatmapPatcherTest.cs @@ -304,6 +304,31 @@ namespace osu.Game.Tests.Editing runTest(patch); } + [Test] + public void TestChangeHitObjectAtSameTime() + { + current.AddRange(new[] + { + new HitCircle { StartTime = 500, Position = new Vector2(50) }, + new HitCircle { StartTime = 500, Position = new Vector2(100) }, + new HitCircle { StartTime = 500, Position = new Vector2(150) }, + new HitCircle { StartTime = 500, Position = new Vector2(200) }, + }); + + var patch = new OsuBeatmap + { + HitObjects = + { + new HitCircle { StartTime = 500, Position = new Vector2(150) }, + new HitCircle { StartTime = 500, Position = new Vector2(100) }, + new HitCircle { StartTime = 500, Position = new Vector2(50) }, + new HitCircle { StartTime = 500, Position = new Vector2(200) }, + } + }; + + runTest(patch); + } + private void runTest(IBeatmap patch) { // Due to the method of testing, "patch" comes in without having been decoded via a beatmap decoder. diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs index a2d2f08ce9..2e8e03bc73 100644 --- a/osu.Game/Screens/Edit/EditorBeatmap.cs +++ b/osu.Game/Screens/Edit/EditorBeatmap.cs @@ -136,14 +136,26 @@ namespace osu.Game.Screens.Edit /// The to add. public void Add(HitObject hitObject) { - trackStartTime(hitObject); - // Preserve existing sorting order in the beatmap var insertionIndex = findInsertionIndex(PlayableBeatmap.HitObjects, hitObject.StartTime); - mutableHitObjects.Insert(insertionIndex + 1, hitObject); + Insert(insertionIndex + 1, hitObject); + } + + /// + /// Inserts a into this . + /// + /// + /// It is the invoker's responsibility to make sure that sorting order is maintained. + /// + /// The index to insert the at. + /// The to insert. + public void Insert(int index, HitObject hitObject) + { + trackStartTime(hitObject); + + mutableHitObjects.Insert(index, hitObject); HitObjectAdded?.Invoke(hitObject); - updateHitObject(hitObject, true); } diff --git a/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs b/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs index 17eba87076..04faba6478 100644 --- a/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs +++ b/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs @@ -63,8 +63,10 @@ namespace osu.Game.Screens.Edit } } - // Make the removal indices are sorted so that iteration order doesn't get messed up post-removal. + // Sort the indices to ensure that removal + insertion indices don't get jumbled up post-removal or post-insertion. + // This isn't strictly required, but the differ makes no guarantees about order. toRemove.Sort(); + toAdd.Sort(); // Apply the changes. for (int i = toRemove.Count - 1; i >= 0; i--) @@ -74,7 +76,7 @@ namespace osu.Game.Screens.Edit { IBeatmap newBeatmap = readBeatmap(newState); foreach (var i in toAdd) - editorBeatmap.Add(newBeatmap.HitObjects[i]); + editorBeatmap.Insert(i, newBeatmap.HitObjects[i]); } } From 000c34dc26ea9d815f0f76c32510f1a0b5f600a7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Apr 2020 21:01:53 +0900 Subject: [PATCH 0555/1117] Move recommender to field construction --- osu.Game/Screens/Select/SongSelect.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 6b896694ea..a7e27c27ba 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -78,7 +78,7 @@ namespace osu.Game.Screens.Select protected BeatmapCarousel Carousel { get; private set; } - private DifficultyRecommender recommender; + private readonly DifficultyRecommender recommender = new DifficultyRecommender(); private BeatmapInfoWedge beatmapInfoWedge; private DialogOverlay dialogOverlay; @@ -118,7 +118,7 @@ namespace osu.Game.Screens.Select BleedBottom = Footer.HEIGHT, SelectionChanged = updateSelectedBeatmap, BeatmapSetsChanged = carouselBeatmapsLoaded, - GetRecommendedBeatmap = (recommender = new DifficultyRecommender()).GetRecommendedBeatmap, + GetRecommendedBeatmap = recommender.GetRecommendedBeatmap, }, c => carouselContainer.Child = c); AddRangeInternal(new Drawable[] From 99677ac17149bff748d96954bae2e8ab0fe6bafe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 30 Apr 2020 21:41:57 +0200 Subject: [PATCH 0556/1117] Expand test coverage of state transitions --- .../Skinning/TestSceneDrawableTaikoMascot.cs | 123 ++++++++++++------ 1 file changed, 86 insertions(+), 37 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs index 0018899769..cdd2a38e19 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Humanizer; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -16,6 +17,7 @@ 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; using osu.Game.Rulesets.Taiko.UI; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Tests.Visual; @@ -38,9 +40,17 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning TimeRange = { Value = 5000 }, }; + private TaikoScoreProcessor scoreProcessor; + private IEnumerable mascots => this.ChildrenOfType(); private IEnumerable playfields => this.ChildrenOfType(); + [SetUp] + public void SetUp() + { + scoreProcessor = new TaikoScoreProcessor(); + } + [Test] public void TestStateAnimations() { @@ -78,51 +88,63 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning } [Test] - public void TestPlayfield() + public void TestIdleState() { AddStep("set beatmap", () => setBeatmap()); - AddStep("create drawable ruleset", () => - { - SetContents(() => - { - var ruleset = new TaikoRuleset(); - return new DrawableTaikoRuleset(ruleset, Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo)); - }); - }); + createDrawableRuleset(); - AddStep("miss result for normal hit", () => addJudgement(HitResult.Miss, new TaikoJudgement())); - AddUntilStep("state is fail", () => allMascotsIn(TaikoMascotAnimationState.Fail)); - - AddStep("great result for normal hit", () => addJudgement(HitResult.Great, new TaikoJudgement())); - AddUntilStep("state is idle", () => allMascotsIn(TaikoMascotAnimationState.Idle)); - - AddStep("miss result for strong hit", () => addJudgement(HitResult.Miss, new TaikoStrongJudgement())); - AddAssert("state remains idle", () => allMascotsIn(TaikoMascotAnimationState.Idle)); + assertStateAfterResult(new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Great }, TaikoMascotAnimationState.Idle); + assertStateAfterResult(new JudgementResult(new StrongHitObject(), new TaikoStrongJudgement()) { Type = HitResult.Miss }, TaikoMascotAnimationState.Idle); } [Test] - public void TestKiai() + public void TestKiaiState() { AddStep("set beatmap", () => setBeatmap(true)); - AddUntilStep("wait for beatmap to be loaded", () => Beatmap.Value.Track.IsLoaded); + createDrawableRuleset(); - AddStep("create drawable ruleset", () => - { - Beatmap.Value.Track.Start(); + assertStateAfterResult(new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Good }, TaikoMascotAnimationState.Kiai); + assertStateAfterResult(new JudgementResult(new Hit(), new TaikoStrongJudgement()) { Type = HitResult.Miss }, TaikoMascotAnimationState.Kiai); + assertStateAfterResult(new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Miss }, TaikoMascotAnimationState.Fail); + } - SetContents(() => - { - var ruleset = new TaikoRuleset(); - return new DrawableTaikoRuleset(ruleset, Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo)); - }); - }); + [Test] + public void TestMissState() + { + AddStep("set beatmap", () => setBeatmap()); - AddUntilStep("state is fail", () => allMascotsIn(TaikoMascotAnimationState.Fail)); + createDrawableRuleset(); - AddStep("great result for normal hit", () => addJudgement(HitResult.Great, new TaikoJudgement())); - AddUntilStep("state is kiai", () => allMascotsIn(TaikoMascotAnimationState.Kiai)); + assertStateAfterResult(new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Great }, TaikoMascotAnimationState.Idle); + assertStateAfterResult(new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Miss }, TaikoMascotAnimationState.Fail); + assertStateAfterResult(new JudgementResult(new DrumRoll(), new TaikoDrumRollJudgement()) { Type = HitResult.Great }, TaikoMascotAnimationState.Fail); + assertStateAfterResult(new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Good }, TaikoMascotAnimationState.Idle); + } + + [TestCase(true)] + [TestCase(false)] + public void TestClearStateOnComboMilestone(bool kiai) + { + AddStep("set beatmap", () => setBeatmap(kiai)); + + createDrawableRuleset(); + + AddRepeatStep("reach 49 combo", () => applyNewResult(new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Great }), 49); + + assertStateAfterResult(new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Good }, TaikoMascotAnimationState.Clear); + } + + [TestCase(true)] + [TestCase(false)] + public void TestClearStateOnClearedSwell(bool kiai) + { + AddStep("set beatmap", () => setBeatmap(kiai)); + + createDrawableRuleset(); + + assertStateAfterResult(new JudgementResult(new Swell(), new TaikoSwellJudgement()) { Type = HitResult.Great }, TaikoMascotAnimationState.Clear); } private void setBeatmap(bool kiai = false) @@ -141,24 +163,51 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning BaseDifficulty = new BeatmapDifficulty(), Metadata = new BeatmapMetadata { - Artist = @"Unknown", - Title = @"Sample Beatmap", - AuthorString = @"Craftplacer", + Artist = "Unknown", + Title = "Sample Beatmap", + AuthorString = "Craftplacer", }, Ruleset = new TaikoRuleset().RulesetInfo }, ControlPointInfo = controlPointInfo }); + + scoreProcessor.ApplyBeatmap(Beatmap.Value.Beatmap); } - private void addJudgement(HitResult result, Judgement judgement) + private void createDrawableRuleset() + { + AddUntilStep("wait for beatmap to be loaded", () => Beatmap.Value.Track.IsLoaded); + + AddStep("create drawable ruleset", () => + { + Beatmap.Value.Track.Start(); + + SetContents(() => + { + var ruleset = new TaikoRuleset(); + return new DrawableTaikoRuleset(ruleset, Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo)); + }); + }); + } + + private void assertStateAfterResult(JudgementResult judgementResult, TaikoMascotAnimationState expectedState) + { + AddStep($"{judgementResult.Type.ToString().ToLower()} result for {judgementResult.Judgement.GetType().Name.Humanize(LetterCasing.LowerCase)}", + () => applyNewResult(judgementResult)); + + AddAssert($"state is {expectedState.ToString().ToLower()}", () => allMascotsIn(expectedState)); + } + + private void applyNewResult(JudgementResult judgementResult) { foreach (var playfield in playfields) { - var hit = new DrawableTestHit(new Hit(), result); + var hit = new DrawableTestHit(new Hit(), judgementResult.Type); Add(hit); - playfield.OnNewResult(hit, new JudgementResult(hit.HitObject, judgement) { Type = result }); + playfield.OnNewResult(hit, judgementResult); + scoreProcessor.ApplyResult(judgementResult); } } From 22fde8d2a0e7ac158358af6839f91c528853d07e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 30 Apr 2020 21:58:05 +0200 Subject: [PATCH 0557/1117] Implement partial clear transition logic --- .../Skinning/TestSceneDrawableTaikoMascot.cs | 3 ++- .../UI/DrawableTaikoMascot.cs | 25 ++++++++++++++++--- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs index cdd2a38e19..f74de47425 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs @@ -201,13 +201,14 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning private void applyNewResult(JudgementResult judgementResult) { + scoreProcessor.ApplyResult(judgementResult); + foreach (var playfield in playfields) { var hit = new DrawableTestHit(new Hit(), judgementResult.Type); Add(hit); playfield.OnNewResult(hit, judgementResult); - scoreProcessor.ApplyResult(judgementResult); } } diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs index f4bc841c15..2df0cae2e3 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs @@ -11,7 +11,7 @@ using osu.Framework.Graphics.Textures; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics.Containers; using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.Taiko.Judgements; namespace osu.Game.Rulesets.Taiko.UI { @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Taiko.UI private readonly Dictionary animations; private TaikoMascotAnimation currentAnimation; - private bool lastHitMissed; + private bool lastObjectHit; private bool kiaiMode; public DrawableTaikoMascot(TaikoMascotAnimationState startingState = TaikoMascotAnimationState.Idle) @@ -56,7 +56,18 @@ namespace osu.Game.Rulesets.Taiko.UI public void OnNewResult(JudgementResult result) { - lastHitMissed = result.Type == HitResult.Miss && result.Judgement.AffectsCombo; + // TODO: missing support for clear/fail state transition at end of beatmap gameplay + + if (triggerComboClear(result) || triggerSwellClear(result)) + { + state.Value = TaikoMascotAnimationState.Clear; + return; + } + + if (!result.Judgement.AffectsCombo) + return; + + lastObjectHit = result.IsHit; } protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) @@ -77,7 +88,7 @@ namespace osu.Game.Rulesets.Taiko.UI if (currentAnimation != null && !currentAnimation.Completed) return state.Value; - if (lastHitMissed) + if (!lastObjectHit) return TaikoMascotAnimationState.Fail; return kiaiMode ? TaikoMascotAnimationState.Kiai : TaikoMascotAnimationState.Idle; @@ -89,5 +100,11 @@ namespace osu.Game.Rulesets.Taiko.UI currentAnimation = animations[state.NewValue]; currentAnimation.Show(); } + + private bool triggerComboClear(JudgementResult judgementResult) + => (judgementResult.ComboAtJudgement + 1) % 50 == 0 && judgementResult.Judgement.AffectsCombo && judgementResult.IsHit; + + private bool triggerSwellClear(JudgementResult judgementResult) + => judgementResult.Judgement is TaikoSwellJudgement && judgementResult.IsHit; } } From 5cfc05e12afe54aca75b6c92ba91e3d0ad35a277 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 30 Apr 2020 22:03:39 +0200 Subject: [PATCH 0558/1117] Ensure correct initial state --- .../Skinning/TestSceneDrawableTaikoMascot.cs | 8 ++++++++ osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs index f74de47425..e23f63e245 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs @@ -62,6 +62,14 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning AddStep("fail state", () => SetContents(() => new TaikoMascotAnimation(TaikoMascotAnimationState.Fail))); } + [Test] + public void TestInitialState() + { + AddStep("create mascot", () => SetContents(() => new DrawableTaikoMascot())); + + AddAssert("mascot initially idle", () => allMascotsIn(TaikoMascotAnimationState.Idle)); + } + [Test] public void TestClearStateTransition() { diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs index 2df0cae2e3..1b7d011d8a 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Taiko.UI private readonly Dictionary animations; private TaikoMascotAnimation currentAnimation; - private bool lastObjectHit; + private bool lastObjectHit = true; private bool kiaiMode; public DrawableTaikoMascot(TaikoMascotAnimationState startingState = TaikoMascotAnimationState.Idle) From db4c8b2ba59c6a59ab4a19eec2d548f28022242e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 30 Apr 2020 22:16:25 +0200 Subject: [PATCH 0559/1117] Fix transition out of clear state --- .../Skinning/TestSceneDrawableTaikoMascot.cs | 7 ++++--- osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs index e23f63e245..aee057602a 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs @@ -144,15 +144,16 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning assertStateAfterResult(new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Good }, TaikoMascotAnimationState.Clear); } - [TestCase(true)] - [TestCase(false)] - public void TestClearStateOnClearedSwell(bool kiai) + [TestCase(true, TaikoMascotAnimationState.Kiai)] + [TestCase(false, TaikoMascotAnimationState.Idle)] + public void TestClearStateOnClearedSwell(bool kiai, TaikoMascotAnimationState expectedStateAfterClear) { AddStep("set beatmap", () => setBeatmap(kiai)); createDrawableRuleset(); assertStateAfterResult(new JudgementResult(new Swell(), new TaikoSwellJudgement()) { Type = HitResult.Great }, TaikoMascotAnimationState.Clear); + AddUntilStep($"state reverts to {expectedStateAfterClear.ToString().ToLower()}", () => allMascotsIn(expectedStateAfterClear)); } private void setBeatmap(bool kiai = false) diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs index 1b7d011d8a..089f5c87a2 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs @@ -61,7 +61,8 @@ namespace osu.Game.Rulesets.Taiko.UI if (triggerComboClear(result) || triggerSwellClear(result)) { state.Value = TaikoMascotAnimationState.Clear; - return; + // never play fail immediately after clear. + lastObjectHit = true; } if (!result.Judgement.AffectsCombo) From f5526890cc3ffe85b45861fc3f46c1d24df8db05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 30 Apr 2020 22:51:22 +0200 Subject: [PATCH 0560/1117] Add comment about animation presence --- osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs b/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs index 165e00cc73..452272211d 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs @@ -24,11 +24,12 @@ namespace osu.Game.Rulesets.Taiko.UI InternalChild = textureAnimation = createTextureAnimation(state).With(animation => { animation.Origin = animation.Anchor = Anchor.BottomLeft; - RelativeSizeAxes = Axes.Both; }); RelativeSizeAxes = Axes.Both; Origin = Anchor = Anchor.BottomLeft; + + // needs to be always present to prevent the animation clock consuming time spent when not present. AlwaysPresent = true; } From 1e7b10320f8ba2cd7148f2734f216495f38846d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 1 May 2020 00:19:12 +0200 Subject: [PATCH 0561/1117] Adjust mascot positioning in playfield layout --- osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs | 2 +- .../UI/TaikoMascotAnimation.cs | 2 ++ osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 15 +++++++++------ 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs index 089f5c87a2..eb885872c5 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Taiko.UI public DrawableTaikoMascot(TaikoMascotAnimationState startingState = TaikoMascotAnimationState.Idle) { - RelativeSizeAxes = Axes.Both; + Origin = Anchor = Anchor.BottomLeft; state = new Bindable(startingState); animations = new Dictionary(); diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs b/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs index 452272211d..0bf6bc7d49 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Textures; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics.Containers; using osu.Game.Skinning; +using osuTK; namespace osu.Game.Rulesets.Taiko.UI { @@ -24,6 +25,7 @@ namespace osu.Game.Rulesets.Taiko.UI InternalChild = textureAnimation = createTextureAnimation(state).With(animation => { animation.Origin = animation.Anchor = Anchor.BottomLeft; + animation.Scale = new Vector2(0.6f); }); RelativeSizeAxes = Axes.Both; diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 084a11d523..2edc697d66 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -130,18 +130,21 @@ namespace osu.Game.Rulesets.Taiko.UI }, } }, + mascotDrawable = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.TaikoDon), _ => Empty()) + { + Origin = Anchor.BottomLeft, + Anchor = Anchor.TopLeft, + RelativePositionAxes = Axes.None, + RelativeSizeAxes = Axes.None, + X = 15, + Y = 45 + }, topLevelHitContainer = new ProxyContainer { Name = "Top level hit objects", RelativeSizeAxes = Axes.Both, }, drumRollHitContainer.CreateProxy(), - mascotDrawable = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.TaikoDon), _ => new Container(), confineMode: ConfineMode.ScaleToFit) - { - Origin = Anchor.BottomLeft, - Anchor = Anchor.TopLeft, - RelativePositionAxes = Axes.None - } }; } From 05183c6e6fb1683bd6fa94021c7e69c2572cb8e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 1 May 2020 00:24:39 +0200 Subject: [PATCH 0562/1117] Final test touch-ups --- .../Skinning/TestSceneDrawableTaikoMascot.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs index aee057602a..492f628482 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs @@ -9,6 +9,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Graphics; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; @@ -65,7 +66,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning [Test] public void TestInitialState() { - AddStep("create mascot", () => SetContents(() => new DrawableTaikoMascot())); + AddStep("create mascot", () => SetContents(() => new DrawableTaikoMascot { RelativeSizeAxes = Axes.Both })); AddAssert("mascot initially idle", () => allMascotsIn(TaikoMascotAnimationState.Idle)); } @@ -83,13 +84,13 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning { var state = new Bindable(TaikoMascotAnimationState.Clear); states.Add(state); - return new DrawableTaikoMascot { State = { BindTarget = state } }; + return new DrawableTaikoMascot { State = { BindTarget = state }, RelativeSizeAxes = Axes.Both }; })); AddStep("set clear state", () => states.ForEach(state => state.Value = TaikoMascotAnimationState.Clear)); AddStep("miss", () => mascots.ForEach(mascot => mascot.OnNewResult(new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Miss }))); - AddAssert("skins with animations remain in clear state", () => mascots.Any(mascot => mascot.State.Value == TaikoMascotAnimationState.Clear)); - AddUntilStep("state reverts to fail", () => someMascotsIn(TaikoMascotAnimationState.Fail)); + AddAssert("skins with animations remain in clear state", () => someMascotsIn(TaikoMascotAnimationState.Clear)); + AddUntilStep("state reverts to fail", () => allMascotsIn(TaikoMascotAnimationState.Fail)); AddStep("set clear state again", () => states.ForEach(state => state.Value = TaikoMascotAnimationState.Clear)); AddAssert("skins with animations change to clear", () => someMascotsIn(TaikoMascotAnimationState.Clear)); From d021e213b216b994c343184d400c5aa7bfa25fd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 1 May 2020 00:29:03 +0200 Subject: [PATCH 0563/1117] Reword comment --- osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs index eb885872c5..9328b607e6 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs @@ -61,7 +61,7 @@ namespace osu.Game.Rulesets.Taiko.UI if (triggerComboClear(result) || triggerSwellClear(result)) { state.Value = TaikoMascotAnimationState.Clear; - // never play fail immediately after clear. + // always consider a clear equivalent to a hit to avoid clear -> miss transitions lastObjectHit = true; } From e0ae9f791c357a040f0ff06180451bf20dca2428 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 1 May 2020 11:56:40 +0900 Subject: [PATCH 0564/1117] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 73fbe3ab2e..336479c40a 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 3b05eb82d7..acb7fe5fbe 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -23,7 +23,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index f3202693f3..6662e57dcd 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -80,7 +80,7 @@ - + From 700214d249d2938977c6161e4dcea22b712b1025 Mon Sep 17 00:00:00 2001 From: Joehu Date: Thu, 30 Apr 2020 22:13:38 -0700 Subject: [PATCH 0565/1117] Truncate beatmap title and artist on score panel --- .../Ranking/TestSceneExpandedPanelMiddleContent.cs | 5 ++++- .../Ranking/Expanded/ExpandedPanelMiddleContent.cs | 10 ++++++++-- osu.Game/Screens/Ranking/ScorePanel.cs | 4 ++-- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs index 52d8ea0480..328a0e0c27 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs @@ -20,6 +20,7 @@ using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; +using osu.Game.Screens.Ranking; using osu.Game.Screens.Ranking.Expanded; using osu.Game.Screens.Ranking.Expanded.Accuracy; using osu.Game.Screens.Ranking.Expanded.Statistics; @@ -74,6 +75,8 @@ namespace osu.Game.Tests.Visual.Ranking { var beatmap = new TestBeatmap(rulesetStore.GetRuleset(0)); beatmap.Metadata.Author = author; + beatmap.Metadata.Title = "Verrrrrrrrrrrrrrrrrrry looooooooooooooooooooooooong beatmap title"; + beatmap.Metadata.Artist = "Verrrrrrrrrrrrrrrrrrry looooooooooooooooooooooooong beatmap artist"; return new TestWorkingBeatmap(beatmap); } @@ -114,7 +117,7 @@ namespace osu.Game.Tests.Visual.Ranking Anchor = Anchor.Centre; Origin = Anchor.Centre; - Size = new Vector2(500, 700); + Size = new Vector2(ScorePanel.EXPANDED_WIDTH, 700); Children = new Drawable[] { new Box diff --git a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs index b058cc142b..fd8ac33aef 100644 --- a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs @@ -35,6 +35,8 @@ namespace osu.Game.Screens.Ranking.Expanded private RollingCounter scoreCounter; + private const float padding = 10; + /// /// Creates a new . /// @@ -46,7 +48,7 @@ namespace osu.Game.Screens.Ranking.Expanded RelativeSizeAxes = Axes.Both; Masking = true; - Padding = new MarginPadding { Vertical = 10, Horizontal = 10 }; + Padding = new MarginPadding(padding); } [BackgroundDependencyLoader] @@ -92,13 +94,17 @@ namespace osu.Game.Screens.Ranking.Expanded Origin = Anchor.TopCentre, Text = new LocalisedString((metadata.TitleUnicode, metadata.Title)), Font = OsuFont.Torus.With(size: 20, weight: FontWeight.SemiBold), + MaxWidth = ScorePanel.EXPANDED_WIDTH - padding * 2, + Truncate = true, }, new OsuSpriteText { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Text = new LocalisedString((metadata.ArtistUnicode, metadata.Artist)), - Font = OsuFont.Torus.With(size: 14, weight: FontWeight.SemiBold) + Font = OsuFont.Torus.With(size: 14, weight: FontWeight.SemiBold), + MaxWidth = ScorePanel.EXPANDED_WIDTH - padding * 2, + Truncate = true, }, new Container { diff --git a/osu.Game/Screens/Ranking/ScorePanel.cs b/osu.Game/Screens/Ranking/ScorePanel.cs index a1adfcc500..c055df7ccc 100644 --- a/osu.Game/Screens/Ranking/ScorePanel.cs +++ b/osu.Game/Screens/Ranking/ScorePanel.cs @@ -31,7 +31,7 @@ namespace osu.Game.Screens.Ranking /// /// Width of the panel when expanded. /// - private const float expanded_width = 360; + public const float EXPANDED_WIDTH = 360; /// /// Height of the panel when expanded. @@ -183,7 +183,7 @@ namespace osu.Game.Screens.Ranking switch (state) { case PanelState.Expanded: - this.ResizeTo(new Vector2(expanded_width, expanded_height), resize_duration, Easing.OutQuint); + this.ResizeTo(new Vector2(EXPANDED_WIDTH, expanded_height), resize_duration, Easing.OutQuint); topLayerBackground.FadeColour(expanded_top_layer_colour, resize_duration, Easing.OutQuint); middleLayerBackground.FadeColour(expanded_middle_layer_colour, resize_duration, Easing.OutQuint); From 8955b98cbb5be1a6768b97ad4b3ef7390b81bef6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Apr 2020 17:38:37 +0900 Subject: [PATCH 0566/1117] Add basic taiko scroller implementation --- .../old-skin/taiko-slider-fail@2x.png | Bin 0 -> 141234 bytes .../Resources/old-skin/taiko-slider@2x.png | Bin 0 -> 127176 bytes .../Skinning/TestSceneTaikoPlayfield.cs | 2 + .../Skinning/TestSceneTaikoScroller.cs | 16 ++++ .../Skinning/LegacyTaikoScroller.cs | 83 ++++++++++++++++++ .../Skinning/TaikoLegacySkinTransformer.cs | 6 ++ .../TaikoSkinComponents.cs | 1 + osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 7 ++ 8 files changed, 115 insertions(+) create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/taiko-slider-fail@2x.png create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/taiko-slider@2x.png create mode 100644 osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.cs create mode 100644 osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/taiko-slider-fail@2x.png b/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/taiko-slider-fail@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..ef597210f1c9e6e805b9d3f4399b70e057b51866 GIT binary patch literal 141234 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⩔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%0DbICSRi1->iw)J4JfN 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 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/taiko-slider@2x.png b/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/taiko-slider@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..05b8c5783f4bde04d74aab93db8c17f5e73c5060 GIT binary patch literal 127176 zcmZ5{1ymf}vhEn}?(XhELa^ZOE<@1Z?vTMPL4pT@6WrYg4GzJBhu|JG_~Sq4ym!yL zwR%>q>FMdNs$EsRtM<2})m7y%P)Sh%004%9ytF0&fQStM0Hu);U(O72dU(Guh>fI* zBmhtwkM>}W@baD7LS9n^0Ptf30AOJNz}?F!*d74j$qfJ;Kmh=ebO3^7FZ_jW-?o2vVJI&02JQd=8ty+I;Ph(H!^__R3eu9=K1)ZB-W?pRo|LFTC7n0J zw<}q%<-F>Lt@)yoiDeXNSrSv3*%s@p-uR0KT?^gsk{sUf^Id<;*wo%bEf;(SKjlga!)6{x{XAU`K^~ z1upxe_e)9q55aS3u#tiPCUBGl;gOj7sb~V~f6n}iJxWt!|L3N6Q54Gfm-j!s!cN}& z?~CeBla^usCX+Bnb{~i4{4|c!rXQo3SWS+ol>ZUqA69dW&HjJy*cRqk=(;W|b4(g= z0HyZ4>QSWmkJ|sRTqD~El8Y0d#ZSvG7iVkWf5bQ#gvE1!^*U71W|0_L%ZI>4^;o+9 zpTM5f`qW1fCDM`^Lc!YqG7xhlkV4Q;w<1XV8HR)1w22;eV#={1fzSM3&w?6JV2ye0pf&RQXl*|Et&0miA2;Mk^j~V9w9;P@D?8jNhca1h8&!%DS5p)dNa7`$eST)7#hM#fX}D)nQW9|E<0kRR}^atk2p28nL^X zqQU5*YDm0Tfk});|1Wi}rE~61f@#t16zvlRd&;a>R77X@s)oMoc+ueh$HM4ddeBh>#SkU{KT-xn&k6K zMEAr+qcCK-l&Pi~`fn3`jg_EAXO3YJY+x2K^`*sCF)@h3#gA zYc}^O*lo<$X(E%AC?n6-Qt}TmHaD}nLg;j0d*wKi1Ks7{uUDVAOI$wu&29%9b&5oX zi^zw^DBfisUW5PbU)bCw%=H_rW1s#R!PhUg1V~W~mHd`!mZ@?<-GL72lQLGKYukrH zqp`8M^_lG*)nAQIbs>=~2lMaIm~*?V+jWo5Fck@0Q7R^)T8)^5{*TyRq9Pf5KTj|& z3Sp~&oF=@b-U&HsY((_6Ey*82NIHme<8{=hBSSK@FYa+kDFgO3ar)>$R)%zm_a=bo z4HJgw>G9}I%j%Lu#<`b4sY#6X9#7tsATJFz%D+>?s;nTPQR_6O`#0Ee9INAC2UA1n z*T|iwOte!d_u#Z}k?+_ReG*+Q_LNgHl3i|wkC*XmLMu*z^c9oe+Tb)-EFok9+!XuEYQdPHr29B|=T0rz#9%EEec6H$6t{~biR zMlRzS4Tl69U$A%}nl#Y+JiIrc3`$U5&CE+~L4@=>7Jv$ri$kCT{p@%u;K_3)i7&P5ay*!*8^wX$%C%J1fd*%mH*$~) zd&(p|v^G|#2J^4AkR)ytR2B0`+X(BpTlI*3I+rR;l8c?J?IA8^L{}mGhNC_ncl7`a z=Uc}l*epT`KUQUgl>a;G0A`nNwJ9A7udp^#eF;5RZ+v^R*m5~NVr|h(F}z-Zf~Rbf z8L^b};nvX!gYQ8%PatfJZNB&jSg12lHE`ZBoI2j>3*mM?yZ;{GCBe(!_(B;lV-Wog z+NQg%HpZsq-=vx6t$K%0&aP4?Aa*jMv+fGQPd*)t6elxC`f1&|ZJ+O3a)pKj*w?EP zd;E`+G6oERnOxN)F77RG(@;`b1}oW91K%BC?W;W(P^qqTI9{tZJwr~LA|)fJt0Hi9i4!54s2^~zze zpPc70qYzjpYQ5vpnJ>Xv=PFof$A*sU)<)|Jj@>gD^G8?Qw*8Vs_@|8lU{74c$E6SEnSK6T zVW0b@^76YG5qwz=!@dlMQ9x7F7b1LL;+|jMyqo?v36GRN8ZxtV&_rPq!}lx&wJ^*Z z!!XJ9-Pa9YF4$O&J1OrFh@@8Kw}!!%cXARlY*QkL$Ex1eai=D83?;j~>;aRZvDUU~ zoia<10Qb>t~4b*?UVk(v7ar-n%YdQr;K_3NL~0`>AxS&>#RtgVS=G&j6kOe)E0 zwzWr*eWv%^>S2=AtIRs=a%w*iFq+avY1M8dRi*M6)8t-fEAM5=?-gQc-?twxJ|jUc zQ4L?0FKYjz=TzQA;@HJXmI4HKQw9a0)Nx&&V9V2wOJw;$@y(=WO$)3x?3916eAfHA zejcOlE5O4j@mF#jjSPvs)7E9gIWau$2h$NEgh=*{RHA`;-ZJg7+F&g0<3m#pVubYr zYV+w8d|#aKdsb|N5YjuOMCuC^jMvn;Y|L&t($^#hz7#vH$7I7)H)OHK zc-v*kFEjYh6uW2c?Kv;t`N|pgD@oKU(~TqX1jQPHc~b(s=GeyMw`rY5fo5luLZ5)oA*$ry zK`a0p5#&Af%^Md_Y)Y{)sZcEB5~89ZaHV)OjBXukY!!t)3!?ggGOD>>fJ$mgyM-VD zc{%U#CPt`cds*Z0a&#&8w|5%_awz5u$=PX_{+V}5H<>@44>oA+#yd%Qkwh0o?9sHC zY)j)k#+?r~_7q0Tl4gb%;X@u0HbsrrHiSglfX3F5>faUcgINI^mG3A z8RIpo8_Uiog!rH2!tX=yanLx026I3n3$y5O4z1TOlSf5>WJ(t3J5ODbX&{ z8iYK6@9fwNajD#o?3%#K%Ai4MIaInNpZub|I4d)M8t?I|EUAYS@nx@-CWkL(?~PjK zXuET5;3>f?4aOuEiz0Oi=GpgOH|X*FHa1IQr9n!o<~tfl8HTet7zh8-S(^bffCD?G z0`tX17de;>EA~4#?4Brd1rUvc$fE?$|HMGY;-|T16ng}A-G*fcS4JFuzle}~UuMb@ z2JZRN)0S=kNl)DGK0)6*Z4I*lyDBiHJ*?;=gybzFLqd?sRI5tvJ5pe;&f%UMc7Wz* z8ER5av&A>jWCcR=3J|?W+=`bOd>d-HK~?#>dOSm^&t~Lh7*jm(=Gk+@|5@tDR|=(} z?y(&L7$B8|>?%M=#E|za2XZK~I=I{BK| zv8x4rwGnhP+TP7+)&7`p$*1+bh{Gc$Dy(k3#Y!WeJtuydlg|7(2BjoSf*PB7xs+gl ziLBC*o9(W&KUjg5JP3>1)x9y2@-^fm1weP=kUqpf79}!zZ37(&1TJGW4HpynN+cpj zuA|n-iWRi_!;FH^efGWI*GXiThk+8}?P+kfYS}+@--q3qc(>#fqi0CW$9sEXfZlV< zhXsG{)EU*W7j6$@wW?!1Y?ME_?2HO;LkTaNM$PE5Y`)h;c`DDWM>l>dh%JvW zx)(9(7DxWaE~{q@m?I6%`AvKEpr0R!uxs!%6j|&hlCD>COsH1DqJQIIyC2YeuF$sS zA=KS}r+IByJFLHzrmmZR+OWt_=Nc(8X^!JC)7fV4Y5e=TG_xV*fW32&7>|s^oqj!q zP$NrD+F$t3Tlk+FRJ|daZq$zR$PA;cOVdz(K2Oy5!taTOe_Guf~HZj;8sqJP7G-FWGu_)|hu0}Ttm zTWpbiE|cI+MiKQ_o$#F(4yTv}gII$zLR*n%?=gkhYfp1QH4A|tgR~vbT!|`ff^-D7yg8#pqM#BX(JCcmR-ER8_L?<4 zNnx{F|Gi}P4T_caQ1#2Kk9q2Q*R+H}fzCg9b?a1Xk_*PR8rbd`JXGmhw)74RO=Sgm zxtS8<+PLpM?aqSEyh-=&>hKa4yS#1&=n@YH7my(_R(cB+lU!!`xxEn-ZA!9tgTa7U`)&9w&$c=kO^?g=Zm^IdaSZQ$+> z?Lde00fU+sI4do2-gQ(qf@Al}$Ob#GxhQyr+<-DuoTOR+v~tdO z$@-gj@dDuZOI=&nceNz*rOKT6bx?qMpD<6%?c19o;xai2{(ZkXuWFYg?YPG|3fj6; zE_EfBZaGa>Gv_g6BI@-`YHUColc650!lQX%M^j>!Yea@)B zc#%hK#1{*;Tf2&OPj+2Z-JD-z@03R_mR4r9-*%oCRulLXxxJQ#B6jopFUwuaRo!^S zI*=9QBe(2FUZAM0qIaB}0zTDRCc4*c{4{=Ge{Sn?Z6s$-wP}xLr)u;On5=GM`Ldh` z3J^=E8#7O3Yz{kI*c*;-lW!$t`EQTX z^H9dDKUS~6uuB3=)BXA*D&3D|S;piE0~y&Rv{m=6Y{61|<1RRMj!&llodtT!O*L@+ z(JHqyk^g;mqi^d$ZV3ikAf(j|w!gb%ks@*XDW>%|>E7XfG3ra71MY|pw0?{}Q zMd997(Ah)=6xW|RamTa;+Fy~% z6>sS&D!)o+Ji+YvEH2=GX+#Q2Ghs+isf$Lg_BtP!c5cS=y0Gsra74oSBl3(&ige!$ z%00fW8(6;$dxRectM5h(0E#N!t=;(Zw04!tj#=F5@vc|t^dB6U@daE{LMJmtDzDM~ zDPNNLmVvLQI3%XF!NEo9b{FksWcnKYaxC{9btUV)@2rG^AT7tmuw605YW`OjNxyc) zSW*LV`inX=l`y!B1o1~4j2&93wP<_QUE(#64g`9?Tf64ye>1hyZ7|~U&g3uI7rv9k zzjO;Ir>j=1OD(Q(U_fEtf_Zj>7f=6`X2uOc^t{9%VK!orI-?9aOu8iZkxHw3-kkF# zd|Cj@^VOQTyM%(?6d_Ov6NX(r8TN6tlCwPb_<@P-T$i zpdvR9TeysD%`}5ew#&45dXcY@VT;#c>2lLXCs`+TGhkKh-h!b5{Qd#d7?f~<1t=C*NmKIz;0FV?7TvYd zF*1D-AlhM-T=`=FU^uj+Pdxt78IjNoapJ=#MjXp*yZcZ19n=mLgDDJp2nNft@~=FU z(eVdLrN|fgrVeQ;1-HTs3|n?v?Yw3`R`!kzc$=OuzE@A>X1?#po;1-PBW0IBM9rp3 z_a-~TPE{C4L&}>9rtKo+&EsxY9fGt!C=@xG0Djl$pP4*tURAw`4$GZIUqq-GCvry< zkJF~RZPK+OVpuDuF!2sRfr2vIg z(?%;ibjSW+UsW8$G6JdOzeKN}@Fd*p)?!H4=!gdgwDNH>yMR`GO>;`qJlu;M*j^Uj zDX`VUXvNzUs5$;>ZuLyH)UY#$W{T4qr*nMGR|w-hP9;4{5lcM)x4b28viaRrTvN?M zknt7!donJ^tKKr!o4`#Sw?nNab`x5R0F+RtM1(-4=27ZylN?^*uDOwZDFmsWWfAj) zAxJE4j}&%P7i$i+3iH`Fwa+m*hUq|%$-?)BdWBpnCBL&3n;*rr>--R5<9dhNU2@@j2xXbVk1F=r`A_2WgdV8!WA#>{WeiGg%F5fk{m2;2L7yf-qG z^i7k_n`*{6Rt;u(A&%P-I9C4nYLr(4<+LWkjciew< zXzgQ2KcXp7Zn1Uvyg@dw?!L}#7V{U6EW>44SnXb6OS7($dPFCx@*8=I{miQ3b>B+( zN8gQ9oUcWxYu}MC!-;EN%GGgkP=yHs<#RlIC#Ewf$=f;!T>2XZlZ;?ABmS1{EjapBm@#-%c%e>NDKEG%=MA z0*-P&q`Kb#yq~b(Gta=NWkuoSSJ>Z2^6}Axp+9J*tS-wCcM&v;CHB}IdF=!1j-Wuq23eQc8%)wh~)|}Pklfn4?jaaM@LE|nr>Si zO9|{qXQ}sG#Hk6)aw|JF9vG*BINkENiU#u41QGZgbhjSPo4>T3Yrd*4ox=r?8U}K@ zNPJe;5xx}AIy_)=w^8|QJyYvVe>t%j7wQ2qLz#NE{gw{qdaQl1Sz-m0i0DCaLD=aX zC_=Z(5*<~|VgJVFwJuR98uF|CZM6Y+E=fhTkS2i+!5o zuP%6{?$iMLEsi4nmSQdm?`=JN%N6>Ez?G9A{6I~h7A$?M{Vq6NsO&V%&LdbEd9m`$ z&8$#v=xIs*dqxZd1Q=UEAb+IUxU{JYaCmy{xb5C5%35yQ(DHRuZuEx9Abq#L)z&z@ zTS;8(=92?K0!{i(c$O3M2HG@*&~&a+m}F5~>1bPC=%2Eg+A6xl(kT417KcWF3*_~( ztBzFOvSo;`nE|ilab~g%Nu4tvp`GQxu3tdiNnr=UzN^CrI@w~Iq+qMgmCL7(q6v%l zBCyBi2UMw8&7>tVHf8}NDyv=V*MHvV(qcJ4LMk0GRe;rxRD9`hA33FZ# zY8G^sTCGN(GD2@?7APG9lRt@0?Wj`VTkZ(J_kbJQ@j!c`I+u}@#9Mn6(2O^b&l}9= z9X0+Xu*PGsN=WZ6VoAY&lD^_zpmivO7mdU?d?t#3f#Z8?V^EWlnL%S#nb;({AQ4mC zsoeW&L_p0hu$HJ7jY{dI@buK%dRw7ke$aZ$ej8b#>pa+NJCiB-qh+bRwn@BDN)0(| zgv`h*%3Fw5~Mrp+T6Gt24WGo`@)yhk}p$q+`gf<2v*%feShF%Pk7j{Ub3!y9cEAsIB}Q z{sUWm?1fT6P^QnZ`Rs8j!LqB1q)3cvmb*zzFeNqmlkAW84K@M?Q6-^c&Mbe}I~g(C zR7gEu)^>ir>uXkiBrgydZdX4#rzS>42?3P{8wcniwniZxCg2wB( z8^tXYTa?F|6g3a)KXRw#YgVydBzBU3*FU=kQWi_F@YYxS>2~fEA9@W zmd9t*tXq7S#~%;0Y&vmNZ)oL1OJ67X8Y0NthCA!&#-!QZP{KTK4be}r8Ol(Yd#Q%O~k}m&Ap-yb}Le&HHo)Ux&=6aH~j;3DMV^=`f zCSq%o)2W7XT3e2B9ujp4%ElMXc8)Y#COHxl|AqfWY+&y%!x?uHXSX;(RY4$A%Hox( zXiVhm#7nSJe9c#uWd5^rf1P(L*sEG5Y7E0Jw-S@MKc;p#Gx&}3;e7ctk3W^m4i-d- zIWT8H^bJ1SqP`Su&Dd_UUsfmaVRI(SAM(>ggI+Mmi+NfsrYpVi?avP^;bP2V1a2-VO8<7eCf z?%rGXkFBp^vGH4@BighG^A;)m8)5lx)=!T$r*pO6B-0Zqz9I75y(Z5j=GvpnI#0`A znb3(+`f-f2{-rw7Q%Z0ca!!IetyLfA;hRpPF&325M~$Pb1Qzg5BIfR!wjXqNb841P z5W1t>n8)f{l4ZmKEtt*DM0xURpI-Q=Rw*5Z(+v3qhBUbppNNe!{KgWR^{wz_@27ev zRytz2Bg(aanRFl5t=7U-YkvCJ`$@V(#!PS5P}cu<^cdZ;Y1JwM1YOACSUo%Gg64pT z=TFjifiolk%$8W_4hQ-`?DC5*WBtaKn7A7uz2j{H;-^0$VW>-6SG&QDN>jT$u$>Y4 z_cwLfvv+k(_i=6DyVb=6!mnA*4Cj4Q_%)}E@N9pWyBU$Rlt%t+KHCWHO0ocZ9g^I~ zp3uqI5yJelSq{dxK{#kimLtSa43p;Erj$Q#zY_SaX9BuVIv!r5MgwwjwiO@|4j&*t z#fnrVDVHmlN(O=_g!7O{s(U*WC$Tm z1jd|f;N{LL``K(H39l7Wgt!MG)(k8iNqfZf78Lyeg{)CThS8!nfzLr>%1V$V7Iq&w znR{C8^jP6{7-bb^S299~ zFn4BXr`mm!KKmbO*hJksLvmFXToquy7dZx4t9fgbHxSKbTOqeI8>KfnMe;<5E17o? zZjo8Dq_&x>I-P6Xo9jWj9ThU&yPk`sR;FyCu0QxVJ&dGD4)6@RWTRF`x&il4!kd8fr77k`ul8xcc!;LM;|liWdOZf`-CZP&6I-`nz& zxgj7B3}{>>X!G-YoJGj&kY)I@k_r3K2cMpSFIK`AQLt83qF?zu{W`jZoyXR(S}zZe zPXw|VbAHl)OD@N;akdOAp9H0Kry-rk>vi3e6&uN0mC6QBkQPC7a2($&|vLI27vP8|{_p~a5Jol9q zCc8@kD+-y_1qB((6~r)!z9So(F^moA70XXqST6C4Fb zVv*H+nJ}4bTuq8^qT65CGt1TVyK)@gvM!>{T^f4o0Ie#~I$rU6+1O$gj~SOdj1gQ} zxA^5?@#JPN$CVQFml1$0;Fq27OM82N!LfRL=6z*S-##%TI_0`j=?LutIt0?PGh zdVEt$vTa?cJkUbP@l<5he7M0IhQSO;@}!FLW5;;Aq*6T~+6kT?4i?&=RTRZ@N)lnoqyNqx%}NRP=Pq zE?tIQ%&k1UH3koI%fdrW?+>@zPqtchetv9SDi}Y269(N+A{V7&U9VtCWAJckar8O5 zS3&3SYC@r3+4d7x9M$Hk)BV-G2m~6s+2YX#S{;F@9LrtUh-FU^@}oy@hg3-t_LTTtD~x2`<-$Rjs?I@X$p~v{&9Tcy2$0^om??n-#=IObUnZuWE@WR2F@tKDPH7`|=(#=l zTz-dFWPO8^l6;e5ZQWS@n{uNy5EZjvhBj=_-MI) z&W}r)Ij(au_`YBbu8ESROr>kku_ttKXaBP>)4ZT1`8 zy5{TL^oK)?wU_PO3!EDbtOO!Ht8C|heb_6gBa8Q27R(fP%HI77SrzG6sdqy1NfO*K zyAbrkZfsWQN1XN#L|eG z&)8MW(8)^^2uk+xN*$sDE73cKz{hk<53@h@zpZqlXsH;`mh3j^be(tD64V@v=^48} zhw`gJ$nO%){C{$dtuS3nau0)it0LM-tI@IvQDo+kB>|X=ac6a6%opV|fr&A;$SvDk zfXN3msJ#Fk7R&*vcO#ZJ^i~lt#YWdITPbg@{1JG-ii&5~7OkmNFOtiSbKQYR{F-_w zWU!_Hmpo9}-uOYeVF4kpCV;TzFY)bBhKu>774H|q&a{3>arscYFiGJ$c%$K{j`Q*& zYF-ra)QCw#La1LPMFOf=DpFA;9y6~b26vPquSx;d2*Ee~_un;Jqni|=3K`nv5Ww)D zXHz(97`wuNmX(6t=aNU2^YshAjrkl`HhLBGNzmKhFRhg2yW|qOpEn4&dG}`;{hs#X z5VMfnvHO$xAYVIjCRgfbO*CN#cTp7C?;2)jc4`e+(q*+Q{@u03x`up_`tXbx4e?1}Z=RbtYpy|c32p60fgE&G!%MM)cXo-_X*aGf3(_Kr4~ zS8f&=;Z`vJ9(&{}5tr$hDxaAPU73=gTu;Ox!|AOBF);;5pNpM5`gti9zw&O^SR0)tSMgrZn&3bPdbJ8-?&VlyE7xFj_q3Q3SFKc$9rBgu*km zKZ^F4GO*8*flC1wPNP+}UBe>h_J_`oJTd(Ppk>>poJDI83n*yH?q%_QzF=A~)?t5G zU&4l2^NY9NYS{7W3!Y<&1GirhUwyxoF!+`eJN65l;zKF4Cj`O&6v_1Ac&w9sZeA2Q z90sERnmvA0B-~oED-Pe2yl;CzhBxEi0y210G{;_#8Z>oe|52)ZYQjb#)ljPN~__! zNDy?=F)>Yt!N`x7g}O-!>Q!@vn&bdOJor$MN8@^Whn7Q#$5X6tnw1-^J?s8{%oGCX z!dax``>p?&MOk*#AL?T&+`QKmQ)+=ILQkAaA#gN=@^wL?U@fyTU4g~0v=v2EEs44< zy=E&3b3d7NJ%!J`zeyeuxL6H`{SVWb(U*iQwa$~`zmF94MEhsCHb(hM(G)+tlWmig zl36I-r9nYvs2cw=*H+56ptkK^qLK8JSoCO-6<>fNB7dj3Y*E5C^bv(7fr^c}S2BCD z>zjx#09jBLjmyf@<}ha*C6{G1qkJ5DtIetJmGjJ!`S4xwmghxViY0Xw&-X0abu*uS zmX@3VFQUW*wb`l)2n+5^~&h^ zK}A58VfH!GB$PQezlE!esVezvh1pxxZ-f#`A5X@Og*ZAu6~+^rhP&r(9RfGpmyZ!` z60EZ?ZcrmpVk)(3hq+wqH$iKDfC8X%hj-lvqpc$(ChGKP+x~IcklYaH1~lM%OG>8W zru$;lTuOO48PX1MOX!i0$VwZ~$>XyC^Z5LdOVw+nkry#tDqUuJ19X2ZvSWX9y5dl^ zS$w8^X*=~^+_3`ZKs}5biueXJg`nym#p#a$y1`E84C6#YDuhkCFfB7cT&EIEd>}kF zR4@&el!Mfiy8m}eiWn=rIbO3PL5NwgKCMtvB`i2?xa2S+CizG}rK9YdnI~;t)ijIp zvH+uh6=T8XXRFE41mEpEwzfp?AD*9;H&RDuLkRge3QXM%6oh4(*DPn>W)Om=14KLZ z418_1)ay!o#^f*Lr@yoswGyK_pL1eW2>iPTOBSArh-6iQEfNW zEQaHmBN4O7CiIf3SBdy z(r46|u(0&MS=2ag+m||~WFUEl&0KVzjknrDBZssYrBrDH`@Y>=h6=Cdf~Q^3^eMX#cOah28Agb-NhflJ zF(8!-aNH!^qnXYbaLCICuVm;9*0C+S&iB$q zOY)8DnJY_gcKwPq@LN+NQ)}ubjsK#HDJuA`YWEJkVw#NnHb6=D_1)wnoUJ>v8Kh%H zcadHa%3X}Q2U%U?L%@E7d7D<GOx$ZAt+ibdi!?uC3mbXNQuHhnP5RiEC ze4>9+C5K>kNb1k|_sRdgyHHGjyRcQKxW`(yqw!En*hG+o?>+E7E;_KP4TBK)T?~|q zVeCKaVoqPKl-M_;r?iLLa1D(R=+D2beH+3~EKd7`Iq8#}2y70*_Ssg0&6Ruc_Mzy3 znO`t}`)AjNC}9ss_9sK8pG6bC|Ngd*4#w=l@!5{=|Hiu$`0{#>3wX=Z7ik0Ovo!kA zw)}$_fzf$*g;?XN#E@i2fv)Ipz2n~^R&o^~c?jQ%7bW(TeG6mjd?!|b-qfOQyNZ|V zGZ=52XXCFLkn6eZY=CW<+v;V;w1gb);ezA=>}io3e_P2nAtO#85d4WDw=&@?@0*B+ zzwc0|GnGQ5g|0bs-#eL}Dv-(-ZuPiH24unx3x>QaIwlZOscG6|NnL(x4il(*CLwgW zzUF<+(N@&wS~ddd;#NW%hBivT%#3xhixbqD&A(2uWNgr*B_CRf_!)nzs?r*QaHwUh z9jU;VpWd%wFaj#pUw{Z`Qs!5n8b5^xlXmo)#k!HY#1uX`p>@Qpl3-Cle;B(71_h9r z8i#$Is4<2uY4dkw&ogAL4Fd+AEhPt9R3=}Pdl$&mYO+jHU`_uu7SVXt-l(1fwVn%dNM?zPy^7lg?!vcpMR ztn-~(PRCqjF=+_-{Y40V`yXjjBne!x!9~Vhf-E%e4;DzlwS6Jn#4k9y<5T4wD|F%v z;Cz+!E%T{F+a`WM_$KAl&qu{sUX>W))Khj+Hjns*8bi^DM4J{}gqmPvKKQtc8D#$|SQcFI1AkWoY)k*4fceqX zfDV+zZ+b)p$o`RebyH6xz^g5km@iWD~E zFi5LbRXdO5#leP@nspB$X1p|=!!((8mLUb%$pQKY-2$=YC*E+{B4xXE@t_1oEWnTN zSZZi&#+3m)H9;~%b@oIryW%YDrInAFo7ABd$!7r$b%~7quF1fK|u8x(5Xqjqm2iA9llTi|-{O~lDrW|rV zlpT%%s5$`f9htCUXfH_fdYP@xI)_(WGMaGwcIO9yc$gg zVf*c{Ka`wZtg+jA!E+aFb$Y4m3dDe)0r_~-x!_(Hfs8u0J6g@NXUr&Ze1M~d{#)i4 zBDL2Ckw#CcK0CmD-to?cutQqev=>lbi~^_pO$N^43F63BAOFj;A4b*#bIo6LNpk9H zK3jA!9a6t3MgdarkGCO0C7~69wT+nUN+!&!T|j5P5XLgm&y_z zg|KKb6nH%r(7qb9KsB@6vUH+_R+M_ix0nSWwUBVvq(Nc)co9QJ+B^s|5(BX3ylgv3 z@zaO~&DFBHSpZ* zHy-@G-x2rqr2JS?<~R@ILbpr+5ClIIA1WLZgvl&;YyyB65Gga@o6&;>EPB{(;3W8(i8xfQ6kjJwOB-*zHFY&n%A)d5a5>s8i;(5YnoD z?K__3F-kBxO`eu+%s3L_5$T>G?ezU3NKYTGinP7(1AR2Ww7#&N?@rhgTQ6Stvx03Q z)|vLhaOS-p%Ds3xawU4yAg&t}WUEXcCMiWaTUp~~en+@5tM`e=*rW&0`9beKMu?E@ zJr2Po&Flsh@Q_Vn`6yB-k*KzeHjTYGObcjI^Qt71-$m>#5ppQ&Jdy_3!B+mk@GZ1b z_nd5m%}%t;WdlwMtwhwfCng>@n?Md%fx3eOEa))kIItZi$51zYa1fiEziTsIrYkeo zDMGbHAp5yjyXg960Mom-8%^(f)MsDv#w)wid04uUrH<7fvm5cq&T7gY*Pn#_wfT zw<0ALn(;fR=C6EK`l7~kKaN^a+fP8jXOGAJ^F81nBe7!9%vNQCKcHz5AA|^kY&b7O2+SrneO*OP`1y8W~1Si0;9jGnT$t z%R<7j%HmpbMq2y9hf64?MP$+siDW&1>DdkUW?E!fD9)~@2q z^e-QbP|aer&oX=n^1f_#v)J|Ut;VS=v>emAap<91CCRCulv^2)0;WunIK^W+Jfyd7 zx2jPt{_KmfZZ?rc><&0jk^$EcQ`LZIlc0qh0&8#d47C*@!X0(;ci^sw|GZ^eH0cCWBG_^7{^jc4=ci7eF3%WJk2iZ0ry6n3o^T#>~ zKp5G_x8iHv4=fcXIGXjwpXd-c8lR_R|LSMJT%bOzDAhHjHa@8Ot;t~n(vX3<%!l$T zt-+Qf?a$6g)48@qAvSA8BRe=uG4P0po`^^s3=H7gz7T%fmX)@nhd*JBU!WZMm_F!T zU6@^6aXxP!w#g3fDCG?wLgu>m6`8I`i%-3W6D(IQ>6>F#*#Zv*>~Lj=Q$`riK9j@L zv-ny`#Z|bbXM+A{!GAP^>FWK0LaUglmUG_3NN3q{(QX^JxzJsHvHPlc%VA_W?9NNU zagSk#aD_RWHHtnnQTn@eIoL-@-}fSYMEXJJepVK3(VdFhpRDFD>zUmL1($`=vqIXz?9~d8Sis5f1C%5=QXBrb&IJv~qyl4wsad zs8=r6B=g7`=(=k(XjP}eDJq!j8CVlEJ|*WC(w`LWUaQ5B{7lg*c95)|t1)p~`Wx6W zJ{O>@BDVwE&+KPp{X|o3St<0)k=yxeLYyFE8D8*==rh9=BgvRct`AOcQFy@YRCAzh zI}>&JZLqPV1z!>C z@Uq8D7TXsH`a|e)ZQdq696YmNm{iiiYGaG5-)h_c%THQnL!AIRpVHI{cfv&iOOqzk zqOVv=^7DdTv4806&8LP_v4hA+5Xea8F8c#4C;}WY{@juNU1I$^7?Dr^s=-%SSW^W# z&fXZyrIn9YZ@$)H({PP9IG7Q63?%j8RUd7y?|8u3HR9Qk^UJ0g0g-OWxukf?sqc&< zMC~1%+no)jlGi$7nI}8H(d`g0m>p2BGBO7O98Lp!8|Ocztw{H7@%-vDY+H{J^w`ch zCT<(EJn@A_BvvVxbe}J#al4q@+-lS4lA|T6y(+GBxb$tlGT6DVaDy`l?bsyhOpXs z-zmvn{`$5J6q;O$eI%I#Ti+;nC!h1kpQB;?#+-+6G!nNzdIyg<-{1N#Z zpA#>97Ee;4BP)e^5C@Xf9P(HR!z}@F6%8jOh&Xm9M5b9M zap0+-@0c{`FJZujhQNkL5si)rrT5S<0=fe>Gz>O0j0o9x&(~HFudRZwiocm9p{aB? zPHb)v3f&Fa5>;o*CCP+3TpUr>3-VeNDU&s(m+$OXffGL{7SrSlMm}cWF|uDgQ@gE? zXF#UNRF#^k+fsu_FzXO`MW&y;7l;HKNX4D0JYTymlsK(1;J|}o&$%nl05@LHH*+7F z1oppP@nM}{b0tg5@vdA8GEAAuNgmjUgd&2$m7Xt&K*?%j2Z6)y@9v8nKO)}I>J@P= z+m=~=+w&Vlfe7McdsEK?c0Q_-1D$NN?6t_$951N9>PqX;eZZ|h4NTwK+Y0Oy@g{KN zaYIH>>O%Ib@@F9ovSR|hAPkh4`ESD@71pl`8$B|Lx8!tTouTA*jxrKd{+ceItN)^ zxf{6}=h_RvbH4%H`Li`uP5=Ngh>z|A4*Y%Oga<)xYWjYny*fk zbw_S51kI{B`0@;>0cM{@?sLG6zshx0aYg>*o(C*BR`)zm&#B*daJ!abAXp!X<49>3#6QHQOsAhHn1w@ixv2lNCO+WQuGW} z2oh;sFfs}@Jc4Ls6lgT;Sp;+$3~Xc!jgc{6MuhP7b;PSH;HxW$S639bN5-u-LczIN zE;ssDwI~)cL5pS~6TAovBQm7~XL39+$|Bv4L<)Hz-4Mhv@brHL-tE48>(=?9$qDX6+4wJdTdN#*SZaQNN8iTC$*|Fybs;|XDnsEvn-+#r9cj>{ku zsJ%^yu{9@e6~abDP;!+{Mp_lB@*OfIkTGCw0a$-h>|uJ_hf0)@Kx-U0@J8VDSMqZ+ z^BK%z>!bIk4`jsnD9f1buc(~+kOxr%0Dz^_z?rWSZOqcv z#-w^Dr52zIyXTB;dA`ZQ^D^;}X z5jB9B+r{su4+0xYz}2UKt4{-$pAhbjR8UqU2`DRsV2uz^Cu(0IN4YFg!%Zs#*F-gn zfbp9U0010#;y(d=i@rt)Rwi)(h{?6h7w5_);No}1@xIprV|(*)hNms$L0-z>vDr^` zs7%+QR7txRq(KdY!RQ#+*qHb`yp2rK2N|8L90c5}jE9HO7#;>@#7Z*0zJ_>ZSy(8n zu2>cOx}~c%btRrbCTPkG%eQJF6G*Fs^nyj|cUb1iNnXw>K~fl*F~vZC9Rd!_%_8pb=}OX$a2ATOpynGF0&neKQOYd2X7O=+-raf zU&-pn*3P8$k1T6UkO)RyQPv1%yfD|=d9Ya#6s?3-0;^`6J7>23B857pCg1zy5#Y|B z6D!!aD>iTF{STn`p=n(}pIcD%WfBM41s8nh7G>@QJr~o$lsVJplCVs0?T>Kv>14c# zgiet~g1lu-p>K+sz}gLcKBi@DRt_hw+a9w!{h!NSkvz^Vo3W7xxv>lSkJ?u;VnUwV zaVSYF0Kl`qAr?NHZo;%Qc|rQF{5vHTDi8{QrJwdA6q>ydsMAT4Sy&zer@jardNa_f z(Z|-c=kxxj)k&IGPmamU^B)(M5mP}q*L4g{00$ltKR~#ix{{CySDpblWp<_`kOl$W zK&|9~j~Tp;C}=eF>+*bf78siCcQp+LPJdarNK--iY_8wZKj7{D50F9vO2f#f)*`YC1q1Jz|v&gBuYa)x?7Gk)$EM%iSL zKgmEPX)j*~qyb}Kqoat%#xug8Q)PER{dGvmSGK6bBWMhd0J8vijCgewd}UdX3LA3Y zDs$n0mbj(-^^plyE`Y0n4oSbrHIMCnl}LE*&SR0I+rr*jUZR zD%C%`>S9G+Gv#l2thlr^{oqrwp1%wjhy?NY1&Y75>i}+xHghj9|4{9(%ZkYpV$UYNpc4hMToUGD2}{33?L$GGQ|~Iw z!FDdZ$OG$w(Sp9Bs9zxaD!^E}F80m5`TfB0_aU`Z*sj?7R^Zy>z``r}y5NaRND%dA zTJmWqQgT)$46=nsF6)upbwE5zE)1Cr#5u5ZCQl&9GTnFyIQK2Ooay4N*ht(`ADq*D zSP%QPvaXVuZJaap3RIjTjBW=PMs>mRj;}l+bdkB}ge5|5qR>TBp%kHDxxq^wqcd*& z;*TBwC9g~h;Jc}44qL0h^%sFg3mBcNl(&X>19P&1j;fSs4I!GGKx1wXntS)7F+C&5gJvs`2c5!q z^3bb%7!&V%Y#fd0Swxf5U_&jjux@M9Zk>>VLooq#N2E#L9Z>QB7qW7G6ZaQ{#RmmYPmh&1vpET=p!Qtqn?6H2}^75=WhrYO0KlF#I|k$K?6U+A&R6b5Wu z0angrK}BvX%(~27VsOd0>UGZHZ z+%Gg*z@B@>@A$ichyOXSz5q?(mJE#YpBe@L#XFw|1n6LlY2S@{E|1N>$wj1sPo_Dt ztdfInGHCvdL}Pjen4SUR81c$7;*}-D z%gf+xW%E|ey1-^GV2h(1Xq*EP-$;*B;apXAa;V1T3=9!ezvEI7!T?0sib9S=@u9@y zG4S&51Bc!wRvCN4Ro9*}eO1GT#t5+g?ZCdb0$08VocjW>d?s5gD#tFl2LuDsy&pgv z8x~SLeLZ>ku9~d3dn^)6xyHDN+;_8f1sL61m~Ia3wPXx5#vS>tlQ5=S%M5#Ds9dd@ zTxV)Js(fYqQu&KHWUqo_d2W&sKq7w;3Yw?^9Q_HgS46Kd`x@Zl*G1Vlvi21jl|xxS zMoM_Pg4viy8BK#5q;5_zX$ue7mT=97a&m=D&R`4}+YcOn7jWT`%vw0zgG0v4`f&z; zGp5M(Mtb04yt)0)0mt8?jmbOW%Nw2pPW(CG`CmhBUZc)!xOpv3e#RKd#5{x2lT;-P z+{lluoX3jdn`O~d>j}p6+n3LYd05oS0^94r=`ZMHlrKr3j6r4}MU9UfysICS%}t!S ziM@=Ii*F_AdHy^l;&>JoLHw9;&o`PSgakaudjDZaeBO0F+)(In{qR-vhN?hE)_||MPtf37j^byu@O13cbG2)8( z8Ox5DArH&4GUb|-J2{d~?vn9>w}nD>&uh!QL$CMIO+>)hAz^5p^3ywf&q;t(ha<=KSm$tEI}KJJg3G{EfJL8fp-JbeVQi@%>tMH3}_pR z9jN&`**=IdLTOX-J*CIUw>vdnmMW{UO$KxuR_g;>pWxQZ&Rs!WDu3=T#U+^$H3S=;hjR3`{WmZl`GjiD>olA3Q5Qo z+;I2AW1f4)G|c&`tG{d?mqnBgLOU+y#45Vu){XZEaNS~ zOfQ}Wj=mF`8JcfaQ^)iEs^scYuDdqUI#tPjG9X^V+a707!`ECE%IgATAhvfJp?z zai%L*eFMvQ>A4{tnC>{pmHjZrO0JDH=3WQfCFrVYn~$WD0#tl!ng#cHSvwTefhC+Z z8i>Xx5RFeF8XFtDN#9OGnfk5rF0{oLp5v~sOB>?8XlxwOm~g2QudEN8b&MA1+^S zR!|kv4KQH#Zt+__1)TmIaN#>hEiEV)I;u>rrlhXQ1G*^d^{Q?L7>tSWwF-u&fr&$c zP`A7z(nS$NNY3NzI^P1YCGH=;37CItAKz6Z_FlOBtt?EoAt^(uo0`W;+lvd_ z!Wye!Nk8Vu+9EXv3-hw=3vZXyRC`r1zE3ED0kH^b&)vd3Xf6#!9yZfdSMct=7kJ}8 z%=AQq@Xd8AyAvMAqQ!O)xc9219yWI5G?o)W@^UxQ09Ou3f-Ri*TLvq%K3W=be zFP;{gV*|kX?*NNu?B&Q26T~&^BXj4uy*kRjd~<_vwN6q!gK=*5$0!&_Q;P#?lVte= z&%7PT^BuW?atWOJV_^Ln2u4Xg(2b6b8eOuJ~E{MlHlJJb&1Y8f~ zk(0x`!3mdtM$UniOF}?X350^WPooV@f{n}x-)K}>UHRseMoGpz244B35QwL4@qf;M z`f>J~{VqYkE`m*Xc9I9wDgg7Ei$GAXHHU$@`++@g1jdf`yE+B~YWMuy$AQg7<$|uP zw+h#$aB6PkMVRg91#g~DGtE~CQ0~i-1)3>=EnE#PAO*~Psq2+p6jdY*noU6(OiUsg zH8!UX=pKAzvN{QzGGszk5i_& zZ(2-!qODEHisbw4P&b1H)&TZbT!9!hfWvPE&V2*8@rr+zs=Q>wt?vhJeqTZU%^v%1 zR{E5FVfJ3&>UTt2P&ZEoGV;JA5oBCbxhNM^5N(Vi667%_;z$G%Vf%yGNCFpmAkX*Q ztB23tp6nkc_X;!gNmQS%9v^ut24b&+UVPHguoyQV`R5t2X)TOpK@!;(sl`?$AGFNA z3d%K|L@?5$#{Gp;z@aw+H%|BVJ`f zfuZq>KYHw!=!ivv5GxF^ncU>qGTXKJyOC~4Sp;*uinoOO37MA4=9we`RUWEwfdki{ z0`@!@HdXEyUj7r{#m_jSP#t~{S_6FHA`H4l9+(^PF(y`;9)1sS;v>N9eL!ov-&NCp zn9ca=#o3cJ#-DmpRR0suYxEfkS9F!plisD-8 zqm-pV6OG9!H23U9bMJmc;}c-5UT!S8%Tc6+^HJ0lO-|}azplq^wGfR@pfNKiHrr-Q z5D7eMe}(z7+<{#brsAglb@KABs1DT>T{%uCd$P`0wi+xAkAU?nz|;v~bboF2R`77$ zLvs|EzZW?2c5%FTR`@hdtI|en_Pg|hPb9cj40~^&%$QKnjd$>^F(y`wwl~CH3g%_% z7Ns0aO-CY%fD=CmOx9_MQwIj%yBL}UF8`TWd52d34F948Mw)CJPm1N~4bw5EXOIB+>p zp2y0j%G7*^v@|f6N*6s#u~MFjr*4|8#>s*cWw@D)W`SZX;@a8}B1XeR_&prMG zf|=LQ1aR$HI)Aln1;R9!VRFR=-vX9i2KGK!xoiOy(B1@|{3!7Jr#fD>7!crs%hzCT zK+h?F3djT7;4OcRVPNkY#HLwC-Uo~y?eQwQ0R`3M+pEB1zamyI#VUay{xVYmm7a1# zNL5$QWt)!CE2Isp08M~31Xpd-B~@TmR<~%v2e)KXU?I@4wSi@Iw0F8nSQMc#F)2uc zeStLS8#+O3^io zgdWyS)sodgARKrQm_AWYJxv@EhjMSa>?i%gwh`{me<=X zp93yFCfpj2?MwW)cNzUwKTIA6mQDf7XY)jXlQb|2Pb%Ln4_L~=Aj``n3*<2~;_IJ^ zv_3S;nMB%Ja~0lN2joV!;N zlAVI3qewSVDPaONe|O#tQ{wWI!kh^ZtC!3Qi<)4|7l8G}EG&+5TR6LYPJv#nv2g>~ zTma@?Q}`MJYQVy2;G4fJ1O?YXX@rsH?EwJ}*ho;T^??X*Ps&__x%@kE1UT|u;MSiK z?uv%CxwSzrn%j%N1zdSr8QA08fY0Qr0d0Vfg^x}k9qhBd1DQ2Jjs!4>1MOjx08_}w z?QxK$$>UhRUyH&M;=t_Z^68M*i^8pOm7S#1l}$vL8qR=dbR5n3d9?N&Kr}HKNP~W& zsxrUKHP?9Bjcc7$M{7usTGO-QyKHYMWP+`o+rp433*tK4?vEXP$!0R zoRAyKwwHt=X7Z-4)R$+ib|J}z*uU)9yMXEAVspgREBWt2vobU4o|5@Ug-RW$NwLrc zFvq#qo14E|{OZKmKCwVT*%QG^ARv*mZ1c0lv%s+*5PsAH3~=Cvu?L`Ob?<0XPWQsL zdt^4rxz?ree1DirBVZuF3c_*UX++4LJKS zaPo74P>5Q<*d72;k~$mIZn`9thnT+?IQw;Ab6Lze)OknxGsBRd7btTgeU9UpL*&Li zB0RZ|*7>QrLOE|TH(4&2r&z`pMXT9fDwwh>|c^si=eO59c~eWcZa?Hya%n))pP$hR4ygR(~P zxz{Y48r}X~+B2swHGc7fM}DcQ`GOgVX0P4XHDSdZboShnXO!bMaOUg4wdaIM?2VU! ztIqi#%u!0|y=gZuv>z*bj?M+PAy4L9d$OjpxPQ2dbc~ zuI876W~{Du(F?~30apr_r1`CjI3N^=G7l{d8LpZDn8pez$x&;DLs@)tvAXXCRtlE;5Vqn0^DPZ4Q2S9<^4I+U7;|GDGKOmG& z>kEk-U(*M&3oH^!SsL%dq*N}H% zq!I)Gtz#^b^BsA6TPTRQxh9^JhY`I$H(eGS!A<3XtaQX-v`|BSAqR+>mqUBT`PUgKf$%9fEPcRC#h5dAlKevqx@>4 z^Y~t@<6uFZbYi7YcAiMw*Q6g%Nr2PAI*KBR>%vV0VNsyI6#2n(E(U~#vXXIg6?pZ_ zz^bu1a=ayWV_y({^e{XFOdJGXzNoK)U}A#{#yPlY4ri8uTG?(MpZr~5>>x08tH;X< zD24R};PL+`Hp;QBnh6N#0la*++SvVwPy|gL0S>+m*!PB=v6o7(np8FVtpI?GW zkChbwvVf+NP_7D49)jnH1cAJu>sGGkHc8H{h@puCSDK_^zlxipN(6%v7DZ@GObNnZ zcn5sDI}NOaQ0N<0CSUbiqQYdjZDnzf#(35mLUV2}Ft-=+$}-xEHxMr^KyMR@g@A-C z9u+1{@3k;BuDZHXy=bhkOA}CJQ7$LeN!aExaON|>i68Gtec3@(NR61e1$gN10k8fk z@U4GYQ<05DL$%jM7weDUj^G**1tXZ-Kl@q7l2b=0ABqPFghnTg5UoTFnOZi z)#w4wwlI4;aPtR&=YB_@-*L3dBew!y(=WvO_(<+?$dmJ;=X1=F$a~Ch-*&m+K35!TS@QQ;dUlH-Tp%9{VZjVD z8@wTKfa#4vHBD0=Wc3Pg`YXWJDwVN*zJ488yOuq?Nd}mOjANa+P{;-?eN(t%40!RQ zz}NqijRSl3qrJ3%_QG}Wjdds;fbuNr zGn)j1g&;`7xFgIqxu+-mU;r+D894AZV5Hj{D*K?zM8c7`0?+;~aN|@pWiwgiZV?GR zsE=BTNYvw=d+IOq#uBi0Rs2@32-j39c~Dd)7oPmD!eV0bXiuwd;IZ!=VwLiZXMy%w zR;Q9Ikg{oP_;5A}0d>`-=|08=%QclK2&BQ_p|Ul|-q!%Lw+O3&4qHU{NGdG zpH0cixzGij{48+#^TLhVfj0nqUk{88+9vE3+QThB3S52yxbze>*C}a|F>>;~#ah~a z3`cwC&QlST>@P>8Ns$HVUFJ0_QDCn(FM@zOt~pT?nV?#AC+B8(W|>O-z%#1@2KsNr zgsY;%ZwHRP9~j-!aoKwhSAEwcvt0N(u<#0`_kz?_QI1eh#!cfdwf}+sJ&~)A^y{_- zRxT0(uFD1Y7T^o$ahW}qhvCCDzPDd8;ih;zXsg-H-x|zP#y6W@WLm6lb;vw+SBrY+8l@8 zFef0O9K1*clSEL%rjCgfa{C_=?$vez`q`Y8P6B5>Z}ivP*ykX>YCs(actR?O<4nLz z1=f-l2Ssswhyz)!d>UoRfPyev+C&&@w$PZGK{PoH*4*ZS0bwkY0%z~8R!4H>f7i2#v8!*{w?t4U)&kioONOJ5ODM( zz~L=m@p<6J6BjEI(2se3qUoSR}9qHFf><+(q76GsQ8^D+UQL^8s zL&>S-SrFC%VIYF@i#zQ-2hWd5r2d8%kX#mbM5Pv*7P&Mks-J35tqgL>A6C)}RbmeloLrj%P zlsQn$1zQ*M>{)mlXvpFx&%uzzS$Y*X`3c~}pGQDNteh1tixy8O0)AqI3U0NN4&u7q)j$@jrG3Lnm@wL$m}I|FY6&wmVPw?$pHdA7f=N+85a zKM`kyuu2@5=Dl3@El`DDo+p?|!5c;#g$&5a+YaKOjKaS%nNXB25RFfwF+GE5bUc?# zz%~QbcWFv+*w59l))X*)LL6Uz7T8=!;u7+pRivhceD*GJP%xZ9Juxbu(6D^pOLE+)76TWW1HfPF=W2j z4H7{v98CVo_m-YZ3_!mhPzK2hJO7AS{I|EJv4|Td8wjLZS||25zn|4XLmG&lcM>nqih z7}sK|Tz>KyWnDnsMynLLmIkvbS>-&v$ug}E7!#W%yAsJ2n=ZGu`ZU)4uIU?TW=cTL zk*v^&WFpx|qY6|}GHJQ;9bo)0u=i~UC<6|h`7-eQ?}(L3=}zZTsitlaWEm8pm<_4v zlE(o7HA8mVQ^$bg?*I|AIkSzZsoojUZDK%fH+{yrm!}cng(k~Yl8xTJ7B=TJi-`#Oqomr zS=h`SS@fAZfJ@)dlh7tQuzbJfF?DVW>k}fNF+GjO^fcnNRm3;03#$aJJ*ks8u7oLz zR1hKEXe|{}S>nOSv`LS%G-3!D6N&tZD6zx^FmV)^yVt0%L0e8%^e!Fcz5E#P%qPVH zBB}6jt)5hg1h2eZ#on1j0{NWgLMkdOtrHGhd=xnLb~?S~3&8pfnmkaSUm1%T8BeV% zq;d6rO1Xai8^HXXz~QhTf*lQD>Q3=H{$XI@Md0eAz}3gZLcKWYpQKVGvK&h=C5-b- zd24zWkWo2TJ|#j1VL-4#7<4EEk=ln{d=%JP5$=IH>@&44e0>2p^?6`xHOrY9o-DMR zC_+wmQDf7r-9SIFI=MSc_T3YP{sP`0}eb0oOloL;vXsxMva%T z9;PYP=>8hP z_x>y}cW1wgzH_m14mkbCNUf3pe_K)AkE1CLE&i z31Mw8Hc`V%3g{br_AoG46Bivzw8nw)W7+xmG2r?WVk2MkK2`BrbWEMw!Uh1*$S9(b zQ8f1KL%eVu?Hkv@+gr%momv-W7||A)5+T<m-3;0FIQJQoAp~u@w!bU7|&i?vE3F2-WCbe@zPmf`64h*xc#{H zyq!GgcAI|oH-PCAz{IhRliK-c08_V%--$n$kqMU{6$B8F$x=lwjiM;;;>gzD8wjq8 zwgWuh&$SnT%@wg)6YMw-nJr=71bF+0viLAQ5f+x}I zrZnn#_qM$dk`u*3j5wCR<|uIUj{rA)02rDoDBpIG4+~Hixp4ARqVI~^&|DL#i{E1>oH6V2mP!OcC3i){wL&8Rs!n}K2$4?X7e8?Lm&(<=Ly?vq+E>DPDd%z>1>L#8 z{>pNlq}gf7WAiW6)F{8^s^3U;lT_K57oQUYPa~{AcH-L8z~lc4SU8zi08+$(b?^%$ zK|tpy>KmA;WRvZF-)n$3|6^cm5H_;hq2Mv_?5_fAmlKo1HZ*Ib9QkOHkZu7v9UJN< z4lKfRzlj4DMQBXVqP71J8q>32t)bdqSwN3bL?Bcl4`e?#b1yKm$B4&(we$6n6x#?a ziV%&Cp)oUuXlNK{Z-H-a=1qYWnN!qQW}BOdl**zS)vYFwmo+0M$OFcN<-zd}_>{Ka z)#Z;%j{%SVTi}J?6PxYE_P$QnOXtJ1fI7dou711qrd>4GVpaPDK)5iQxkbOWwF;a~ z_D69k`HbUiZzA1gpeA>@{-*7Q1J|Af4!t?-M^_3A7@h}a?gkFM1DL%NXp8_GH!`;| zw)!`ZUAYi*ZCjjx-h*20t-t1iwg>>5OThAZVDg9`iC~+$uP1vR5adDOzI3()397=? zEVt@*5D@c=vBP3PXsLqt5m5u!doS?HXN5~Cb6!wH8kqB*L? zeTph$2lUV8BdnYUp8L-LZ)cVUIoCz1Clrp{=H!6(EoRn|C6>P9Z;kkk zU;L3nzohHoD;CPols0KgjMUe0a+-vVC#UEqb^PB!$CNB$Yv0E;gPfm_`RM&?y?tc}YoKTdCxahnYdqcJ&+ z#>AwUIBaZaUIL_CD^^S7d7_4zkf#Xbn>N=E=! zo&lE56K$D0k5ymzrW~e~k3*kprE@N1ZD?x+SicJFdtI$>WcPpp!?VEb-N4~@0CV?< zjkGrw1VJRf2lcN{U(JcXs;;1d4lkfyFu8wEF3dbO{&FL1a}`*86__|wU77jHXTi2s zjJ};IW2$-%L*{0VZ-~+Tx7NSNUk`>AMd!9`#8C$X2JbZ7V(Kk{1kpb*>Z+yGNL9orgC4T z|CQhA@Dy;zPXcfF2f+T+VhkfulcJ{6cmURxSX~{hHV)nOZ??b7NG| z`XE>)1at*zU(}lHcWHJu)~^7k{vh86kgEfbojX_GAPu8@@p8=-p1R_RR)laPO!K8_ zTixPgEl?_sJxsVMnng6GY>FJPdr-8>#6=`b-A*rrU=3jEHsJb`+4tshRYc{vgT`kz zG>qon18C0gMSJ0faBsA^q11<=&;~NmQ0=4S2Ff{$sukVw|p2_ zItg5O1i1JeVCjO0BHb5JAo_RtF4gxNFbJsd9@I8xVsi<&@k;fLm7AmB>kDMxr0rWt z^;6n2F)?mR#Idr`a+H0)(lyss8)!5{mv-$*fVZLT&$%72_g>}s^*-0!D^jbmx-1MT z#8Kx=#qNz9->S7gF?E7I_rRG}CFFFHxn(89^CxaA<^CR{v%noc4&3@dvERW!!l#Y~ zKn`7a4!HaXQkgrpcs9^kf$XnBOr>p$+^5ObM**C&6b0z`wYo7X6PJeD#;lIQo9R&I zs;crPl~v7E3Ec6$nVpgSdK|}K%7CDf32En|4k)IOYAUvEb$0y;VDAqABQ_JbU4{!^ z6P5>?YlL}_(pUkSG%)I$+UFn$qX9jEDzu*aCUD~2{VvfCMADXD`nWJ-wwe{H{t1=y zShqgVQZ@*J?2uOWbJGW+8N)+~UmRAIT(E>i42`Mj;G$?Zz*m7;bx+vjDc-#6cNifx2fqA!?v8Gl#~^9OC6A#5b-XUR}u+)kKj(93+3^L}|fefXnBW z8=A&I%rd{tECO!+3BR&f_0at!61ecQ{_^hw*I!mPsv~4T069}}p7iJG)I#kS?Td*> zA_1tEOz4XHl^(t;Qz1y5tIxT19k~7qFmn^I@TypOtj442h`3(3AA_x&E(PWbzaut7 z4f`R~hl%6jcgu%>rPILqM}Ujp1{Tf~6HaP)z<{8O?}Kx(Lb@tOX2pi=GA%%i4@tYv zk&J1+$+!t1vrtmU2BwUs$(SRRvmDts8!_P%ec^dv`i^?*=H=fK)|+fdtQwC-fM^(q zhLYci@Mqs>iSXtq&=>=n!{XN%5&Ke6h}*!)FF;$M2}MT&Rp*(%jx?6?9`zSXzv~*~ zm}T*DQ)Nlhe{X|=IA!{-C=xoxNLq|W;!mTYT+he?tBYjDbUozg9Qdst0&e;G^KG_`3cvDNUd~;^#l-U1Cn63l}&b{2-*ut2PyfnNfZ7U z!qlGw&2eDl0CqjLmW2}V%Hu%0oi$D#>kT(;yz)HH{BeUt&a>(ERZRf_Rgjz9%5$mA zdFtb07R{*{#gZ=cg#i}GMInu0pfx4@skSD8=A>{_6t!Ueexep| z=sm#3bzo}|*jxlQ7KMNG_PSh^kwm82m8GpY!?q8i@o_{G6X5G>h;Lj+duahNk2CZ1 z#0?9Bvbro4+M|?1$OK&m9C!;bakzY8NIi5fCvXXIaPs%X%2ZSKOJzW2UHQcmB@j<0 zX`JU}LakpX7dKgyn!AWda9SbA1yx1X2ND;)D>hTU{G?9iQ*l9%`Np|e;FYXWSLS{k z+?-wNe^YCfMkFFb-I11c)6u9+Af#oy6x$gkyzX{wpWms0_=Wn8m zDv#kKv(Wo;q-{L=ykuV`N#*29ta!N4JW3>PU^vfQ zl0=cP`imkF7A514)Z<)vyj}v~;@5$5U)B|ong@p1;zGrU0|+H@V_5u}qoS%8&w#Hj zX!Rr8p1IJ{yiUHuS_lKnH{fYmz;hN(K4n7jJw^9L5m>Vc)@Xn=nm{8GBtX;f$H;t=s^L6oqPi|p(Kw_>;-Fx`mRW97PAWq{I2ZbJBeVlTfiW~S!L*F|f@h(# zL5Y4K**P$?5oH+}!h|Zw(!qK$&CZ7soVhhl3dw$}mI$_$Hk<=f_n_Vh*fzw%^S}!q z1J-Zk2SRgzbXgs6j-rW25I6%mfseUAag*>1`}KbY-1!mUrXNJu_ryfScDcC#oca`P zaiFeFHCL~?`eYd6K@o9a6L9-V9MD8UG&+XH^sMm39dRQ7BD zw|=Cv!j#fO*P*oLz?JU-CqE4=oHXP;rT@9AOlN%Gf_`8ZX~olGPU&p;N%PbpaF z+Q{}yo6EqnpS0J3kw=+xGdx{sY;#>?V(kj>+{b{se!;7*0xDwsFmUT(;ZkGyJaFz? z!0E35i)WpEdT?XZOLShx&b}nKxRi;-2vspUC;m$1F|+7xxS$qOmqw~SM;1wrpMcmi zEnU@}q%B<`#`arV~3S@2Ph&S5@ zWSYMrSqzqu9h}FRWdujYOd~mF$gC6OGO?(ke}B{WV%H+x0?zyqaOR7m5w?|$NaQ9Z zxeZTMQo&9hP#Y)9^IV*OfC?yOQqKWwZ30jI25|Oa;J&{MOr7ZWBMbmKY=f_S0@z44 zQA>qrWQwJ9BW2l7$Yb%B8+}K*iX}-eGwCf*(_iZ8*9ep=2woj>;4wRy~jmq`OaSI(=7 z_us7ZN9MUpe)j6r2D|qH^o+tYtq&kKua}gLN zmIsnhSiS@-y$Vbm>8v8#>%f!$4p?8Ii31yHU@o+j^=n>(c&^kdLpn^V1iUh4Dy#Z= zq2*Yw2m@cTz>g?k%_dlD2yCb&{x(}+trnuzkiO3;{IjVYjM?*vn!sIuvjlliOM#R> zEFVNwkk+RsGwOlW!6oIo_*G!-vaWNCd7`W2&^H-3^^?ZlTv{C%B!kix^=j^fRIqvE zvE1zQE|`T-;B-8hd9w`LKqo}KqkLT1N#$G`5jBxb2#moim#3*%c9e;9qsBAyx71u# z83f7AFhIYMSeg7a#({~Ouq&~45qRmpWg8{O#*eZ-0O*ElArH7E+&m5ls0)Rg4RS7A zGyLiQ3%L0Qf!jVLOiOnaYHEAe9tEyF2CKyZUbVLy_J&S017FbyVzi}N{Q~bws zo=NVqk82DAx0`z()IcOquqNx7-`XYM*?$i#zN!!h#LCK?3}`4#G#QX-=iE{*P5v7c zi-c~ZSNL3N*0k(mBN0R~3y1`-M=mA~sIsUVCwp0e+BQ*@=P!RsEb5pzQA=$Hcz`kB z$oBzLM}RN>6QI4>?-K12^kDvLYnN(UwC#Vu+rX7)gv&G50OoE3=I+SPl~@Ff%m6Ei zk`FxBzE%B=xj8og@R&nY>16f8^7|l|7ELKJ$$(4W>zqJ%_M^b^SvP5*5(BpJk{kMx zII}$9vFWKnXXmVYzsdA$*J{6Blz*9Y05&v)XlMv*Xc%m02&^^aPZA6=CXNCl`}$o9 z2MpqnBpRdw-5?p%uzq0T7DHD+{G1jDYFyrraGh zwFm`lgEhkv)(IR^5H%jaQezr1P?)Eqrp^A!VWFTMtAk>C-TZhA%-)CECw1Eam%j;| z`dwgaJ@Hx2GvDRhSZ$_G94!NCS@Iwtpf6B`PqcxT{s_469pIjy1@_$E@6z?60_sa@ zYXvy@$-F>n5(kvBKno{G1q=dlP@P<0jfQYjG(DSb$Q2M!6#sP~+H&=^_%0@Vc!CdO{?S4{a%qom>#b%o7P&b=dFo4KKQ8 zma|RqTWUyg;{o-tql_8ircZa zDJ_@dPs$`HWpB6jO=_b^j8lvOh%%Se7asv`dVlqLJNp&j{MYluxk(zB*V6fnIbN!I zlkB~yUOWI~d%hXha(lmVbF76v?uh`$^^$~v4_QDF1&s#S@Gzp`k(4ZmhK9Q|mi7oa z{i1@ktHRYzw4E1;u>xxbgB#-``;@z*ruqRp*@hae6Exw}YYzfKF!stbe_xt+u`@6w4Oa(i zeq0=weGPUM+AF}RPXbpTqfOJT8|J|}s8%5l0+|p{H*6DWt;?W%@vdG0zVk1DgAW0B z{#5r|h4i;}>Q8dvX<%z9NnYJpogfZ!N+_eRmVF8IO~my0#IsZ-4t%T&eC{*OS*wNS z%p4k1)7|&W8L+cq%1K+xz_~902j3w!;;RE&Duk!ocU6`!W5A1JepY=4(}2%D-yAVvOBy z!fya~{5)!J8v$-?ZRCO&V`0A55&BFT!01y<5<#^d7>5ClO|K)ONT+t2ZS|A2#;+55iNx~}sC z-o*_vXqFMyrgeY&(a7JS#j$b=wp&Bw{`gHz!&^7WFGS3YM+lcx8DkeVawSj_fbTTZ~o)We^n?FDSRO-ByyOQ)bn8MgpitC_q!U$&rgCKAp zgRynqK+Cj^x?#uoskqZIm&vv+6^BJ}JMST=P$Yp9MUY%GOhJePOkcgCGjFq zBtjN`!A!7H%UD0nV)Z(c-eiy=QuE!3{g^<-GU)m*xNw~*J|>n5K|@D6k*&{Oq-O}e zLAR9QvmeZ6-&kf%bB+8ZDyrLI&eo)XlBfm zFK2(Zsj?fp1hjq4f?AG{@*Dj{EaR*~sKp;xHfAMzbZ3y6d1WA$A-vVFvQ_mLqfs!F zwfeV3Eo-E23fMk80v=J*e$1k0-X?$1Ufe&AMI2z8NTq+uj_?!*XDpnYf5`h~(R;2+ zmsE^&z~eD(%l#9l-Lh25w}-JOg(FGca5VAj2H%|Sc1ZY%4YJ#~LSmFs7Nr{fWEpQD zBXNGwo)wXcBIc4>4E3vx`rDHje)cX}CE%F$U*8EQjh>PM*^RLFxYMsBJxh!G73?}bF9qjI* zZ=%z#$;XPnl2rnF^?UEuCE#Nwn(zlG?SFReo>Cq(fvQIQx9hvRf~S`%@C!U}5U`bc zU9@X_g?|FOa-kM z@gtL*IA9Paw6M*c7qmBsLLH1YOa37@FifkWx!Zn55`lZQricT5&G+M}5Z?Q`JdU>e z4VJkv9}5axfl;;9Yo5_aM(k{ITh`&TiW-P&rt&BY>GovK@tw|PhH8zF*4DCMtWAg; z3a9m*3O6gR<&vn!%`n>hv*# z`B&KD7fr9VcO5cLKE(9k(dJD-i?7Sf`|j!FoC)bYQu;$Pviikx z!J8(rT(U-Yc-v}VAHMRx{DE%fM&xucV~ z8t#d7j3S_302#P>hU^}LQy=3`*Xdx{mfk%|oZbok^KW1mk-2MV4gPqfo7x_oF{mKR zJC4r0G=1_$A}-V<>vf9pP@O>d55?sS08>)XZhnrR*&P7m&|qKKo)jKkFlPe!HS(m| z$;@}ey^2L9SC{#-BrT>@#f^74uEY<=`Ge!^7aGMbV&y+Z;(h)w@B{*qauDiBKJR}K za>5HIDncb~>!|oYE~;YWbd!{`lJ7+rSHpREc+W%f0`zj2RX>NLqZ5#(q+o4Ag>PMO z|A0lw;8->g+=1%8T5eh}*#Pr5boMy6p_+z2QrF0(5(QIEoBxLL&MDX;d)b2;igYZ= zL;eQjNSVAq))?~R#PDFL)Fw`END9w+SPbT6{DgiIw>g+(jNyAR_wQogr$yzU4%=bd z|EJ@`qHo@RM(8o|{YD(AXMqioNHXU@my6rA)OwPQvouikKxX?REb~tW_b+4u=k`U> zdF}-PvrFlb1M3o{m`qDRXWU+wxTB!HsG{~OVOO^unu#)0IU1`ZKkw)BWqbzYG?2@N zgOq-p^bIZk=<%&QrMJeTs;yl6`EQwkzhU*{+z9{FQUB=gDupA(dWr#5{L7t`#k$_tUrOs+kKe?>YuR?98Z7_l{#I_Nt- z?gAJfS9(13>+s(176`eaqmlT%tpnucW)Bfdz}&ur2iHgg6yn7E^Ii;4IaM$TpcVam zftdJmJ}l2Wz|;_$l4!$Hhvd4I`SM@9~S`IY_q&@ux_g4KVY00cs) zEx0N-NDseqV_VVd2v`)OVo|PU6j{x^mbU_+SClOz*_a{et?H-@8ix_gS26j2o!C=9 zI9&jB8A1$4E3<<}3u6osB8=|D}v(@(#_o5V2ZbA-|AJ_clM^}tCD%Vjw zpC{2T;tG8B1vbJ4IailrnoF4>(ZZ)I7f5%3m3g z)&Qr9oGh#$<^#uxY)Pg8-c=u((3-B-UVYe7cR*ey_ncbMrs6BuvH~7WKK2!7O|8zH zcAE!Bb29c{uMYv__z)QO35xr1j$N#P``wad2BP<>DKX}e?OHnF{ zS`x@y{G=lwYAYcNYC?G(X3C7JyCb9S^(DbZl9cFA_fq*qT;CcxD#X2AD6*sZd~0^b zXz*3@QzvzZE`O;tdmwTbcSj0)3=fc(||gsZ`VcKBmB`5{-A_*hC-r3GVyDRW-o{Ej6V@-X%1JSsy* zLid30c8p~s!WUc*NrSPTV;R4~Sosxja|Ow|oW%|#y7FKjf1jUxXA^qsf&9+S4NxJ9 z!Ln@hcLQd(g*`7HEOX`;26xX=N1*ADoGc^a8%C;^5;kq@Ih-ZY++$ZHam8{*2bIf= za~k8)1kiMJY}GZ=4}N0bD+%^&WqV)Zb=xd2VxCSvlbG7H5}}#xaQx=zJZdo~{i_20Ss$*11MDxOgvqHUEy({CbXbB|_v_>G)9s93q_RYM^`UeB<7yTg z`b`INJpQab7j#AZISdY7N3(no&xt5nA{dZncOB%-a>pS?d{p93=Vsgh7rB zxhTfudo5DpVZF(>cWIgc+lPvmoWmah6|X1C4%YTnNOF3K%phO&DllQe1;fx6b6-H~xoM`^Jp4jp#}ypLMekiIAGKiT~3>U{W^f!+;uj=ztC zFs33z0K(W_6J|_kzU=UH;P1hx2$z{*~g&Y_Hq!1KwI{h)qCrdG&{i{A#r?cUA4WRK*C zqDQ0C|I}BhQ1me-id0skF<}Y?uHEF!=3HV`>amC+4Tgp%DNfX;|Ykrt)lCu*2&ly0W@DFY$5k6Fy_6gFk+1$5d` z<`D+7h8K*DHI;D~J^>iLSR%vnh`Fz>_f`gLO4`3aBr~9`AIoCcV<(F!%62uQzkXza z{w9I$Gkb55$djHHK#uih? zzliov#ed0wO)OE!#VgciOK==qrxFPU?U25&`uQBC4nu+ z+T#{#|E`$&QmQkbM}Gg_-pHp9weQjf-uoO#$1N9x)UgP2lEp3h*Ye|n`RG9_U*j=< zxa7pN>D}Dtz5U&A-gj>eu)dvFz`LJeI%Xab;PxB5YIKaRIC@gryKb1xrP&6opA5_8 zSa7!mkm{_!4Z#0zT}gH(DH6Tt-uG#_OJH5sN@8*_t{s$SM@)bu`c~O~&3nj0kAf@O zQK(svT*b~^YFR8Ql}eXT|Jsk&IX%=ocRaSr`qd8i>3JlwvJ4LOtvvKv9ww6Hi-!bT zq~^b%_r7}dIf~#ugb6e<3ZK)1IzqTp=i=?45j3V2N_`!P!T(_qd*t(8Ty7z0;g5SS zNz3f-CL<@J$Cif+gW$nkLnOkEa9s{?R&STD$D9E=*{dB=p%>bR9pdbTC&x^L|4Np29#ZNY?(iIO*Fh&CcrD5GlftaQ1v7sLJI(i_) z3JY|{2zsS?E{W!Djrwy1rNIi-MqRYcQ&`Xh+xMJfwAwF{l^A~WOw(MF!^4MU-7yG5$%tR5!|C-pqEmSXy0$@NRi{G?_=Y?c5XcutaO2km9ddk&F88) zpN%Tnu37W4!+*lkQM$$$lAV(k8Ug%nb~<4e&%Pg=R<63viBGp9k#qSN8ewBIXpgJ2 z$IR}L-(YPG#*G3mR+(|wJ74~C7$ZPq#mLc_VHOgm+rh|3YPfM*VdVc#DF2nu)c0uW zvoIXMJZ$t1el(>bzW`+kr;n~uqXqcV+u)6WH0)l#%)q$zzLI*@E}s(s&@Rr&TK1mC z;DeVLQl19vwZ7<;HWs}oMfW%xLv-MWU#L_41kg__{rF+OFf1QPVI7_!D1R0|@7j#O zG6rswD39*Lp9A|lJKgLpDRTE+C6i>Q{>f|j}gWCJ5X`G^y zh~*740ObIR#e%BA`SwY~ca<+^ ztB-9_y(0CUTn2$Hu^)aby^=)UW!9676PLYvK-|89`VXG+4V-f}aYt68n&CHcCF1Uo ziNK{FJ~{5e50?;Rp7EFO(V;$~q~n8o1E}u*{5C#CvRmq}U^P%%*r$K-v(>39LX8rb z%Q40%Vv8i~)**yguHVq-==eJ9gEG>SUiQhlNhJz-fL<}h=m*Cq6A4hLd}%s$FBeWYr_e=iO%61 z(Gww3cP!vbhtRapwE82%d+2x`T3jSzpi+VSE>K3xP6z30N}16QR;*6t<)JTAl=90n zaE^$Yk9#j3aexI=ol|c&Hn0~knOdv)qQU9{O~l1SyKWz?iPX$lwV^jE_yEtPKyig`nCfr?qZzTZNZ^&t zLNcXLh$1)lvS7XB!)LxO&x-Y_EYJvs8f6Wha~x5Yl1Wj6`$!RZ5E>f?JH--i{XtsE>u_xv#+UZt z>$eSywpDv7lfahBs1$C1bZO+YWP>#Dgy(OIWEsF*P_Xhp}<)e%nE+lMUDM!9MTuCfx<{07P^nro0`+GCx&PH=-XmekcD;#{eQ z*LnOtz?815sehMNn%AxKa72KvGl*^^Zdf_A@)w&tdftxr7Y3`RVN7|Xc|wMtf3rF4 zu+w><4xDfVx%U>a_njGZhQ`c#{dOH**%~;6M9+;c=!;;+nR1#w@+r+=&XzO|>oTjZ zo0AikfsYKMl+T?Z&o}=C{Q$1=0Ty9`(v&r!u_>q zJn(Kzhz)O2V-$)gnDMh-V^Pq`gM}EA{e}CUE8xak_d+&D&VF%oOub#4bR zJ&(wSoUHPh*%>74VR|)kmtfWtzR^?oD8jS(Iy%I0+?Di7%I=n%Nc4Nj-`}0cVJBR# zb7{cu1>W5!4=f?eHNMD-#F0m|$>@H5Ol-A+NPowy>Y6$bTlV>?W=Hh~E82i%laQ7=7)AQr~X&-dPY0>XxOhOL94Q3Sa8d5CSg0Ts<6dxAkfsM?BJoT>1PI1iUi|RS)^a${_-45G-}! z)4T#aAMC9t8lnphJ?Q@8euIS?mptB0;p9&4%_Oi>^Rjfs6Io=9C&;S}Xkw)2Vz}h% z>nkR(?NKZNlcGU2=F9p>oW6gt;gY&PmA^iwt5Mg)sC4My>s$wHNkk;967kybx!&75 zc;o(;^Scx-2)izC50a()4~|jB@3hb2E?5?7ZM568HX{blA%h>{fy2S%66lJbL~;Ns z*{n5EuFkh4YxYjPcQAn_M=W6eNC{-5*UqN6Dc{>c>vP8%*S`AGRG>x`7e9pRg-lpF z#i1&cUCGY{pc&-a86+BmmM_>>la~vMHM2fuGh(6MeHun$e0;z14Ef8O7vNC$!oU7@ zKTb}uQX)P2Rfq4|RsZ+0?I4UlG7q%aRV)f8lUYJ#CR3J``q94(mtiikrRMZCplVgQ z=iX+dzEeHuvFFC0Gz_KnK)@dcAHIE6w$OK97ti(Jx>VXwRcpqHR4_Bw!;R~>H(OMU zLyCs{6Wn2RCPg|?9!myy(@7ApQqq1k4X2s`WAP>7%v5`pnXKoJ_EN|gqaTRK#TbMs zRpv5jo?v;%@6vZ$mib?cJ zMwNk*+zCP!8qcn`lh;yX2QR-O=MgmA%p5SUS>Ac&J7qUzb`03KNhMv$^VJjfMVRG|(~BA=bqs&0Yg1I^J)9|*ArklAciAA4MHS#} zyO`N&LMc@R3rYFyky*@_m8@$~3bV4!<>jGJVS2hlFl7YGYNz!dI6gjb7trE%7{fJ6 z&>A9e@*;%zAB;;S5@0A{>o}HA5d6l2-^z5h5o`;RT=I1-%3$|!(A=#e<%pOlAr|V{ z*5RSLb(Pc+KMH$`uJ?ye7>6;eZ&n1ljn{}V=9H@NMCa{Her2D_+CYvebQaSs`-d|l zvx_G2o!H`&Ti63c1+k|765l8peTtEt5_iTMvMatJMCb`P*CO$o2tNh)NOW z>d+F0K7;gG7tfP<0$Ba*1J*w9O!f=JD-aS=U4vn7-cU73LJj14iQRV3?u|BfbmI97 zAD9GBv^FMghSL%c83_ZUW22^{5Jl|zCyGS3In6OlOsLyF>dfqWq8gq1E=~I`(BCg^ zuWAvy#-w}q8bH~Q;j|+XGx_Ku@@u7E!WBGPcw;$k{98#sGx}5np8g?tC(=w_x(H?2#Sfs%WS_hs;eD-$=|R?jv3v3mMm{Q_^9hcuOuQoiI7uBuX$qjriJa#i3Q8(_Uz z6TiHtp2a|Y^Hny8Pe@dc7fQ8?QMfcs>FwWP6fSMHRVr?+$Ynd95Vs;a-{Qw&DFL*| zANyGE8+(swO%2xhC3f>AnZ9T9ndMjE+#lErN-RAsCNPdP{ro=scRk^#U|?LZx^air zpyaeiRF!vCsyp|lDFua8L;8oPQSRt{)oVxb?5CK+QQzc@6WK&0d&j!vkAbZB@1}tS zi^2(djC}Aj+AmKaU+-;qm(w7Cwl$%sLR*{2{k7Gn#Mc33`?G!MGz3`j3~kD} z6#^FYctK_m)aK0If<|Y@N%DpP5B6@o*j*9s@tq>tm*)fh$mN!BvcpY{IT2MpzW|P? z01h_~%%&gY?wNQfrj2ZK`+)fQEW!Ue{xz<_KU1l!E@eUPq3kD+AIqgkxRG1xmly3~z6eHFZnULa%YN4EK%>lqWR1StETv z?<}uvM|A)!cNBnO(B%IqZ^K18OPKPWy3H(G6yishJ&T8jBZ2hD~ z`PV<+wfDWVPZ}xS7g@FR+E7#q)<~BhFT9U{n%qys0vIuMlY|o+By`1{%lurzkQQ#f>81Z#H5rm;ZVGBu(dv{AR>)o}Yr3b^cZYz&bQ< z5VqC04ZKp?-)F770d<-yG!_{ivkkKaV2Coa$NDqetsDYQ5lZ-7&jC6RRHvi`t)uPtMBnG{DYn1)3{Ck^tbN1 zNR~Rh?cFh#!8%hqd&STRc4x&X-)MbL9Y%)kDgq40cX~HjAuk%-e5luvDQ2p4gV^5tA;Uu(1xs$aUdv|Dz{(=D7BP_U8HFHFd z0|deT19h=Qhn0K8GUnr-W}!(`uk97^CbI`A3nmL7Ss;d(5S#B23;l?xUf8bYpxXyo zXDy_M0Xp#;Gav17!U5itP#`%(J~zwHFAYZ8IN||l^ihVUR!V&Xx!FeD2V>bbD zS5Yl!3Qy%%Bk(qDe!k#L+}H;;_6gR-*{zf*fYMY(7)V$~FQ$t^$nZ_rD?ph0-rn2t{6e;2sZEYBw-&U6LR5Am;k#>M?`!yYk}W|v-UK~ zCOx$2xBgS_DpfZtd!-3E;cr7~8?3mssE^DFbNxyuzy3*zB8!S5*Kq^O`brIi)3X;s z@;MSw_r<81du7PXfqOnDIa6G>BHhp|v%A6GY`qR2!|X0T1f;~gy?IE~U-Z4%Y*h3{ zNolFx#q?Lc$S+S;BFy4es=VGov>D+I({R+_<3SKt;&-?UaF9U0bL-Ao$Pc%SHLO94 zpwyi&rKD)`PG4&&iyj=8&;n8e6&^#j*mq zln^a&Q$B)q5xCJw#Exte1AWMo8)AMa`%A_#yQ_kj?$L6JV@3kX6z*h!-`~B?d`d;| zGza_FMHXcLMv8g==DO?0@4zs9pD;2^Oi7gg^ZoTf;_fwjyK}}E0j6?!obw_IOZJ{8 zHU1$=DN31ctUvt(6`Rbs!>a!mLu~4By9khoh_M%01c@8MB?76K5VMG?>VO_aRo$CH zc*hFE_meV`AuC?^VL43rhrqDhr`900kQGV>k_w_0Ses(}&t+?LO9moE&9fvx_@(+= zeoe8p-*_t;+fqSz_ZW#47lhULZ5UZD3uDm_|Hgg~E{^^~@05W6>4o-kc;fN;2Rm(o z#5!qqMkIEp6y1@QPntUYqJoe>XKY}KJs3)H`*$7r*Vzob|K>-D*YEGcv3gOadbFSq zt7!I#^z)4Jm!DZPkR&DK2(d{uP@_=j>j{gn(!xnu1;WD0e^D7do5gTzHG1FfS)oR- z5OL4^!~l#k5n6b~c5(z>AfQMtet|j{$Z@0SNM33a#2y~;^-!r~G}lUza`x)L z-Y6nD-)YA#5acN;5G0g_zD!S|mOvA2yywElur(sv8Rex`j*6-Iqwr5xGZS-Gg5o*IrYVc z>?ioX{b9SY%K z-kUb8x{{JBrYJKqQw0h@NC_emHQ%V@YfC-)#15k%exjn*KB_$!kD5QJtm8^O9nRJj zwT<&O&(WpJF0l^w;Pb^t_d9EA{X}ht!Q^A7F+T;?6CHvNipx*Sb1o%m9Mn)aQ_W38 zmxp#5YW~oYd}e~8rJ*za)_JBw6h5!5Ox1X; zYF=h*{p8O7`8j+DN8x27C=s2X_-4QutK+`%*N1}Fyjm94!_sTU*EdC)NW&j7@9GY8 zII_Aie;5YV4u1F{o88@#&w7s`0-s0d_lR#+Fa&dQ@7@NXwxn9C`Dl#H$NQLfi7g0{F9tY z#m<*9C1ISOZTSN`BY`Dpl^pBdho$$bcuD1TgSn%wuQ|^U5=}n!>+Rnnse{S9kCWuW zmU&ckw|hdkXVB39za8zg2l(oH80&}$HDdc@tnZY`UmBb=T_%OHeP9JW4m-Tsu#~eU zg!CbLkQZf&xLZYVeLj5zkRt*6dp1ur`DnWlt7PB1qT)J%s~a%w0I+V@8y0F3B`6Kj z7_0YWurs^du!N=_`=>YsOI@5qLYa59^_D~&7o?k?AQ^`I6K)L?vt!^adC`(L0?>1Q z{vay;7ifK_6faNt55ADQYfeU&`&pRE9(X{Zn(>C4^pdFzY+Vv`Wxfbevq&w9)NLnI z26sZ%0ILthua~+rp67xWT+wzqM85n_ZOFK_lyv>T7Y zOwlz}?(3*JqzL&MoAFxDi7Go;xD|5oUJT2jsB0{aF-W;`Jkd160ilsvqwt0kbzRax{nS?N8dxo)u%zw>K`QH9uTRPS4>m=`OHaa}RL*5@ z3KgSG7^E>J9wfL) zKOG(2PPhpb#79H!MG=iEBVw`BqZ^>PzqKA(q8#3NUVL-vlR?svQL7n#S9u2-8nCno&_HB9&^%x7?R*hTvmU=FJhiV|_rc zMQOn?8-pCs?Ap+uAr2f0hx*d&6&lZ*oIIrlcfwK&5Uad?Hf_F;;JYov`Q6k?X*@Uk z?Oi6?YhZ}-LwxSg$4xR5V!z6`3G4N0oLfPV@M5?-9wc_%r}&Dm@te(8SrI!dUEQI( zi2|RmvY+b<_qaZb->qF0W0dl!N{p(ig7TS=Z&tlh>pjR7v}S<%`lDPeCPU{G8$)8Si*+PpzWLhO`K|4>CQ*Qtk98RHGZL3v@`ssT z&5O!eYSu9%HD>lmE3=zpK-kS(8{HoSPI7`(UR>`nAD9G~ z7%pSJk_=3iISu7CN7oiJjFTtNdthj)NZWzYqhfrMquJP@U$^FQ)i$E-O3Se-#~xkB zDS9#=X!JP>!S7U>L73W-41F?xNnY?X+S!+|*IEwqM~Jr>!`LD0r+!(3?*f4n+t4WG zFiUHfaRgZg2CKxYs{Q-G6$^v`FyXpIgWJgh3P1GlhxJO*AgO$@(}f7#Oot*X9lxDr z@_e9whO18_T~-%FfYI@Y?~VeVvTId$-`^!{U#Kh&^stW-dWXzN)RT{gdbCD9xD1?y zd4Iw#H%oTJ)LFSPePcknOdcj}C)Gw4S=P(Ng6t(#uu`y>F` z0Q~K5WtUGfFUj;`Mt&&vJY#x!l`ZGxDcjjnZrHwzy*_9BuNg{pID+Pmeb3#pJ%m#> zts7;|gN`{Cr@clqzB%Tdw1cdXb2iRr`yz>K6mcs9eY(E4nY|a0Cog(@(d2JVidhk* z;Xsl4-z_8}55B>dz(9fhe5bBa zjjeyz=DYsO7^Qj!VaIFwf+E7D&DXM#a&n!B$DGW7w19z#18r&@^-tHS00ZR@BTnVz zX*b_nvFdq70x(}VVepv&v=2v<9~yyqwuz$@r$Nl^QiTyxN&tail#&Vu)iY;6t1J}x zowwl`d{Xf!=T^byEZaitFnLAiw~#zd{)-QJWE^6C_AM>Vxv+mn5z?#cciNVwvzphn zS-ylV7lB3NItOV?d?qBBXg-uxw$Qp__*07XJI=>LR-7^6Lq?oSasRpQrGD#eIbNJ& zZLuIX<=o}5n9C*vK!5#AkAtN6-p*NDo2ejgqR$eWzmaqn&(vP-s&`7UHoWT}$Nd}4 z5{w--GG5l-IA55C7O&*!kr1DPK-EtuKTHir6yrtR{_y*?pv4bLA|;E=8GZ%G7Yvj% z;OoC=>#K@k4j5!z$J$Iubr>^6cB?7OdK=FUgb= zuH?0bb`~*#I$V@+*L8BCmI+t@QbG~!lK*BtVf_Lh_7P?t8>#iR_!(Yv4Nr-;d8K#P+?#!g~Fu)#l%4#YJL z5DdigKEmwqv;@UcdSu`sa^u zUF2jHsw?1JI3?bUgR3{{=@_xwiswf}50p)gTz+~BlnQb%GR^#{vd;`W1PiFMA9q~l z_)@79Gr8q-rXs}bAUbSWGKnU^q8R%{501cT%vu=-gyNpP8_LDBPnSDh# zZ;XE1yQ5DShWgh_n%u;@PRcB-P2DLQ@mu4c{i6(2hDUyxlq^khnEe^SpLI?+@p6;a z(n)3uNi@Xc``C)CxUcq7WC)f@%9XXE>62ObM_pg;`i+kuLX5Ips*h(SXeFE+-)rGU zN*eAY5uOxw5e8#>;==c0IeB?4IU&P6{tf0Fj57CEieDxlL%Bs7+mHqo#h<`|`16`^ z2g*4t4ljfLH#4fG@gS`tii$*RbAfUz#^1Fxpxbd9AkVDopw9Zb)?w+1-B+gBn=v3k zaqP&PTrX{_= zmAJ9bf%G`OXX@#Y*pT3Kqz*S@w`jus!ga5y+R86^-VXvI(IdAg5jp_P#N9JRc!)m0 zFC8!IKGyK$W3W_u4{8W64Xrm);VY&cVxYKG^bfnK8D^N;^hdOHP_*0LsdS$tNkGw# z2(aVKu3()`3>SDKGX4n-zHcYDKxi+& zKuA;E&T5^RcmiCSk`}eF5N#yti@Y2XEOTOvcb9G>F0G}5xU6*e z%<5=uymyn@S@hHOUllh;+OsRJAANzSoEC_7ueslG@e&Omd}S*is{J=9e?tv!YZI2U zpYD;hnDZQJY;)vxq~+|Rfc+lzJk~zJX##EaVJYl}I~5kP{2K>dw9<@IIDAUqi~XoJe=x!7Kd`;{bbR0K`2|P}>+zLr4_Os3313sA87q`msI}lRLkn-U{u% zO5A5J@bR5lsf4j^2yJ{^fCkv(yZY<58Sbm1#+_q>G&%}3#w%qHary(!Nf)`yBo{{L z$%t11`)*QwC(%zR1YPke&~>SVxVL zr$)Cwjo)`~P|b9PANVZD2}`E^-ZyLWvi!Vw#kFxHx*$!UNU`1DR8-3we}F#E38ROs z#>j`pnqlo^>=)Ftc*yy=^h0Bkp4Tm?A~K zAn?9A2CnkrmrGEZaA~VQ$8%DHUr<-zw1V+N3?M~f8A}3es?)Wyh7J%cfkv>1b${rD?)=+yoL{DF*>H_Fm z8olrS%VyW=;@iK}3%p9r##qz)X3$X5|6bXcEB{@>a)C=&w-t_2K#-yFR~BO?7pfA@ z37M~`t=Q=D309$)UFUtLB8|33FT4+G5tBA3ys~zBmqNiF*P2UpEP2QopOim(CX<6# zEC)#iUXl0!)bpoj`CD+iaWRuuJK#bO>f!zN7R~1(VzHewb*go{wR>Wly-*w5FnsW( z9pWz!5CqTXuxYSd74<%^9SS$=U%D3KF!B_fUBYc{Ad#?|7#*AOx5>_MjtgUIU#p;^ z9e*USt!PNzB>%y`Hgbjjb^u4}susXy?%yG?f zAMviMv7=$mHF?}kF}>H)r>uA#pT+g6%Ll8CA*e0^g~IGT>ksBMza7S6qWly17oE$2 zA^7eB_7A|e47!ITiX;8S($Lf)>v%1j`k+E@33M2L-=y~_zcsEc!g~C?D*?T8e_A#d ziZUc$=6ek!<1?b={Q3r3v}jqz1L}{1FbZW!-0xZW{XluWBzaP**{#t| zGB-5P$DYhw>phn>Av*;qW`@6y8?+28c(%(j zUCuwl=c|RUHW%nXGtYsGqvP<$gZZfVVK4CXD?s@KopFQRg~880lL(%hxHdn$2K@+C z-Vd^!L};U{I9y=;DE?!g{Nw8oltk%FJLGx|_buac=)9tQR@T^GOaHAb;fUeQZi=T$A%Vf4(N&c z9tJ^z@?3p9Ynd+DoHhb^#*l~%!V1*AQ(=HH=@H^7%N|hnw7@TfD=sd+sd#O$=RrNO zn!0!Mk~}z@)5|Ib?qvRJ|LuNQl3MiYzlJ~gt_(QYZoGGWM71D`V2n&gN*c1Q#oLO? z5?u5@U`<0Ac+!NQ(WHDI1QmMppuT%FE4iNj=z1i0*`Tmb9t&T--IjZ2_|M7A_ zjn8!O?w`7pQWGml`(&DjNa!O5#aV?Hr0lyMQCc2Zgce&hT`#f-)GF3Zqkg{r(XZzF z9nuSgIB(mEW6qYs=wgN#{kskJgERFF?l5`Wxo^g*Tro?1u;~d&6+B#&*B_E{e59s!)S}0#TR51>4^jL8U|?=!fX01JhD`{$2KNv-zaJNW zAd2aWcXwAtk*?o*?WFLK{}N}PRH@`{*&h=ep~b%N#o@6rt(S>2xy5pJ)5ynbo5I?#F}YG*(y_mhF<*Cu^OGji z4fO6j%O$MhcaFB2_$}BQ>A{B2lHULQ!%xy*8jPdGXk)vn>f&i%6!-%Ms(3J&>hK|i zYfTErqOYLSb}-;8bJY?Ph2{dq;sY)WW>jNajzj=#sjbQhQNZKW4neu3sZxqc$it$E z{>Sxs7zt)5%Xy>lqiazT^n2wqBL5lJSDaFg2dj(>1KLbCu!KBk*TI;% z9&OeybFFHte!1B2g>&m*YVm8*SFV;XzahcuaW*cOvVDI(k;dLhMdH+WFzWkBa1lZz zf|NSO+rI^-cWG&?k*f`52%O1;!Rtjf<{~jH?;ET|@1|O03>mYzs^XH!U|(iHd~C<5 ztZB#($7t8^^>{h;RhR3+EEJ8VbkuGH^?Xa8~Ry|)~;LQ#^B zwZ>wlkpKy`1K{|E)d#$Y_$hgo(u*S?E~%!p>^Oyl=oF20h&nhP`zS9$A@=4T>oVK7OZStX8oBE)F)yF9H^vJlGGIG0%Ii1)IV# z#Gj0zo29SoU7!7SUqR>XE&!l=D{iaCogo8u-OUc$@2FBm$2DT5avFiF#`3ne6(uiV z0f+fZjo|e=hm8@gIB$6~2Hh!1g1{rxv#|S4)QiDAUg)zM{IQWJS7#e?p=av@{dAiV z9ul|#jEG^ic#Yo%P4wy(@1}D&{PMe$?4ZtKy;uXa^OBFe>c7^>3+3$GL1!zRq77Ec zVJ-qX%+5(M+d>HO#st;U^HWyr1;H>=6n5Y4j~nyFCCnJ{qLvTE7>+Nx(WYeIh7(_m<_4{{UeUW7HKQPqYb+22MTwRmb+tiT@&4=yE}!YLpqm6Ktj3~0V$F0S{i8;B&0(SDV5Hp1OWk&kZzD}kpA}l{)6Xt zpL=KKoH=tw7l7gs0Oa2Vq1)xb^T!HiU&LaHo-YnhVn;i@Y5z?<;8$9Fd>v^rgQc+eAyiZ74TsQ} zTNQAt6E7zSviv~en1m<1vb@{pc3ob(cf!}YdpA?PUlbnN-#w+4K_ISIS2X3*KIms? zP%ZVHl_~7faEPh4T`$0=#UYRa<23MmeOV7vHT(zn%@f3ho2}EYT&oi&q9x?kBhDA4 z=IOrN{>(GNNw=UUae}|jh%<*=(m%>*cub_@S!9rnODMap?In)j*Z9I^hp#e#Hla4) zS9^VPtxZi{viodGdNrMcApT_MU-oAi`&Ft`oQ^42B4$z9>&-KKTHCH!W+a``Bk;>G z;Em`YuFai>-%#+zD9TvAaF+_>(joU@=o+o&ut8TAhb+4u3mOwJnCZ|3y^7WtC;9QS0n<6n z#n($}#s7p{+%10`WY!z>cdUpiZ=&isTb?NUD4KXbyGK+*b>W{2_E#@S>th7l4w+*%FOa9R6fgNL zu->i3aO|4T2Z09MnqIy>ob_&YXM6F%bICqHx0@Sy9b6=Cr}%5m<7$%YxLpDV>SMK` z{`;0=xP6JR{mWk)U%8?}&x3@QgJfPLCYFDpdb(WsMgLU1VsI}5__T`F2pOGpjs1aH z=)2#~tKR=14seE5{yM8EL!tH|hKFj=pTg!`Wf^VJ&Qv&2L)KvJuIDNVE-D7uL-!3=PU75N(MCmhbY56BZ?Z<7`Ubwj zsHo|<%8qT7fI8W7AKnq1g6+%#{z~*lmiNK{%^6^~#@~V*;(D7;7PqZG>U7k#lcu!^ z$Por#yMZ3UF7BnRd^e`Kv`@+@F8N{)9Zyr%NPIqS&mr(*h;CH&DksEuaUm%%j2zd* z(8Lg4@M}RD;}iK8R)HI>2rM^q32~_kJh?#`2a}n=?l-qgj!>r(>X*?7t;+OIWfA-t zVFxS)plwc0fFO4*KpeX3!yBWcL54q`zP|*OyWB-*83VVhjrRz;f9>3%>vaS}SZBhx zi3fzW_Dme3T%fsT(O(E98c~H*a(&^he9Vu=oLNpG0Ks{%l-HwIJj-(V4}8nE@_4-= zFN8Tgu=U;8oId{A9Siz)>e!JO?u+&OoV>O!$8=!d0^Cpo5@^>?H*cac2?3deK>k*9 z3*=ITk~Q5M2>FkV9kaf=y(pce_&&gq0ri;lt4^F&o(U>K5T z$f+50i4|lzv6!cV72F6+La={xewTQ;`qUl%B(p8`NF}{mc9tOVDw{0=8r_%7?YKNd zq%M8}8XrR82=iLw7PL3TU5s;^d7bv%{nk}dOIJ)ReJKFR{b>JS221 zuKZjIn_Clz`zgZMY|tvwINd#(XAswvQ?d)xN+>Z@iqBw*Fb&dI=hAOHoS0)J@KN5$ zV6@i^qw`;qhvbDZ9mQbG7cB=)4muz*m#+pSMlv;dZb@A}brXQz>!!(83m1Tk1E&3Go>+G&S02IA>=s_>07@{zX|dx3qN43?R-*^x9MknS|9lt&;C{o z3*TV74doWAG^9PsQIvxffy%Zk#*TaEwaW54rhedR7l?)Ly@ zM{W>di!79$mzR`EW4FWm&aV^ur3^UujSag=VN{7EG`m>-QZ(w8KKsK5hWLA7_R_FD z{;#uq&+xBjxn%4^&eNUA=+~@HlSI6m)azJ>bIkgOcWAHu4hzC=DAx<}RZi)#QW2wk zhGN8mwF)pc0h!)6QmwB9!{7Uzsr?Fw4#>N)o!e^Wu$0%3ze#XYc(U0lNBa`oD3385 zVy``xT<)F*P<6i2*e8)W?1k%TfT8t+!S>#n4Jstb+*gdz2Zndd>VuKymezY$$5p_x; z5rA2N6Jp)5Hs;B)h|RJwso)Wm46ILCqo%EQAm;m7&|Z>Ob-ZZR5ZYZ`9|{6+38N8V z7Nj$*L!Rmuwr}8@uWJQ@<6r0KH50%yDY@R>2J*0;+qpJU$H%>&eCYie+wj4%e4=IZ z^bpJHmiTCF8t9M*z^niXJ$Zl;`o24qb_&V4l}IXZP0NcJLMU_2 zuXeo83LmY1Jfy+sXskt2{yHtJ!)v|e-Op{CyOJ$03$Pbt&%n=LUl;=M=(oNcI$htN zqTX7m(;$S)@CHn{)yN1~OO&F9?}>a-GqeZti~y!9HS}rc+ND@+6&NvoChB#VvKEq^ zZ;TFa02D2C9ENzCvi%$JY9ETdXLuF&2sij9*%d`kt!K5~hR>@6IJ{BeBghOovBEow zU>dpc3{WL6p>w7B**HG7!cJ27tu1C`oEv8T;qOw*r|zuTVDdX7NxVQNBo!_#Ie$wI zecFk}^2v7~N0db@n28u+#3G>nrZwQ*^Kn1+>^JQFtfZzhaT8&qACzd#nf4cSSD4*G z6lvtuNrhKutf2?Ng#PO(%}S4x6U;0p#f7)o+MO7+4vB2VI}J-ra6ZOwC5bQ@>eWsM zCy}(_8~o_OrkzMTXfc8kL2)o~p=3%dGuJjjY+~#Sr|WI+G2s^oD~NXN>fH`)j5NZ9 z|2A8$<$`~C<^f`BsnT+#_;>rJ@Zq9EAZdta_HvS8{JpKrBK=Qd)kj=A=fvlMe=BoA zCB5!`R6Pr>z0%Uu2m6ZaypfaEv}N-3I~lLVBc5gB6jsH%rhpVH)gnS(0%Fud%K^EZ0vR*K)Afz;-biz* zF>02KFXCD}&{d62Z@R{Q>sW31cKI`soa$VO#w>`q=|s=LGWNxnI%LI|)uHSZ9yPgG zpP9Pkbc{(h9v3)^Dgxs9{@U=mxD%)C@(?gB-v!3vncV396?4HZyTsk)Pi)|%Kv)Nsxn7^cwtCx1l{#<{fqoVH1uur-DH=A_G;{~62 zU8UI9QM&O(eUI?{8h&+#koD}lf`+|t!#q23Ij&s%6~LytQ%x_Jk;~@(l0O^ENi)$R z6ZeZsZDfiA=5Bp0t~F}y7vYR^lp|msqNv6%q)(8ilFQfq6Ad9*ooYW17j8w4Ryy%y z?m1B#vHkO6-?Aj3Nj#f%jYFl!qt=cS4auf&tcOHF)sG1Tzzy?h2r!#KZ-LtNF3*`* zrk)EO?=!c^7+PoeBXd~orT$uMQsPDmqP-bkO;Mr6LJ&%$%k;;|* ztk>{gWdJq|Cwyw2-ptDj%?0qt0pa%KG7Q|wsx}Oep9(W7@+|D1<0@YRue%?aYEc!F z|1jTbRlu5ra6sM=D_bLI_VzlS5#d>?1@`pCXC#K(RTrkg};<#T?DjK z;EveyuRAHDra`|QIx?$Oq@N9xI3-9=lO+>@FtEUde;fuJne*H~W9WHm5k7){Q zdR@pHzGNeXaFNUWnt5To`au1^Kj=ueTd2(bfl*zHj(2rFQ2$GvPbw|jegph=%H!Tj zo@A|}uXp(3Q5zIc`Z{UuB@?aYf|TGXtm+>v1HuQHIy;;ZgArIPXhoV6uI>sH4$H=_ z+3k#IzX-FUl3ETXlREzV$+ozp_HgkOU+nM&V}I8!BlDb1Cj*nja`}gVF6r z(i5q;Qtu2QZ*vve^^3tNZBw8JXLN|P$=bsi0gd^v>M#S>8^8F`LKs`m99r!`$bG31 zbQ!wlkFY(su1k@@eRCV=djUg|GuAa+l~gE(Xid|R25^C&BYy&>*-oG*u=Hl ziUsveQ6nd~MR~tPTMJ{fDQCW7`{5rUKnoYI0H>FUQ}10cqls%kqL=NFukuG7Yb&=D z^qRj~;i;f{gvxBcz`E7Raq2d=#x%x6Y5G%xTAvIQ=k`9zhR=|3Eaj{zn{l{hm7nN; z1%iGECP~_>eIKAfDg?f68?8sF%d$BR`#72KJNP{LoHNPnDe$9uBLG>a_Cori$Xt@L z4`yK!-hF-qsycJQqO|ZEspC1+ety7t7)i`ns7`dP*K6%}YQLOts0&bZAT;SPcE#I5 z#UZ0UUxG8_SDRvGej6=;25qTrBVfC~APFa^@H562xFAy7aOhs0n!&0o0u^0H&=a{6 z;mlilFtpC+?@Vf0|n z-TdalO8mXKGLJ(!;K1M_`Yl%Ka`NZ53))d2T;mO@w38c+yn-|aYt^K5F&s9m@KnleQ<8efV#1~ zjrr4b-A8H|QTn}Fl-syH#N90k(2c}uy7yS#QA5$?Lt+1w3~XrHy$&yU|m!0DHHMTehtNqGEW{mwQu2wP~U{Y?|RDdxUBD7m+>AL z$G*qP9U&SU2f#MuE)Ny0B*l0L6n|)9QJcxMvQ7gYL~li)ZKyZ_#hcXPI$qR z=tD#b4b!$!xRljc&&dGj=b3$>!&QIAo+b0!mwoVs4S;epkU%4OW!AE?&7Jo0R>k998w|3~_+TQIvv4;9yvBa;S&6#FE^jJ;A4xC3!`n#MNQA{>R`fPv0=Q zrVFQf6<6f;a}a#KIN58o4}?3|2TJbOSG>S_HRUhUp{8q~Dk$Biy>N#xMD`5i#ZBpW6t=qu&&+ZYQ4vD=z&YK3axo z?eymb?=k{R^On=J*5wVjbh11~p2zZ7Z{#(FSW72VCeO&UE}Ug%ohI(k3Pi;uQ(|m5 z9`8DgnVR=wZp3w?+h=G-v(6wRN4W>yRyyK0vdr}6duDI2HGfUic)-OujeRJMAdBm_ z1*BTY;>pY61g?f_BY0>M2ybvr(sW2WHiR5xB16P$$?Yfm!PgCBEC;fvOCx|u8E5Wwj`2}A(+WUWr1Rx6m)TxZG*vT!H+i*MwpR?tH@br9>3`|w3sffb2 zmhk3-vpYM|5W#@?YgYFg)8Z;&WT#j~=?Jcme>IP$@b#iExzMRruG#dbP8tRBqD^r) zX*i>uY*a}HH=w&L4Ch~azTsi=zDnvhkh=*s`(-i4&HdT=3l|$bAl5`k)803M,o z@E!mDeW%&1T_tI$;n$k@dx(GeS}b)rp2VmrrPL+c5x^WV6)ajf1( zI)&f9ex$VM2*EpEgF4Ls~H>WlBsIV>UK|S-37^6e?tGO9$bk({tadf7EFI7<09w-f5t4#p<^HX zmJ1I72M<%%Z6kXfo2y#=d9|b!r$mngnvcqyyPDzsHk(&LukYq;r%+n?HzP(=ur5@Zaw8Z8=wPR zR#*#h5B|Z4m_>yXBOE9*W%20efrJO|!K4x!Eme+}K-|JhJxEN{d}~$Pw>g!etJz(> zG0`PGCAIg;4a=TwjS2Vr=F}J)dRS*)E*4KpGEhr0qfwit24Wo&@kxYS3y?P1dU#u6 zIoVDY9D+=-o&-QA?cRl{YUSC|J1l~tDG1WV4~LkH=0<$Rr%2M%y)7Cygy^)0LH^ed zuvfAKZPz!uL{1YsP%fvvv2Q>I3A5Jka%GSyc?vY@Q)h(Dvudq{0^m=tNy|*eXVw>l z@!D9qr4$&o7E4{?FI^>qXflU?%BOG-n6d}{-Xc6~VW_hd4bHIYg3<$GzC2I$Kr)=F z(db0LWB_9T?2Wi@l}>(RW@f4`PNod{2pd1F<8U0fIu{-Qw0>pi#PG*x{|f;`y=Pa! zTw+Ucl6ds>$s(4FNh%hugi2j;SONf4_I&9a%>m&paTU-t0sojK{|HT|Q)YfOJG;}? zi5Gk?j=FDH_fdY;9!%se&Ba6r-sHx5aOk{=OKeA|$PuOiASZ6;{ML>795B0In z%^3Hh&R38`|8g+{ivCPFdC;^U)3!KY)ZZCpvi{mGXm5e>@&hOIIiggvkd`O}wyt|O z$AzsDOJPW`6^mcA4fKU}6ek&fhkRO80_&KSS z(Zi&*Va9z64RbZ4^@l&87;n!plCpB~goCSuOq#P_onCa2Nz$o3HW!~L{!~nCr89pc z+rjoooBA=)A8>@*8cxwpHon{ZYM#I#(_rGbg*KFA!{7jj;$gzV?C(49>=?Y~ci-Sz zKEaxJ=rZb%E#=E)*w}B5|4{bVcYh7t`XF^QD-W)ev-nB?wc~N|Hal_S&(zw0q;F=> z$xP>9r#ER+fLTYI*?_PSP0HhjM<-=4T8Y_}fegSbUV@`QO@XL*%71Sf<{7}K8^6Ze zmdC*&jF3&Ux_-Ls^+5QkL%}*93{jLJYYM`7!ZRmtIFzG%RUY!pE!fWU+#*+`TscAr21!?Gh>_$uC{@sDpWX)?f$`4mpfcA(osY5O#ev&aCz9G&c9rH_RZA3Lb zNVu;#E#flsX;2EpuboNuy*|%_!Oc(c;@)S3H%G6AVxP;EpU@AsP>6ZW9~X@YDqcSr zK6rr`F&63C?HaC)ayY>&@?g5r^_hu4W)|#MFwr~n9*#r(g zb3nQ1WL>VG{5{?fpBNKj{^e}DIpKr}={D|u;5+F?VkY`jHv#!-Y}*C!W)PnleCC2% z^|g1tny1Y_{Solk$QB?rSoUi{&a_FlvG~u()hzwu?2^T_nq*dYn8da9o|{73c@3sb zo<9+Y#F|4_2>>$W@h$F}wtvu&5{{~{+-k%93w}4&fDdox(8XZ@|D(CjqfoKFo?hul zamrtdSiW#GX$HZhX%3Cjt%4n7IR3_Y`+mhh28@~m8VT!Kju5C}Uht5makrY-=XQ%M zwU0C6X-mOejzXC$J0fjWkAPmBLd3rn;8-?S$3a3TQ0C6fhMH$*_s@e0BaZKf=8%J? z5jpxt51IOF;eszn_kDMS2>_lFXm>wxF!{wLt_7;S%;v0JIKA(Fy zUDl|COq7JV5ZVhtq?%?HUF>l8j&qNFv50fo?ZMrO7AEElj*u5K_zuc{<>i1n;I{VN z$3gC=qw9%)qqnTJL88_i6T8t3Z%!4ln+2+0rXQ)Wf<`)UnjWOXqZ3|aWl<~}E)lh@ z#V%YrT90B)U}Av~3yI3#%F3c|U*lIUC<$LJbz@3jWH*Igo`rk9A|~WlR|@2|Cm#lT zE<@;+2OQ|kbhXnr{8d>ja*@_OK(RkRpDI#%
    }yM>XgHr~ImQgC~?`F>Gh7oE%p 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 z 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

    5};$)8hHQlgnx)m3IOv=u%u;#}^7!mm6_O`JX-W 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 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs index ae5dd1e622..e02ad53ed8 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs @@ -24,6 +24,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning typeof(TaikoHitTarget), typeof(TaikoLegacyHitTarget), typeof(PlayfieldBackgroundRight), + typeof(LegacyTaikoScroller), }).ToList(); [Cached(typeof(IScrollingInfo))] @@ -51,6 +52,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, + Height = 0.6f, })); AddRepeatStep("change height", () => this.ChildrenOfType().ForEach(p => p.Height = Math.Max(0.2f, (p.Height + 0.2f) % 1f)), 50); diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.cs new file mode 100644 index 0000000000..e4673430d6 --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.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 osu.Framework.Graphics; +using osu.Game.Skinning; + +namespace osu.Game.Rulesets.Taiko.Tests.Skinning +{ + public class TestSceneTaikoScroller : TaikoSkinnableTestScene + { + public TestSceneTaikoScroller() + { + AddStep("Load scroller", () => SetContents(() => new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.TaikoScroller), _ => Drawable.Empty()))); + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs b/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs new file mode 100644 index 0000000000..6276eb1e8a --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs @@ -0,0 +1,83 @@ +// 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.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Skinning; + +namespace osu.Game.Rulesets.Taiko.Skinning +{ + public class LegacyTaikoScroller : CompositeDrawable + { + public LegacyTaikoScroller() + { + RelativeSizeAxes = Axes.Both; + } + + protected override void Update() + { + base.Update(); + + foreach (var sprite in InternalChildren) + { + sprite.X -= (float)Time.Elapsed * 0.1f; + + if (sprite.X + sprite.DrawWidth < 0) + sprite.Expire(); + } + + var last = InternalChildren.LastOrDefault(); + + if (last == null || last.ScreenSpaceDrawQuad.TopRight.X < ScreenSpaceDrawQuad.TopRight.X) + { + AddInternal(new ScrollerSprite { X = last == null ? 0 : last.X + last.DrawWidth }); + } + } + + private class ScrollerSprite : CompositeDrawable + { + private Sprite passingSprite; + private Sprite failingSprite; + + private bool passing = true; + + public bool Passing + { + get => passing; + set + { + if (value == passing) + return; + + passing = value; + + if (passing) + { + passingSprite.Show(); + failingSprite.FadeOut(200); + } + else + { + failingSprite.FadeIn(200); + passingSprite.Delay(200).FadeOut(); + } + } + } + + [BackgroundDependencyLoader] + private void load(ISkinSource skin) + { + AutoSizeAxes = Axes.Both; + + InternalChildren = new Drawable[] + { + passingSprite = new Sprite { Texture = skin.GetTexture("taiko-slider") }, + failingSprite = new Sprite { Texture = skin.GetTexture("taiko-slider-fail"), Alpha = 0 }, + }; + } + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs index 5dfc7ec0df..0212cdfd9e 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs @@ -85,6 +85,12 @@ namespace osu.Game.Rulesets.Taiko.Skinning return new LegacyHitExplosion(sprite); return null; + + case TaikoSkinComponents.TaikoScroller: + if (GetTexture("taiko-slider") != null) + return new LegacyTaikoScroller(); + + return null; } return source.GetDrawableComponent(component); diff --git a/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs b/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs index fd091f97d0..877351534a 100644 --- a/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs +++ b/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs @@ -18,5 +18,6 @@ namespace osu.Game.Rulesets.Taiko TaikoExplosionMiss, TaikoExplosionGood, TaikoExplosionGreat, + TaikoScroller } } diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 5c763cb332..a5edcc1357 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -50,6 +50,13 @@ namespace osu.Game.Rulesets.Taiko.UI { InternalChildren = new[] { + new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.TaikoScroller), _ => Drawable.Empty()) + { + Origin = Anchor.BottomLeft, + Anchor = Anchor.TopLeft, + RelativeSizeAxes = Axes.X, + Height = 100, + }, new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.PlayfieldBackgroundRight), _ => new PlayfieldBackgroundRight()), rightArea = new Container { From 5e430c726cd73c5bdafeb9fffdcd5be53c3e530a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 May 2020 19:25:55 +0900 Subject: [PATCH 0567/1117] Add testing resources --- .../metrics-skin/taiko-slider-fail@2x.png | Bin 0 -> 187725 bytes .../metrics-skin/taiko-slider@2x.png | Bin 0 -> 189590 bytes .../old-skin/taiko-slider-fail@2x.png | Bin 141234 -> 102010 bytes .../Resources/old-skin/taiko-slider@2x.png | Bin 127176 -> 96449 bytes .../special-skin/taiko-slider-fail.png | Bin 0 -> 61156 bytes .../Resources/special-skin/taiko-slider.png | Bin 0 -> 65012 bytes 6 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/metrics-skin/taiko-slider-fail@2x.png create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/metrics-skin/taiko-slider@2x.png create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/special-skin/taiko-slider-fail.png create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/special-skin/taiko-slider.png diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/metrics-skin/taiko-slider-fail@2x.png b/osu.Game.Rulesets.Taiko.Tests/Resources/metrics-skin/taiko-slider-fail@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..ac0fef86266d4b0667f4b62a4513175bcb5c5968 GIT binary patch literal 187725 zcmZ^pQ*b2=w5(&>wlUF;HL-2mc6MyrwkNiYiEZ09=l-W|-M4eAzNda)-BrChQc+$4 z5e^Ry1Ox<8N>WrA1Oy5N1O!wR2JAmYO7@Th1Z*2aN>oVI6ZC2eyxvr%iT`fnOyG2< z&u;Ui$!@dt|1nUW*-Ovke=jDtBs!9LtLwVW-Q}TXMyXyqb#WH{LVY{e+t(s!d#$7G z-M6A&)#AUnvGD7V_5VX8_s`W0vbe{nf#r3jj`#P9Kub%$^|go2=fuiRN6V?rHE}ET zT6@go|IU|T%D`K84Zn_!!}bPt^UI%_Z)cnQ%?+K_m#LcX7I)lqr~jS!SK^qoMVq`y zXD=Q3x=C{Ue_XCX{+9 z$!Ss1zLcS1W>X|-;H6%{@}>`%5p#tEK?FZrYXUTYp13AJ?Pn;MjSqH+Jo;@QKO>EY zTbYNRLUA^QIiZGfA%%7v67G=*n`Lc!Hy zO7e%Ke1DH3RDM9)A@kTQktI_cgVfgC1+;U@6}iL;s*sBa4Ks7noq)L&VlYm?0=VIbY6t1#QlH zz#8aCLuXb2X-{FBY$DJ8gj$S6n|B3-*=sBTYX-5YUXl*;DJ=oID#jXMK(9H=j=XRo zzhjDrMDEtxy8GoPfobiv%T2YThaqW*1;%2`7$#l>3+UIP^f4GRll6~j7$scn8gfWy zsm^blMEiMxV`1?&Eg?`?w{d(GS@@pS!cI9UC&T83_h~X<;*~4(yOPCVpOJ}pB6nL( z=1ZhoqpvB|{{(3Kj@_aPgWs7mR%tD+_i3qIZ?*;J>W;`2E18geUjL=nU@SRSsQzfl zm4v-|&XksQf)~4u;mv*mO)lZQr(?3vYW9vi`Oqly*}u|T(;{en?XCS8v7&FK^|RSX zpiTIYk$+{igk2Fpqr7$QO0#X&&&9dCUqze>O|k@e7rqZSD&j#t z;lSLmn_haTja~Ql7{v4Q@aDUox+O$RPi@WT;&geAptPHVNfKFoURKLtkri7ao*~e1 z`HzKvXB;ps0I}sWuny6H{SfiLwWleI(42N8UtorfgjhvGa+@jdC~&i%wRUqND1u$z zAi+IDwF7g0BVBQfSs47kt%~Pb{22Ufq^UZ?Rija|Q*d8{xNVxR>`V7JE*UZ~D83R` zR9~A8;nvM7(p4X=_qKgCP~8#K`Io7S7v+sM{$W}jw%RkxCxTmr3;S}s*9tR{J|^W< zNpnIGa!aGQ>=k}i(_JRb{OLbLI`MT`BISXW{*aHieFZOb-PV|~o~tl?SP+UdAm=OD zz4>G_!VD0_HA!&+C$|$0nO)IYZWh^ zbDl_swzoWM^M2e-HDw^dWpo^GGqicTM57E!xg)&4!kAPi8rVT}-RJ><0YbC_v~%~N zFjc?^&${uRLB3xj9TSE0!2p`n4rP0EQ9xf+q)-Lk7HAVIq|4)KQo9RwgZk{=H;K>NWH*l_hH3=ZEgF^QP>s_qe89(2&ndU^?2p zwpRCuTu&wKm8vh+RaKn*qOtge9){n8?ud-5H5kndEhZzda*Zrg&&pWfnk5p?GbW4` zlsmXxqlz%ML-DYpa<;b2?wv{uECXT|l^K4}-lvw-sjivf!ZooRE;b19k4|*nQtxwd zJm6UkviBV+U$u+|7s{;ojA(#~wAhFo!ucXaV-6pbZ+wqIvuhll<2cV0(u{nJSwP7| z71CRaV>#1%P3{}8oR=0tQfo%Zw$;YiANgb#wk7(rxEl2Apw> z)puaT`jeWqj87>#wHS>qkWtp2M%9uxI-J1xoFe}LXzj@p-6T`l0n{_fh6>PIn3lHmJwfz%5GyG zHL;ETfC-`O(Toa#8%O*QJW%LTnvkgaL@eBeYjZu-4U7@aG~pI*ZN>_*VHiPrA4Ky! z+#NiA;|)Wb0ouGdFaUa=^@HE8H|G3`m=NdkMLX`IuZJR9at{O$t44>@!(f(w+%BB=$uiwHGXmRu_=Pe@?F- zWVIv>L6ke85|w4UAZw`Yl!YzZDg5Jx1o|25XE_^&ZC_QpnAswv7SNKg2455r@`kyl zEUd4V3GbS7LQqJLO$!!pipWhSloGO2f=S#&ORYBbOa8M~xm{FgrHwf~M(k9#mGc;} zUK-sDEQQ4i{<&5Nqe+_$@#=uMj25?UAZWHr`Uy*Ip{m9P8)oE0L3$)8dF^$PscuJ9 zv~nIMD8HdVZ0q~yGU0-*)qUG#zRb#}Y(6+COXDD-?p?9g1ixG^FsuYjOxCuDzWa$b zsl<$JkR>QmTgBKI>%N8gy%jE?1llqZOBlr1K!Te*o%O2C6{;RW-mh19in&J>0&rU- z`RD2zZ2v4>bSHKRSy>jonEn@m8EUiL5eJ|nNzDtFUrR6ES~W!bZr>owx%%+th}t&m7_Sa01_K`*zluIuYb> zpCfLn7!jK3g#xi7U&U>|B8kK5dsZ5mYY+A{14Jd-mMwo$E zKU#1iM_!1UeVwlk3->4sKGG)txeoG6^3=o<39;+_bR(-` zq2hSP-5HMOcEem~=s;1I*GeTiq$ngOpAA%Fz({R*F`4H1@p$F~XG7fpj$ zkD!deUXl;lPNX{$EH;NY;22G0@qK{C-d;cAeZ0Sq4Ge!VD_5|X8ou`5q?>nx8?eso z{lo12d&RH0ML{=hRWH3F5Fm_O-5-VgO4uh{DGTs#VOTbOWR# z!pYd3AJ?WtT6KcOVI7D1ITHNnb1J0i70a?k<%he_G#$wBabOj4K8Sz}?#qUAGBv|m zX6DQ`T^cog&d}Wh$3jbE`=I#@fuR#KNqYi(JW%x=L-91ZdmRNY?J2M@apaCeV4M=< zC5ZU^4e7OT+7lM!~n=!gjjR&(4KI{kH_RB=k@_&|)`~MZ4K$1ssqPE0tYR zqi*MC@cUieCLyc+j@U~ywbE_GKJBrp0bEj@1`#?ow{{PI#E#0f${fl*TU!p}dj{&@ zx>9)m{xg@!5*u7acwL55^Ca{Do5%}}LScJ?MV*xa6c{VS&R}$qfhX#m?jCrSqtNUIpcZRF5eMkkEUohF*l}z=teKYE@2usZqVxo z5xXP0{~b6OZgUoW1%WVWpZZ_$nPYxI9I=1~hv1g+z_i2G?{qicP`B?nv-euFj?aI5 z-dZ=^eG8pCT7veRf-kHyc*NVYf%2wYP2Qdb(Q^7wjlpUn+hZU6oT*c^M)wqp(C)eL>=f{%?uli;|&$E-P z_=Nc4iZOxXH%3+b(=nkSG=HS!>4F|)xl_9(s8OC1CMli!=@?L`!lmBX3`zT_}swKTctZkLrV2z`d~%j#YE&UvTMT%8!AwPkX-g5_D^Mzh^Qx zk$>`VRNS+gx_Sook;SPq7wd@ySP^+nQ|8#eZGP0<NH7^x$v&Qln`nj#)`sZ#ujQ+;XdkR6(&nm^KOZOzusG-n0 ztRn*y>yu=WIK9ge!&OIhm;)O#DEV7X^q`#{VgF6o6miuQBWxsKei4-g5)uvWK*w-&pWV>PU&0#{RgZ3j< zTr*+f8PW%(2`j>zDt`5rx0rcOw&&uO1dm7U^gz9GoG>rUQ>z#?1+VCSUav9iI+H?6&K`&Bwh8SleUD5&1)3&RF5u#J~@~+ z;C`k&d!dgBed^Q`$9UH^&uJU=?)-HaKjAN~zUNz%$MDuoh1U@8&ElAeO}mzvYZhNI z`?lMo_F-y=d)WB4O?ml8=;ERMnZBXzw#P%q4<>T@Payx}5UEc2Yqyx#c+RN)LiuyX zic==El>1B0bkztz#W~L=kB%x z(iBJUk*9=~-h?-fQ?>Xz0UGC?NoKxX!Y+s=y$P29mbHVcSj>kcv4(&YX8Dv&-I+iZ zk@hsX^Fi0=WrJxS?oXUKTaak{pMlW!kS$jM*v#R^iWt*oNtZ9!K$C{XT~8G1THs5B#+VOhDYD(fKvq!B}? zC*$eqsmg4yR9m$oNiN#wfXo)W>nsXv<8;H})T^Vcg93gtozt^GUcbZgTm@~gUk>IY zqxx9Yx5J>Z^5jOFVl{@ciQ_y8j9U}~o~E!83tyv3YwLDIwxeET1BhK-NQWJ=>^4!q zAz=pbB-%TjWkaS(kTw)p|M={an=Y82s4X{KF&<1I(wh8XZ)4E8EKszYfU2<%XhfC5 z@KRQte?a;zk_59F1R%y`Zs&pZWxvmamX*lq2JMm}g+D>rd|!R99jQU^vK#|u+HBIK zsk@+VpiRW2;d-P@^C6$(b9>s8M}P*uWU*e@_M;dS*SHudd-R*e*)n(%p32rz$~?*v zEGRdw4TI9)U6nW^8D~g&2TYTOn{o|@+m}JyNHr1)&zUdH8UYR>Fwsc?M{-B!%NVT- z^XEx&0-IsE9&qrzMKltC%TV=wskSO?^)LB99DC0^Af|T8QUNk8;0>trgpnGd?!1za znJ!%ZLZj?htQClqG}a=`AGU&O!RzVXXz2Ss%9_>a!asjIG(qWx}+!=O{F5>qtQWyecqQ5l%}R-+){(pu7beX1_CQ;8YU z@n8EWqYR*8H<`#++7&2v(2Z-p@SS?Noaz%XM-)UxMupLGIjzKqXd%{5Q_x&p-D&;Tck8cYXR<6U z+p!d%UO|m<^Nnup!h;R6GAexdd+SjT&dPSwj;YX!kcVaFqA zgvCMR``pGBF-*A1@g&1y_zCCn2lNACRD=xDtE{Ws;wxJ+xcD{+n1_R|yC(7Xh7GpE zNZ^|Mubz@=^Fm*)US}mvs>bBE|8l=sS+W$Aww~u5qUa2M(jPcPT^y3qh`L9X0~6eg ziddhD3CIc%ma*Xea$z^IKPC}RKCBjxA{y*09LR=#T;}^ZI9|+KEh3+;YMpUxCP>is zGi9!w41{g}d@ZVdLtY}BgROUty9ZlWvR{7&{4-U?GN{A_dP~Q=B*#T7h;l5IU&)y3EW}Es! zwvFuh7&40*(+cCA5i{&^Og0#R4Lki26ZR&O)d7Wb#FBRk08m7Dj^i#0-65IG3Z5LZ zzlTS*8hF>b$!-%~Ke~$W#ra?DgZaCQ%DkR6l0!Q(qH%!lM?7jKo96tYT4M%4o~!;+ z1rQWRYNY*9f2I}ZLVE>5vdGG05s0x;^s_AHDqM=%aEGd;j`*+50x6nA*f7{+c9b6) zN21Thi*;8pY4hFCN$j{X#A0-|3hW8bEANt}6KSE(82w%YfU4~0e_5xsl?*3Pd9i3E z@R`)}w==sKFEa;$C}+D$0a(bYG@K+X=^%QLM=jHoQu0zQbh>GiS`}h{*5BB%{}rd( zEW!;zTgqBchS6|vup+^|aN&pV3L0T=$e>o8U)_l~so>JXc=jpx7Ad4;N?8#@-;p!v z@-(}0*+nFhc`BAjy3LEa3}bHv?46N-bA_oLJ#l$W%K^+VajU(~4Jkzo1KC;itt4@x z7-Mc`dOg}=hw2A7INlYLm9<*(Tm!Rr*ddXGC;4vmdw8@0btv~FRAxa}+Ci+XYgE2h z{;(6a%RQc9Bi3;>!D*)PzQPlsdz3?GFn`kiHmybCvi4CW2EyP)N@8C*ZLu-D2P^Ti zl5ydH4;1*Y9Wtwj@cJKn`fLpCKet`0tUObv9lvUScA5d}?7SO%|D~70-umEG4tGp?K+N@g%drKwS33(HEu!@yoM4yAVKJ zqLT zl-JG+ks{Yd4?!fk|ExRUb?$}xniH|{XWQM{%cad4x)fQlP>qC$tH)-qRF8Zay1Ko7 zUnHJx&n_NaoL$^s%=|r28-6fwZ_0~JzB^}6CVx=!dJXhJFy+Q#i%n&99-g#}xRD&2 z+T|z8KXmlW_Wk$e_I+&j{%rPs;O6^j=YKP_e>*1MOj(5!!_Fth5|=+?&QCO!N-!W^ zc3;ykbQ1A?28M9jJH|No$VjttzX&f4dl6%fTg~-|K-Euht-%T+h;AZl(GS~%zfM)!1eDa%QPB^+T+GgX61Wz`!%NEnBJq&SBV z1W6R^Ug{h$Rx=i-g3pOJOv>VCg<2$Kn`)$}ev$mM+x8+2zd)lE0b;x!(bs~;+j}guB+;;ybB3!k!tmC z{M~(8=oxriR?QMXdT_JK_C!O(0-J3-5Ys!YLFACMVl$Hhmwv??pG|iOrWWyd8 z4UA}y;K^)eUc*PW=sBJYG%X)hly27#-?rV%#3_&YHm%1WYWE}R)zjb-E z!fhRV*ZJhi7r~6_IBQi%-6j}UJSUUN#z7|xIk(*yJ4i_g+;-iNTlc(ufx0#;`Wh`q zZ1?vg+P%-P|7nHTwcnwPW9jNZ!~gVeMxsI+ej;XDytG$(d1p0dD<_aj# zzu#VTC#S>gzE83#Yk2GedwM4-B}Y&ea!a|;@6U!JHO;`O*+%qz^-rB%Zv~0#^xXx0 z_F8=}JAaI91lt*i+wAJx*8c6@zopk6s6UNlzJ&*}kql$g8pja?aVu1yE^0mLts-@e z3nRJkeHib^9+@71y|tGnDajBp0O`iCy0`w(34D;IA8pR_5ylc^&CZ&yqF-3WLj+rA9pXQ~JCiO!ewHRm|oKz=H*sLjKTFpAt zvS_`yp|j?&HWxx$JWPb(=E8jWoB1Ja|7I?(RI9~}A{0wP`Gk z5Vx}{n~j<1>^9`dcMQ=^!Q{{a;Nj&iF>)udDS=v2bSz%knbeM^SPS#El&%CBK8mH2Tv7IlUDASCgSl`E~56skIRik z@o>dcyAIK$oS0*bh^f>Psj@M(O306FO}MH%jpxMr^n*$FtB?D=t5TFI@yZGU&EB(8 zOjXzBKzLL!^m*rC0D&@{^Pw_U@)hGI<}cDa>-46n+i>A`EaumB5l0Fll);$Ezk4ZI zuxQ=9n<5`NNxiI71!Tx%11a|n`05Gx$^{mmnWynG)L1AHx(wp(|LfpM~e=f80a{myT~4cAtTZ{~oU#bXJjfTGIsy zekPai|4a9HWz9OGRdFU=syk|X7>#5+Ce_Z)&e$b=$G+y&O%L;(y&4a?UPhg9`yDD2 zjYlNis;dk2jLL;g4WCbO)%U%jj;%-y1xv-cldZ%4&>+t1OS2>B;_#ao-lp(cVxFGu zcdqYAE;;!>9%AxMfAe4DqZ22!Umm8MMU83lxwCqbV$&9dm&^p2Sjw^Ib5Zvra^#Ln z+>2>nYMUN7iM-Bm<1`$c*93hx{k*jNJhXbv^>$5m4UQbYG*F{l<`X;}!*Jvmj(=aB zhii&y|AXTd?%%CWEh|y7Bqx-e@X5g#pVDRQmT_x7k%RytBM%=}rkcOfPkXS?r4#;p zcgHyhoK)sZae_iaJW6Fh7n{N@&?d`jr^53^K=9lHMU7B48CQuK`vKN`F^uORpA?9T zbLr?yh@1q`gFj9Qbdrbn9hV-oXH+^GDK$Tm#yjKuX3i7?SW^-~#=#U-WmKdmVl7pR z)@lnir?Gg_$uCL7DGG5b_*K9m7E{;*%MmdcWKRP~_4Xt%6L~ zu@FQ#z_~^SZZ*pqF{n8N%ihcdQWZuME+u*tYD5`}vzdiCv%D;Xj4u3HT8L|uil(k< z#(mbv`VB47o#Grecr=|Ro}nzRtM@R}bjvEsxg?9wpaFe(grfX0~jv1~y@HM&GnuV|T7Dy38ogVAAx zNTfNk0$C0`R1h35T87dSmAWvGLSfhYXsoI{GgMonSL_s@yB};hjmQ&tA6W!hPyflZ z*rg}6KJrSNO5aCMi`YnWMsQw(?1AL9dBp=@q~Pd|-Y?lR8k`qV;1XZ2 ze5l&1q>4hP=vE(vgP`=2tDxbY8|;eB3gYnVTCg?_U+pgz)4w>5H|3qrdr`}v%G!3b zb-LdZ%;a(X|LzXO&(`-n)yf@e-?mrjy(Gl*eooV0ubMhucs~77gBAF>fkRDA{$74R z*+K2k?XD+_2wlSYH4BIKy!4TeF*(8tr^*P0K|LyBE$eRnjuwz7h4#B|fYXKV`q8eH z>3N)PB)JrUCcEuNOA8tIXTO`|j0}roTnSjtrH4C}NhyT6Xdg$p1(APB_b%qo6Z`eg zt~7JowtFSjuNL;%jXz7)YW9cEyuYJ_akIl0wA#SW>{XrC#XPJ7?8D)s;)AUUR)^BL zQjNV2hY}Q(iL2b6O!cja7mnEVP>*7jBUFkU#b3g*uyJBUQ~|kcU_5=dJoFb=Elqjc z5n*B6sHf@eQR0=QsZUe{d^y-eHB-IQ2FmbT;MsQo3#zHy7J?NFEtTQ>5QM>kT3sAY zxymN<>a%awdyLNBF`+L zTVW~lkuxOQ2@pFeJ~KXL5ti?E%Jk54>9QCegE?bM+>bxzw>;q0?vXR)i4y=#^voL% zbbpo!+0k~s@vY?rvT5VkLFs;f4V=}lbY5M$ZqHKcbpfn+r}t0jRxS>x+yEO%kX216DP*ByKQn`k8PW%2njQ91ldv-M~GI0s~tfzM$ zdB?>GJRBV@&(8kLjEvNk4O4@LKYclEfpBvlCfhAL^{dS;1wmsPPH>H#sx zY^y4%{j4?_5W1YxW*D^AH$%@E|CJ&ynQTlX80ry2=Hkr)eKKEENVAYRYT71)L}bmx+X! z62in}mJMfdOi07_69gv!RSJkKiw6lKNEje<+v9ooOLpFN*@i!{adI^|=6<)gWj5IU zZqMmKfHw-;)pnY6Q4xxalaZ7%dpz8#fE8b5$mH;fyb5`Pn22K#azP?YO1SmBH7)w&IVva zqYtb9mn7Bw#^yo)K-Qi_nAyeJj98<8bqEhAX7XnrEpJmqySJyd{_1f@iAN9Wcy^a~w!d{gmH(5_BX2rY4-77%w^K^Xd&n@UbySP}V|1aOd z|MC9*Ge7Sq|1rAn?&u-TbcGJYyW3qRR1oGRTthErG*!IJu6Wl)|FUvrOf~;16|+MKAPWw;B#%Pny$(a`V)zZF393 zX^l{0aZielK2g)ug(<0Y-}ZGRd@D2ovUJ%R2Oxiy*`t^uL$~OR0G2+WmSzP12H7|WXdUk5*8K`j2@Wi#xaFd3d;zT@9=GLA*dMt3zGgaKq%BYY) zNh(~uTe)bmMU}ZEiV^Ma-u_{euAsmF^%bn3&}aAkeMT=IxBGl`C8)3a)j+LKi`+$V z*bj#;Acarzbf|qhBa!%klc26a>T`Vsu zAvG;wvGqEf{+ziUqs8!?W?+2TIKrB2l>fK09^0I;{)8-pMB?NEy-$aL6vq<8v(`8!HGFAGZSIaUkP1fPa3bRN!JoHUSH`=D67=`tO$)y z5!t)#KISgJ8uRlV1)K98x?IHuVuxj(06TH?ocq{PMvb>L` zh}7<_R{^h`fzz$Mfv*tO`&)!So-D>Z;DsoV4C8;MWgS)C*tw)NIJp$JaInjT4b+mI zwDtDi2v}62Sm(x7Yg@@+Kj%y#Voc?le`wX;q~oG4K3Bvfhg265usU1672%ux0@cPx z=ZPffNU|C%)RU3z)1yYK9}Ce?aPb8v6D%zD;XtywQV7pjVk8NmFL97DX^WX3|8kN6 zOOiz+!OE*he}|MX?s|*^0#T_)R`9b|0v&s`nkj`ODELIA57(2AaHX?pZC|xC{U5KJ86Vfl&HuifoZNNcvJ9$(t(9EqB}&$GcR$J65=~Ht)z(IYTUBc2 zU4_l<%~YKc!Eo&B^}u{GomImV>fi|VdVz$zz+5sN?uWWio3rBO2}<;HNJSGTlO`;# zso}W*eVV}-Iun&irSHN~mr-FH&&rKr^KPuz2Yf|LqWu@IEh?tCoc2NN-8@ z2O1Ry_y6vNI}DEwB^`&lLV!yJp_A$Si=BEnuPw{2pr+-4U(k60*bG97W0BJEzoim4X!ht%8K8+wt7UalAam+KFTS|YH z4m$G$B_Wz($4l2bRW~Q&miEY1!p$4c7nRNXb*3Y2J81g?D8x~DOf7eiY4PeKVz%I4 zYU$Vvzk`38f2FcSs%a(rq#q11>kWA|u9WO+UrD3p;Siu|pfZG0gHC5SDczBp`Kq>n z4795k70uy;8`j0Bkn;0Hv_w;2pyJ$xizO5&`Pxe>o!mn=EM-2VEiKtDg{)AFm7X^h z7*&*|g|!gNy8-6I`7gRkM~?;E%GRqa=(VxJ-4dW?@v7g)GSYQ|ij&XrYobL`-}~nN zZA{9HAf9FMq>yA|=g&Ohr&XKUTkpTNFqkWRn37=>sDGkcYhJp(6I-}lg#~+#J=XVlaglGJ z-@E?)ZjqDo^)^2rPekauaDI-TlmBm#TEXve)NM(ICd9~9!G=MWiAZ!S*ApOdrFQk1 zx83Yjg{=Rwz%Zgg8Sll&A3*8FQO@C_bA+Mh@6{@fDWir^FnCgUDPl}DPZ^R9o41c6Ut+D$Xc7R&JTrcz~QN05h6%ztMVr(iC6_5KVybjAD$EBN?Y3z|C6w{oUv7)h=#Ap1|sz{#E!4;W(|InppkQCd6jd)hwh$5 zp=qpyIX2E)KTjg=mie*cP5krtQt06=KxjX@bpm`xQujyNJ`c_u?KuWtFc-zfDQ_bF7d`oPKOuP$PtnXjV&FKa z*8JeCbq737dU>ms|1~7R^V1)<@0}eTz#rG-XW{0O`SFbsnbT6N*Okrtmp)4e>9uM_ z88i5fmoUk*=k^~Lg_X%6v|aoI*Cvj-Y`K(N_A@{9+OeZkT;PcXP%e`R{c&fXn1|r! z`}mkQC)am;e0-LW(0BFzULq&A_hMvZmxsXb8XnOr35M1m7Tk+sHK^e@DR_FeEQL{= zt331-XG;`E@S}dysh#88PgXfHeiCcgnPA}x4=pof^Kk-7Yro~XT!E)Jet>j%t%Tb^ zhO-N>D4jE60y9J@xs;tE?8VH%!$GosXc{?=$ED)0J0ch2S%fNqvxh*ad5Po@ zWRdP1Qn6}fCC?=h9IVJIMUgW+gQcTza}nIJxJ4!60Sh7Xfp){RkmM?)AkM?STWtC8 z)(-8I*fv;u4{JLxO-^fI(emkKn~roaTZS}ZlwM|FqQq)FlpM+ zlfEcU?LP^KV>&7m>Z5{ZhEr^j=??>zsFlnp}A>6HpK9$vNK+y#s zqH&YTPCfK{^6v>inIpulGNH{sDpS zI~EoO{>~545)!+PJ`cSX7XLmU9+>p?_(#s>&*LGdDz;}@iDsg*>Gid zu}%~ArvfvJTgks3D$SO{|0e7cxMxX2V!DRG9AjVyucyqK@|ir{ap_Nm4_ds-3FsgT zMb}Sm@x$fpry-Q!rx34uQ-bheut7Hj5?&1q5E!cBmj7c$hIkziDdy{XT)T z)-@*%eeMgN2_?8z1j~b9OcE^(qjjk)pZlG*D-4Is|F$$K~TxEQ#=!iBeo4gZ03UQfgs`!GH3Nnh!9NxUD&@RlsNa2lC`z86cN58 z>LH&V`Clnaf&C|w*tOtvhO@ttVnnner57R$77Cort-y|BW<#uv(-K&k1~^2)Xe7@< z_L9Z`M9V>{uXu4NHsM3OjM)d*At%32w%A4NDN`J=Citj-l~NhAK5#NZ+W;Jsps`0e zK-*Ci3B|@C@#s?#CNKx7&&?gwsf4$;nuK?hPEO_=& z#0%tqU+b;oH>ZVunulPQ?m%Vg23%Xvbz0h_Dy~3|#8I-rZ5c9m4acJh9I@!M1RUR1 zL#0SijOA(ffM0_oOm9%dmNw4j5%`GGMX!j?!=E*&p4ln*VV$cLsWWZN9Lj`km94sX znX;}}%0>H4)ex|Ja5Cu>nF1xD;CbFQ6b89osQkOLAws(-y}pg*PF7IyR+MIC8Y%Dw zDA(C0%)8~;b3S{FZ6ao48bs{PE4dJzSjr!DVss}T%aia)#2iaOb>8ppvYY$ zCFI({zog6thz;1ciapoJ3H7wEemBbmnYZ)5gvn~7uHi)$cKP{_rr;AId9@z_hq((_ zZJcYH?D~jZ%Jz3iZ zr+p|5hg-V-tvfHfIg_0vjld&wMbT(IhT!!k$%0+lTtA4$qJrrqXm%>~iD=#S9iP19 z6Cu_41Mj6QECii>zR)Ek@_oHN(y+1reK@}HGks+0LX4TgOFIv%61%pFk~i(p)IWO! zyHg4cT82S*=%^-2zBEi4Es2N=H4N+p_l(odiJKjNYIEoO#v<6GnpZ7W__ijM<-0;g z`#aghv15aOeJo~9^jTUN+a!H4(6O_qI*E}UM{A^ub)>AN>~c6?kHb>gG^#T;DFj!om{Ln)GS3&DxNmW^xzotE97wnfynrs|%`piw zF)8e?srH1{(x%q;_UL{o3C|jRVNIgXA(^iDgDI35!QS1Fn%QuBXnB}m){s^ZWPwoK z)7`dd;R1*{{Xkg+01ad^hA6C~Iql@|TR(VFyWy(NAd?}Ivu9AStgW$8UNoo-G|BFx z&NYPU^mdUVzdR&12|~tKG%viH80g=xmRC_ zoFL#{szc9v=^h6Yd_W+p6U4`ekNK|C-_93s65nrA;}|CR338-5^3r$1R_Ugt9;&68 zpGxU<4Lj&jNTGvZ$R@+x3dYG0!#gogVZ%{^5;52v+BV80QGvE8hv6X_!)y1AQ|d0M zWKsZM&6?3Sx;^pX$`vZ`v=tLy@rND~OEagMDPBX~@+bR@R^g~5qpq)ozY01b8h{EE zC$1Ix@X)*)c1lZzD=my~m=cUV{>BV9EDPIQ5bv1@)v*5qWk8z0w#i5^OA=Xps>aj| zpUvF3`{V5oMoYXc9@*`M^V*t}I^&~gMxm~rnKFO7=>kleYh?>zs0H061y zU|RR%6bHlPLGc!oyp$^Uv7M8IR=QEan0l;Ey1claC^lz4&*_3lo!e2G)?9=LsjF=8 zU^X+V;Z@d9?)(NE)|gaR6^vQ&;fc)%%*oF)s&r@0X;gDsPE4DrCk1DA`c`E(!b9Ri z{hfu1J)b4b7K%Nd&w~QJ5v+~|IeV_j{K6jE(B)=aJysZ#g>-N@PywKwmkyGU9bxdu?FNj;E!m_!m!bM zdhg~>-roM_!{J>oF24Nq^yiL_zVrC_!_UuO^~saJb$#>ecQB>axJd;G49H7~Is!x@ z18DjuFbN(pT}b=E45!H^kcD&!zZpw7C>lp;qIg~)D~p*0B3;UxKYfJf;+Y*3Af>cDmH{)oCs=BY1_76ds5BC6@(^&s z&;3jawf1cVAsi+9{nc`^=o8uiA9XR_atN&`!jT3qtV93slyML~n{5IHhYNq$f?fY7B85@qdTCs|9@&~RonpsKZ(1By$laBg~ zCoANAB^T(bJ$5>C*8z!|DI=kiZq$>wEh9ZAlzrS&)Zv=YCg5F*6^+XYAkdB7?8i-_ zIL06o1m}Ijg%4PXfhCnD4Q>K8Q8r&qyn+xiHqj3dX)&r47-joT6hI>A9hc6MDT1kv z7H`g!0S%lIB;wosKb$K#3hA_ zv3Dk7myxN08F{9)l#r84a`~D@k%5aU*d?SqYU~S=<1= z88}AbD=mRSj&4fM@6?M`;!!>ILvknqdlJX7kG40JVeaURLXr%U(#{eiHD^U8BDc5{zb#UaiT0a`0BEYyS8#Y31@TC!ni|^ zFZFDTdPfp(V;^KcD4u9e1=2we( z?@q_n2jPn5w6H)(=y)1nwQH0y1b+CkcfA5tZLLy7b1K*pbWr3s2Vq~@N)|-1!s=ZC zO~N1kpUsTWxW|+CSVUUKNi8wS#8^co1zw$HNW|2qX-F15+*CD!s^zk7a?y81YH;Rp zL9Dz2Ja*&iS4jWnucX%>W;f`U93eV$<2zs;nNf*NJhF6|mbH5&#Y2nvEvt^v=XcvI z#hpi$F+G+Jr4vklIhl+Xz8MX@*85=AnB6D69dkc@!IE9t6QkwS17TCirgwF9!BW*&FK6|X3CW1H4hHEmnP$B-y6k@B$ zgW@^<(5`w6UhIky-QleNmN-I}FjnO9@;HCfPW4Nu@z&vr>tN8qg&SwW?4sBbn&ipN z!|a9;Db~|18D3h9zII?<5^0aC#?H98fWCUF)~gPAxal*zi)NzRKtuJsJ2W2Yh-}=d zkC8W~UwE6<2MyXI{|yfl$W=q@GogJS^%~_oYul$%OSR;oX*Ze3r)Q59_$z-fdQr++ zEB;z55k$=6b)1K`Vjj@e4-a{cOAaFAb|#gi8iz)Vqex@)lz`D7xoDW4KV)evq%FRm zu>{)1KQq;Zm7A}c`7dr#&a(cEPoMtX>zmg+96sv&{Ckd1e)j0-OHNPU`Qq|FTwVR~ zyF2R51NGvKeXWmt6vob;ugq}bjES-el0vzFEC(VLSkm4YsATu;RC1RttaXx4%EUP` zY|Ck1bVnf(y(RWZ?H%DM9*p$fWujAZD%sHJSQ8%P@ZljcU8KN_yL`kPZI{f#Nz{;& zuje77LOG%f>6o-T7joE(=AP?SWrbxZxn*HQ#138Wdbbz9?BhdcR!fm&9Lz~97u5R= zh$ur0M%2CAGDz6z2>XOrg_O}t!=gu|-gwLj1(ntw>$prtx`<)CWeP?y282x*q{%@} zmkyV9AT*Ia?;Gb>WulRYS?5^>MO(D#zHrBdKitYJf|iSCjig(g(BwACkSTQIGFe3! zm8ugS1)EEr(cu)c;K7-Lkp2;|jsF;_3=suvTkX=9j&Q=i#brJlvW~Td@h`6qyQ~sK zFQa!yKLJ`4ND?femey_&gM{k%QBlgMt0!SWMen4a(ucWG75gBH ze}mUsFDbQzY|+$#BK?Y{6e<04ptb2*?jaZoLym1#O7?CVDdDiLM7P~O`9X7wKghtg zl`FA)|7oUoD<-4z30!)11&s!Vt6U|a^&;ajk_Nglg%M;q1@q{MRu0SxV*-RLu`T^$ zS`j!OzF?eh7+V%Opk^4-whe^IeJL}HTRj^+QhYHrrrOd0fN-<%k13+ zY$Q*THE<0xx!moU|Mwj;Gcz+YGcz+YGcya0nVFfHnHj`7Py5Z=pHj6dwc6vgZvHjy zuB^<+h!-zjWLdu3UB#n&B`cX`t%YvO-h)3F?d|PtraHKP+YC3K`~NcBj5`y%?#^u1 zj@<1XZS3|3qYN;>DC15gSY18=akh3#Ms6XKJ%&w`IC;!1(X>odfHkGu3$)K;n1aJu zm};;)Ky1{k7nQ^|H*C8Pk575$_G?|g{xMgseCLG=e{=Ti-=97E{TD8L;*~3}fBnYO zH3gs~+{S(8^P}_Zb7L&Z8dU>)oHkGRBAld3OfHx#m8uu=_F%S%yNIGP0Z8PZd{I1? z1&9sXgRcz8u$qqncB(nvS;Lrd!dgts8yS4vnmYw1F*GQ;-f(bX)n?$-QJDe;g3c_2F7hHMZI z9g=yxC#WoLz5 z*JaiB1w^X-0``U{t@^GQ-p6=BZqi#cQp z>5j&8%NK=7!HcfkSRoY`Q+>4C6o|jiSd@x7Uy9!Rc_L8Uc}g_y_--<5OCBF3#GL52R?qef?(AwIXO}?6l6c@$eXbH0namuyYFS;uoTq|v@odB& znkUy9VdyK%zA$(Lb1Ku-_%)T(v%Wcbh+P$VoWT{mgS;6^iR3o{PBL@x*svyTeeBJT zq&T;sEmur&Uf5_D{rd8(XQkOYGU=AOgSuLg_~=||!kwTB*p6t2E=~u4&$b}34dgjcZojv<47cYMJ z)vK>|^X8MaA9kmBne{~?DaZE2P29G@Zp>--JeTv^P6gtdwk2iSKB42Hz$a&SZ!LFI zwB4~e?c?s3i7p6bscO4JTaLf=zG-oJ`v!muEvH?Q#G&ov^0pN30O@%9&1vtuze}g% z;9}ZZUpvN}js+B%=VG2!|pWfoaruV>H~i;nx<*|NQy$|9R%j@0>gLb(b!^@3m_$ zbL-YYLFE>T!3Se4>%cz+Zmu0bqxhO-)z|FdjIGXy33tkYkQAjxK%h^C-G13)-D2GO zhpwT7r0AN7%O3)*Uw~$%j|dCq0vTDroJ8{7P;=8$0t)m?YS3DOxXBX+JdUEEWhG66P(|+_UFpgzUMp@|wg6M_?8`;A| z7wmO7v@H!GZypK-ngW%{76YA6%)<)-2rCX!=eIB(2C2*K46{R*$&8nnh^9|2NK~HU znyZo`(}%L~fTwSX)HQh>(u7kx+o*={?K8(`QDn#ztw60vHxq8Rtx*Qu-|1S{1RVn( zV=;E~2>6Q&xY zAt8fiRVwbV7inS{76UkJTHkr9O#^A44{MYOy{Idwhdj|KeWYY*R643wKUUjA1cxwf zW#-?V@+v1LFmg}G!eYX-G9aqDg~4`ZUm4~`HJgH6F5F<~y3X24>|;DUGvRIgCYA1@ zXex{x*hrM(_05Og&~6N1VUS#@0GeWE+GDd@;2s3TgokXk`7l#9O|hq+$F#lSM)V9G zJ|vw#7){gu{^4iJwZD11|J?i0*XE&5STZxJYN;x6=Bp7)qcH)mpbcXd|$EXqePIo{>)3Dtz*b+V|QY67Nd_z}< z&|(oy0vJBp;MSp@0R=&9RpMfVR~ojUt+gEESkEXj`Yb1Trm08EbQm9EQ#k=3pSA(32#0{(REC*?qEX_YgNJTp1y_BtHXMtcQ~fgLE5wxC!?bRA z5|am(drGkM8{3>}g%MZVO-3pZb3uj!B0~<$^N|L(p^N!eFoGg4aICCK;d+rB>Zv_} z4G|`r0FIlgkzHbl7wD2=ensHJDQXV$ua7*Nmg2V?RBG*Y-I0222>1sLgaFSe)r|ms zgZ0+LJiytWb<^SS_0VJMC_sljl)xkN^*zi&`CNl*uOsswUTp|z6gra=1oBipyd)WwTGSX{LfKv|8cp zAs@xn2|m1i*pcY|PD{@6&e4LzMW6!3D};LPW2ab^3G?p~#X-%o!en;1JR@M^h=x*T zR_}%xF5kG+Sk}xj1BER@N|cKzzQ3Ke|L5gf3(t*_Vg%Y9JzX_~jy#Eb6qzI=As!PN zOmS&J%4Wk4ys@3NCX8C8o)c59=TCN)RRTJwBi@2DW#J4`fw1FOos=(>C|wkXK!!s9 z48j8>Wh`yFRYu@aX`psDtI3E!bEmzi&=d-&Qu7C+{fB=|rz*zD1DGBr>dC>w%HYYT z=~Zss`p|3FzWLInKR$Q=zdZZLFI@PnD_7p~=FR8W+dEx_nRWp&ZLv5VTe8#k8z-YN za(rq#ZgRMLsNJ_uCr{XP0zjrGfv@TKU7r4HI{jXCcZxZgP72O+x@etFzqXxuG}?07 z-ga7cb^0YRJsLrc%EY5_O*`96PinB!u_rg3b^%YP;q5w24?W(rMW;?D&-o+y7mfVE zXtmnkKm6I;HrM`Ukca!T9p3-RvQyccuR7{rKYL=mpDmeSb+eqAY&G&Gc0pvOVTzsy zrrceN{U%&Q1O^4Hl&kVa=;Xj7YzQdclYBvxV|iq$9~nu?;Vm!?3GJo3`(;YxT~6?Q z?oU5WuYdE_CtSPsy%#V3-PyB;KZk=)yn5~RZ{0dvTHz#lvNv<_T?la>0$V5O=t>@# zB3Qnvp$r@BXo{#Hv6fCawNC+YLIWTJ$OT^3Y%SB;Z`NvGgSyv8xCj$vQ`(AIO$t~+ zR%&9?9Qm~hutG18vvWzsCGzKpfEqma(`9x`wEhs42bmmPyHd8MyHW; zL%t@2Fgq3mq_${gh~(9Rf<^@LNBXv%3bAdS!qgIQ=`d|%+G?)K#T}^ za@id@_h_^8*^`CB@1kVNytGnuv(2a2Yyp?PSe`yy+C#_&^+0>2Jl&&B!9yzNCS}9XMWh8%gKb4>$gU&H%wL~g#DS`}q zNl@$QyAx10a#ztXxs32SCgb6Z!9#<{UVF=c9;%iT?@AEKvES{CiA}%B^B{eW`u@S_ z=vuGWho9Usb^oX9`D~2(yw0OG+O6@$*3G>S1j#)nS<5~4pc+DpI^nTf$49@DwGR9; zqjfg3mSvvtT22MD;=%yNpapP519j-h16~5QU|bn+1Da4ENPRVkZ%go;)AUw1Z+-UF zt3Pq!!oQz6^T+4Tee>l@A9nrvtKGi+6s!FW#6mVo9A_j?*+W2Bs;&NNBC;@1@J*pR z)l}nlM*W~)92l&in5-Wft6vF5kl6GCcqt&848$bQwPybG097XJE<$;_X;^ zHMb2n4;giH0um{Y6~5<>9ISOIg&B2L3CPZJ84{}^M2G^*Iu5NYFB4v zzcZXHUDX+d9d+!)_`;mYLn!jDKE=%DuA_NkR}a1}dYp)TMhc)SKfY{>(qqXIXp(sY zY03H6jdlPj;cT3jRQsuu>X%d+#(Gi-Sksq9VqgYqY;>J?Rty=O(rdNj_EQj-@hQFTuGcRzP49a1=9gZ% z@=F&k{MVT?zjgln*Il{tJ~wW>%--H(Ijl~Ayd9i-wraM$-_w6R5_zq>E!#P9ikV&g z-xAB}p=ZC^N)Ib69?5I=1+{I?0w^9TVfL)75pWllb-a zxZe{hU`J>UPoDh8&xeQQ_3>Bh>V&cBoWbLTS?#u&+k)rS&aof-!RX-NV7)$AZ|+C` zHsiym%;YDN9UK{Ed(7h^kFIBQ(5qgZaCsPYf1$?lx^4t%$RP$dFcV?8ngfd@FSxol zsxE=K)-_HFjm+|pe?yK!Gq|(E;|6^`#LPkg3=5fZi_DNiQ~du2>zCi#`+%D_zTwK{ z-`)I=ul>S>3txQo>O0@O^}_r6cddo+WyjTdiI^}yqZcD|E)U79rEOi5xG|`Btc4LT zV;S2j(4=F-HYDrzYDKsXni%@(IQwp|80c~dvwB#0Ni?^J@- zLE&TA2<)K{^Nl*IQEdv<3UQF4GB}qy4^~S#!#wpuf6O*&fd#w4h`E9_Gc3WZQY#GI(MUBUbQ%L6ocD!Zh-Ot3yw5tcSU z6=#F;g}X6;`kU3YEctdoL>5u@MFha1AH9{M(&ag-3}(+PNM})bk)x~guNY%>_q-2T zZ^N0^Onh=14r_KX?aPOwi5Sr)>@uQ+Dj0Y(uj!n3yd(PDXk?_*)X+7v;8AuGWqbp> zTc@KdRICwl#!w;ah8|~q?qy)6zK99RnO~MiM{G0_8RArTCWHp@gCd7>X5lwE!jl$G zJ^M!42+8R&ME#Z(%QKcl3v%HZPAN>x80bN<0e~7|0(LXP;dLQCO67O75Vnh zAB+y}9vmEg?jHUe4G*u)mOLH}?q+bbl0ld6x|o&t%0s+v?6YvU>kU*JuSCdqo-z?* z05w2zw#%3Q{M@;JJ9FkoFJAo2YuDcL z_U-3buK}EB@le0~kY2JtkR*rpGF|ve3$N8k2HDb)Ohx%@P0BT}#EOKZ{P+Z)Wuaf~ z0=v`(D%!F{#&J3S49wIqk6kWK@+@)5Pp16tkj7}Z(k~E+Xjx{w^aj0HHWwWRss8IWclhScFwx5Lo_Lza%hKy#yX42PGS zjaujgg{;y=omt`JYyxT$*766Vdv}NH-o3*qW_e)5#UjJ_VuqWO+~<~ko?mu-i)+Ri zH=i|Pedc19j|)LNbYhX-u}Wso(C0;twx<#;Gbo^ev>0-tqdUig_IX& z=gYWVm$2vF00*2Dh%HPkzocAeiU~%`=P-EhEvSqjv!d}~K|0HI4U_I#KEjrzWT-7J zJ#Tx~jhG1U(sF`MDFeNUY@XNU3R^nY6G<)>8;hIs%cZbkwyDzs`OCki4CHCHwV1H+ zZX74KI6B$91w)=dx)8?vi(t8t_2o5_w9gBD=EU+FaM|(pUA!c5K3jUQb~DQKaje-Q z3dW1C>;(YJJ{;+0Ub2V^4i+LVy=d_2 zFQ88*;pLp?%)K~VX!?aCVxB5-vU7n9WxAy8B?KzTfe!@O!uev&7;s^>5PLM(_Bht3 zB)FWxqR)E|{$LbG_r~k)(bRpPox$q z(tWl^o2I8aIOv}2-rQk@9Am_T{EQ>n z7#YD#5@yF(04c*_SkNY9xungQj5QSxlM+$RrEt`i?hUSy5A!h9z3J;~NTCy6ZXo1L zDGl#xwmlGUlkYSX;YoK?(K&o_C2?c`_Yht$Zj@xF>LgDLsH2R}>(;Yad4P-vz=9xB z){Y@?$+du8?IPgDa~E^ZD@2W-nagQYqXHC&z`}6R3kNPb1qi#;th6U0AY8Wqax{d~ zHH%pA?ITI6K*^BEc?TdjVF1kvUCeIx5L-SOg{3~m1T!qEt&=HxP`cy55y6&tj7O2^L6<8%R!DjsD(BcfzA)rAzLl}kN_|Eh zW7pWApsYeyz2@~PCU!~*OHwd6be`R2iEk#cH=_BnfjhS35km1ioPEsth@}J(QU~0C zh;Z0-1_$=L~ToyoS$^v_VjG#N3Q0<& z)9iwQk33rvJtkbEcAU^f7^b>d)DPgc!p)AyMkpCD5m)iiNPVg!D6*14^knxQd&$-6 zJ#XLss;k$2<-&#kIdkSWFI@PVt5@IW*6o*G?LT>l(@pTeSE;eEMN-g~MMbK@&|U?p z{6=dKXouJ=OCV^P3)OF4Go^3dMIf*|HW?CZ#S6xLm4!M{+W*VTJxAG$Ass#vE(Z#j46)>zT%H&b7NVL;i7EO)R$-BSSFhg@+8rjr0+ zZeD?llSD-@?VyUjwt>8ZM~#o(g!B<#j)T(zj0rp;l5xXm+4=KT?)oZV@!HXhIO7%d zJViwH3;S2bIJute8NEg0hPdb?^99VXz1}CNM0dg`yL`1#3TC>*5?!zC#=U4T^TR8{baI$lmz=$N)v<%v6HVT%%^C z4Nza1;_REy58hU(2z^RU%&s#_;{IVyfP^a@L;+tO_!?yfibuzj-vt1Y3PcK2DVb$E z2D*%*7dKd8(6YdW1M+SPFweSV83F;lp>_D7Ijm8>rV1DoM zr+~=F-FuE^DlAN4XAx5A4@RYXa`w|xpVA_Y*U8C+POAMw@!%k$7B!*DCaW^e{M4LM z(!FA?0x@*RMkbX?rFNp4RN~G`WG|GGkz{u`BZ9zj3n}W-C&3v)Iyj5OrE9gI5vMIx z)q8%D`TqCs|N7gv|K6Mb=h=Vu`t_f8=gzl#`0(?;`(Cmr;f08(CKnX! zMwrrBh8ApEP25 zEQpfoSPd`jSRR!WR9oI6TB$}@9l1QjR9}#NyYH3A% zV@Re#%E^tdu$<9hn(Fd^!rlut^hg^@Y;MzKND}rcO}dhXn5?+>O9e7ta}PKT>X+b# z2Cq-^{Q1{>^5jR}yZ1Y9-TasDyz`&0Ui~9CZ~m0KcfZNwN1yfm_d!h9?Qw7e+cNL$ z-)!?ak5dqifdZOJW*Cn}HY=xls-bP?V|N<|zq6tzSIsbb9Ep{h&T+IqAo`r9Eas<*+a$0`c+}cv}bMN!l#B8!wLH;0p=rw6_E( zUbc?=MeXFUd*%g#^up=<^4q&9G|en!Nsdv^NkbAK$he{n=in!-Mb z+spn3)-d}M0UQn~AXt!#)Ti9x!AEMx3wfAfgS-4YLijL+JC$Ww8abDW%^DhL8Oz}z zk^OEb*z!5XiQLM$8C-UC%zEZMOn=AoHe~1dvNcLz2ri(DI+kX_7V8l5 z$!B@??3+A(@{{l0`@=VH{->)~&;Eq;yKde3(f99vttU^;{Qgw*gHf=Hh_$!3YHu!eL0Eg zrRw{pSQFlIAa!H2!gA4KwK{~@VRvpd#6nbdAzqU0wq3|tI9tC-0{UZ5R39Z?0I+`;2tx*5~$fiIKgrvmmvXJ=xfq$L&8nV&6 zJ@aP4T)N)v2Q^&umE;W&h{O{0fS%W*SYYS?7rT#QQ!_?eFy~-}?Si7E42)1q-rOuB zdyis7$S;p3Ml?Ov^1{xLRY2J3k83m{--)P7G;2AAX}C^#IUVfCHA(QIH(wA`V(|ur zoc{1FFcuR-`}rDcRP4XhDwaM)rgWnPegq+y1Y`mEy0bNrczIhb1PPM~5YKstON1iD zZ|RJtn*g*Ds!d^4pbXe~{}K>Q!GdVSrxsX0%G#<@aYuns!*!1o>H1wl{Q&*)i)czD z2A|q?VU>*FN;kC3_Hm3FRy;>$iqh(Y=*Y6^!H-OtXcz}!rnrW;4Qr%}5uEO9#UwEot9=REe9V1`gHgf~wc`S2JaqS+oy zW?=h>L4hzs48)QM)uEEH5Wp|ylZ{z`$(5lc(}NKnMWVlsX_IB;fAeX?{WEo|rcZOs zi)09?n_S0(w#ZDUd;r4dfB*e&_xRDzyLD*RK7>+qZwRZ(mzUTN#0 z?ja6cdq67*P^3_HKr@H4OR#GlrH)**2@t9ZP4GE+)H7lUx{s_{0*GWKRZ`bC%m}1P z$t*9uMV2<}LjQ~)Zo45wW}OKzcpluNb98qtT4nurnIygeT#uyW!Uw{Kt(P8!VdSW^ z*-G4w-9ZV{KxnpD{A10cfq(T8uFjE@v$J?eiZ%!#3r#|pMFX~$#D%4Y%vPd9tTcph zi+x}J%Bo`Fftc=LQqrjLj)&QR)rFVfVp#ne#hw&tA}~LK6v!kj@IeoOf()X>E3@8Z zEcRwy=FJ*=#P48=5@I496*tT{ATNSm;sAIgMTnzDSxp=-)>2@ytC(zN)4Iw%MPs$? zG4ECv(_$Gk9d&jA5&CH&%kH&tzzb$B1?ZCnxGuG%4F{PsEu$Io`{TV~|L+lR0psdH{?_j8Y5;3ewScS1sAm?Ym2Y(;;l^%rK!6@V;HO{Cf6T~PRHxT_R#HxJRfHh zGi_4eLpQh~e%bq-dz?bj(TjmOoYR%DH^jXfv-PQX5O%BS%?f5wnmwFyTIdXgeR+EN zrQU!4dp>&fEAHI+8`rP@uPayn+V$(d?9QF<{^;?Sc>n#EYim9mtNhD=cFyPW&=du` zty*Ww2xEAb!a)-PK`{?gWyjWV91H-dIhV^+e}C7>neyzb$(Fk_OytDCer|d7~*EY`@qq=7HhxY&F#d#@7c)s=a>#gM_?u6Xs6PdYE zrCfe#jAk{W#JCdoU&9wtSmSIGRd(_S>HJm)AJ5FSzy-cSa*3o%plZ-&l1$!{~q&cw=|TUt9GztU!}|ynb}&Ced!UM zPD8xYdQ{8f?9_$UABp|k_F{el9ryIA^#V-n)hrMR$9F~E3_A0WAK>A56 zQZ|iCYsjAx3l{~zkPf)Z3GWM!{Ys*)csa$oevQnktxKm39U@x|i6Q79LkYBk-EQPz zw&t8M%p=2D(=!a#?2k@Qzsh^>{oscWfAj56^$)LI`|nq-{Fxg!e(t?{-~P#y&-?tj z8f0LC9u-AcA_-axo>#+Sse$xy^PCV_aBMHq07^BlbWOZp_7Ly@$Q57p|^^a$>7Y7Q=4oalarL2=rL>&#yn+~g~ zQR_)?j0LfN+BX8@!Uh`7h5>l8jIul{As|*k(>Fi^ugX%T%Au&mxAul$UiDu9n#mZd zV+YMQz7@>bA}O=S9*J>Z_!F+uR8xGu+*3=dT|iCUpqj5d0MIIZ|>_asletIRPHk_OjZxK%k)Kdxh3JVbcs4i%BTT z0|X|HbOz9>ZdeN0YOKd1Lu#>{1$Nc|MJi=57TdSjDpVK)O0{8;k7X72rs#kIgh|Es zv*V|^p=vPoAk3vAsHQpi3bz{AI&LiO=E4n#;?wDMpc9XspxMozIVxJR#xgV_m{0+g zJC9nFZ#g}A)R9=1$p}LrAa0yBq;-${`Bi?mRWK6QUlpvXGOa%`W7O;ADS@6Ds~438 z0}}0s>q|Q4MGz*85!`{l)R$1<(wTM@8x5!{TPcq&Y186JF}O3KwORw(eftNa4_@^9 z<{yVQ$Ftj;-xp^e=TA|RiS8rHGttbTwn9bxz{G(F?GjQqM}JF_8il%nju_G%CX+c>J%+L-mmRf}%yjM<)Uy zFDz^}srp+5QnIimEi$Z7)v*OO9w-G{(2#HZ=DO$4aC-Xn-+TARJ$UeYZr}RX@4WLL zuU`E_w{HF9`}e=;)2E;9UrkuDC1zAZ6c<88jz zc(5kLO=77cbD)Sl1j99DBB>W7LW{PtQe|c%^Q@txVI+TX#vA}IFv@t5r9)u@*$!JE zoKs}3O4rtGeJeE^<@AeYnv4$yfuVKYm~|Qvho!oR`v~JU7cO1gbyG04`XQDy z^NUWY*lrCa$NVNLI7xqFu=kk`UcCAix>m~qy5^30XSZ2701;NHkeGQ;#=HYMT$5aq zjFUAKpnl(R**MMSZ^dml=*rnYR%0FSP9? z2qhvgNQ6Yemj^@YWRI-@C7=|{4eM5<_};K2X}Izb(hTEFj?{_$b+GJ;!ws@YAR~M% zW>uC}_CE94jFya{xlo*J++&INFe^>C`HHZSn=}#lcJsBt>eGM6h_-}zr#*N@-2WyO zQ-to{Sb@ABa&`?95)a!LH6rMzZRRV;Wyz0hL~dkPQBU3pi9)b#n_0B zP`reXma$+l@ff1Oa2Q)w1K((EGMYltA#gbekc1s`w-B@ChDEblPz;+5IG9Ut$!m?) zxT{1HTLiN0qM+HPUt#O7!iOpeKc1#*LY|BoD0f8$ySGGlt`X3&=!+pM#g=54m9$m6veSBzQ_@Tm+4y zmsX-mW}~9wmt3fPXXWTn!64s1qmE%3^|Nvp?QzLFQ7Jx9fwFbGU*V=vuCmUH2tkl~ zgFq;jiHI~5?{k0bW8d=W)1Ptw{vW$_>%U&T`Y%4^Q+~(o+dt~z!>{$;d!O#~^jw)> zN(L-YLmQBYPHl#mqt?fGI@na*gNU&fo9RM{lrOl@1!+f9cRN7pIg@EGhsc2ywVSor zV1uHvY^-8%);J*OCZ-Zl-TL`(03aS%QV1uw6`1G=sZ6Y`a?S^ZPd^6mv9lnD;ER6M ziXxEh62>@41k3x@J{9ejdHR97W0XbuB4d3q-h|_akImQ zRG#So>%NH*roOh4XtS}#Ybi1$Bwumeb-EM@1}V0v{)V6ovq``P1*9u1XYD$<7Sm*% zcT;c;>sb_AT=MpX=9>u(2RdIyrw}g!+qa$@#qa<=dh`Qj8cXy1>UXR_!R*8W9h4BP z2r`?7SC&K?(&(gfKheI!2D?b|4?SAFK;2Z^^!x-|M1Cum39*hym!o(muqr&_2EXA{C zAnoW45?}P>Fez)oJs@dc^9Y&H#~|eHXTwisPh<92TR?9tF1a$x6cjN;M|>}c_GV8j zC92MJZ@Yf3nnpYg%wcE-5eWwDZN8=$N_bXq;`YO5ur(`@@9r1Wyg}xo>rWYjogUVJ zhPHF`BX@yzZ=W@cwi-6d}MNP*x;b_dvJ@yn$*gK4%fbM}^fIvq{p1f3Cu=T4GEq3UU0}SYElL~2Io%j%|3~JP&EJFi9pobh) zTcpov5uRiXV?N_}8)IL14cLotQR76%AQ}SvML!UYp!^IgAE*_#g-@`84wW?K*B^ZF z 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

    9ce#)%<# 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

    >%B8t;Um@gU=Ni|7yxlLXfD89Lc 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 z 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 literal 141234 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⩔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%0DbICSRi1->iw)J4JfN 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 diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/taiko-slider@2x.png b/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/taiko-slider@2x.png index 05b8c5783f4bde04d74aab93db8c17f5e73c5060..07b2f167e05f2ece819d34d2a98fe091fdfccd38 100644 GIT binary patch literal 96449 zcmY&;WmKG9&o1uHV8v%}_u|eB4ucd5#c6RZ#l5%>(o)>5XtCmhyK8B2DekU^=Xt;P zJLmk_Yt5{EXD7Lm?Cd0wn(7c-tXEhF2ne`Jit<_r2xtTd2#E3+NH6~=scz6AAbLh9 z$;;??E*@lhWH3Oyt^;Nz_|rGGGCoBWbI^xq(*3ozz#-otqkMQ}fz!7-ZwhA`5<;_} z!VscR1qWgz63~UXif)PRy5hAo+PHeO1&pYAeEOv8@hP>=@N)96z;ChNOVip$motq4 zl|93#}x&m&@ zf6lY}kbg(jf;eXFU+{kkq`#If_t*X}i75x2Y?TYlx7ktudqTQnCLr6^^go{%u7k;L zPSeHpxOcku|1XzwSuE9eqknh4PfW3^@4;RdF80d3@VvhVCWD*h)0BtW7fO&ry8nmI z7j7Curv!Lx&n_9uYU;NkFTxnU7FgUuKd6I!aXT0j-822Kd@qdF)9cK_OIW|A^u!kk ziu|K1(W=QKk+D_^S*70|6G4#wsP8{I{3Mfw2DULCl~&(;c0izWQS!M#a?su=YOyG?Mmaz9KLDNX=NnVy2wd2mXhZVamgqrD?9GnNdd8f{n2fM)ZmQ z!sRwy(R7-D23D%d!N4p%0ezA!sDb^1?dDuiLWKKhv5J$V&2N82JzuW>J>&I~0cF;k zXFcS6l&|C1VGkLKP_rI-dMWy;k$;(!&CjfJT6{~(JXoSKrNB^77_cngnm_mt+5f@# zwIFVcIX8OnLc-t1T=ww39}MJ_HXr^$q;ST%{~)#D0_3*J@`{+Vmx-GiDPG`TCH+Ul z{otY?Xe)*Z8;7gO$6z#X7*+Rn<wRgOf z+&+&+W6zT6b>6l=D8cDPYS=ie0j&TEbi8k!{^h}Dzj&`OUBsGQU9Qo4)OXG-}%`;P(kk`9{b1SmtziG_2i3ayRtQ? zohV79U%a5*_}aZj6$|BVOmykLp~WybrFH0wC;NjUZ&n-{nGS9A5~G`x(qFMDxMCZv ziVZBAz=hkk3jNJSo=5jUQ%l3}&&w)k*5w2+j;ixLSZ6)F;wA3z3}HhFF&l!u+C|I| zP6gXg4@ahlYKC^-_X;&8i5X3j(pw+b>SrIJcE`T;)h+%Q_zQ)Yz~ z$6HHz?VDVzK=eU=YuDx_j`Rf?zmet{D$}RZLLrz!owy}KVpjgqSj1FXe|w~i1)17{ zL|%c3o+S60_B!7-qW)-~BFu4gcsp{AkD6p>-+e&`y@X&t&E+i3bt($S;T+5B6rS&x z@o!)2-uBgA=wciVtTM+$pQE>H;ntw@yqvL4r=uojX^ahp+HKcLvCPPy~w5hW0Y zL4NnRq@Ni0>vn{tNvNg1goPq4@|>h6On#JKaWAfgi;0o&dF_D-Qc&7ho#p;%zwVd( zwbGI(a0EM*W2VL0&A-l6D`Dqh-zeg0=3B_sw~5PO8qN*h_bF9&rT|IhvMC@Q2?ZL$ zCYnp<3h*_X#B$}a*^0)#-%>;20*3jAg8Mc{hgL|B8OrPS?>(f;>PIVF`{&eC&)RHc zvfB5;YFqLZpXEmfyzMMdz*@)du=qq$@jX0O`7}T5{ad|8^>j_Q?3oIReY%jTyj9xI ze;r?~*=RC+9(HV^w$)3~5_L+@oLLU7OSVgX-XpV(lU-a%Lj$wPSy~!ck{|8!FGnc+ zMW_DcPJ47)4bfoe0Ut$ayq3~t3bH;otEqwCi3aQsMht>%a`R?nGXR>){N}B-1mXT%@UNa{Lx=4=L-wz-82&6JjvpBG zlEXFb%w&n&e6h4P62ZdXz1+F6`Nx7q$$?BPbaW7z_ym`Zmpg#KdXPpo+SW8e1^gW| z+P3PeB&wk?r9YZS2kmYCC-o5NOhORf)LWb8PhI-7>d}9E`r&)U77uQ!$GuPKrg{<^ zjogSyl#7#QBPA3iEdCJ_;!4oX;oo3Iz-iT@peuM^gmpVkAsAB71knl5SPae&6w^B` zn(L!zt~V7@D%5E@N@`{_8E2OO#L(lM_If_-vuu`pR@tAr0QOaOLzFOznv;RuRRhZc zLo0crb)K$(ju~Tne-WY9>Zh!4en0OG^id^+HM_xw@4->T0js~QMSIP~z7Z<~%d-|W z_5O0ezSHP3w;IHV62({M60;=|0op7zF)SaJdphmy>NjrIMy`X6Gz=O^%m1d(U~!3J z|MJi9ByfLg!3^u(`=$T*fKlwff)0}cnu0TC26pHF!K4KgNnp^!n;*;F7e;pO z^T$WR-Or?qjpz$D4%KzKABaKaLnLb0Lq}d@Tp}8?Ed@gql#~-w$M&}$i3nGu5ih5u zlo}mGjZXx~d;Dev4CG(MX(Rru2lCdM;}l|xnqi*R8)(+pD_Y^D_m3aKY&p(7FY!R+Y4x*zJg+_yss1kPhJE$U z#G`JtImn)H%>vx{J?!VBWtKLB*6m{L_9z0`MiyXvh;NV)JraInDE5rRgM(Aq{Vm{# zP&=gIQL49{cD^%#mfR)M<{y#&##|!SMZ~gk`M(Z(jajsOnB5n7xW2ZG9|K5*o;!wX z&T*JZTx=r1znN8 z+8teg6JvO&?K5%>uP1@I)b zqy)p$H-e{Fg+Wy^N+V*uLNq@Ur7-A}@Nj(@HCv5me)Nq|T; z7=Tm=E;WJoEpL;^?s=O623dLJkJ#PbUsnZXVp&tMxh^Lh_I`7M94A_57dQj10~QJu z>d69yHE{otJv|6ow-@wH^4+NmuWbF<&wJ1S?fJ-~zdA!1c?6RryCOJx=DTT0s^-M% z_!NBxOG9b|lc=NZhH-8GxSP0VqD+S8NeT7ty7@(M* z3fbR(+^zhJx7u!JmV&CtQvHrj?1(usQf`G7QS~JcxmPb!mIz4%9+!eW4xtcr9p1nQ zW+pB1MggqK!qB-_*i9wE79kp;ulJQf|#vJ$EZ)|^dA1PVDjtgyTugPp;;O}pJk)kuo zz$wRlqcG2Moj zrV$A&8Zx1RSs63@xlXXpu^F5eR%X+b=2wAouT59t#j!wRaK)_DqHi$Ail%^G3yFJu zxQTQEf&CM8IVOOgrVUrDohmu3PsA%!DK-(kAJ#{O$QG3bKaNZ&VAg2DFU8cH3D z0C6nAZV$Zy#;#%bbz`Iv*@uyqXdG%LNe3e*^z&@w8g3)wX2oQtyxsG0~LqCQ?&Fp^2_sU z7^Niel2Y(#q-zJVFievS9cDv#@8n2s^}xs{$1A0}i*oc#_u|(?(>V#L`RR_k$e;j* zwPJ;OPO>K{oJw2~II&4L%&^cq51}5qI>gy$P2;5+9Ta7mi;!YaC>)-nG(6jwNL(tN zheEm-S)%EKK3LM6yW@jhB)%jQCtDGqgfMcsS&Awpp`}MzKPD{Kh$<7^1%*}Tqy0o> zzfk(e6{1dat~7aG3<2Ro7CeqSN1U4PEjk@Ng%DP4)5X*8^8P(@g95&OR8@(m;?G!v z+SGV4>%s>1o`^#2s?95dkkg5d?n;3>gaR9^`a*$V9h--g7^>m#M0t+o19R_8lSSEF z-Su1Mc&9r;k1>V*5ZOj&(C&+Zkkg+Cx~&-QLgIa?`0r6hBEJ7?35U^cy)gN}aXm)_ zjax`Wl)g`zQSWEk{)2WpaX>~ES`D&wfmqkGPymI+im;p<(4Qp33_T!fwbv>Ur|OGy zo~dGT27#ape(92I=~VWI?))#X{1b#lCuKEx$k)9{3}AV8sFc=TmNaoJznfu6o#Miy zW$)()xi{6=s_AS|dvmjo{EKp(AD4-cVi0tSPJ76BYgHD*^9}U1ut4E86+0$T513R< z{T7BBaf9yIq5+S4&|3x)8mcng2Tf`GWG3!}se)w%pqK$caP7+G+$wQHWccY$Kvx-> zEVyYb0$e9aS2i3zA*kq)VgX92lB_M2(>T9U!Z53WU9iRi8G_%LpBnm3hY0ipdza;xv98bmx77xab>#U>kh)_qsrl|YO6tZ&~F?7kbHBjtgsz3Dq)vHtbxef!!WAJQTP%*s|vb>&j8d98qb5k%>1LJvYCZ z42_sFSC!m`t!9;kF7T5&^POD*CeEAaax&gPQl)#TP?_Kl&lCGi7qa%gh*89zC+8)b z7Rd**A8WFtbM-Y0uAC}xIl8}mwQv?xmkgm^k*No4A|6e zDpSXjHOi=y72|$XL47vxYuAD*uDt1$(U~MpC6)+$@X+(3wwjYh_|AyK(_TKf`sGzg z7diF$>!ywGTh4XJi&K&46htH;iOUyAh1q(CCcNAtmeFR$hVm1QfZ|7yn0HgddRaZl zw80Lc5i!F2pt|CrpN_A{BfBxK(CASpO@egiOK5gy@;xu4Q0pQ<@~|utI#~*zBM7=` z?R;*QL?xH*0Io&%Iil(HHZazH!Dgk7ImnQuhP{N$(hvm_lIP!$})ny~PX1DNi{@yx>S)d^Bm)xLFu{PUW4s<+`KDeg5GpKmNt9I&ezf9fEYG8F0aA@J!y zZDBwI|B&Z5H?`*P8}GXLwPV7xaF}a&3J*Z7iqYD+)v;0f&LA6a@zExnH{X)Iy$%g! z5Eb()*(p2%KC-_1Oq!L0u-fD2`4Av|nBa!87XQ%C9La^6G+Y!MJ4F+o-W#ul2JU2F zX*h`RDXZGFq!6bVUhY=;#rT-|u~n#c;q1djh5BEh&Lysd}` zMIIX`)Q%(czLuLkC}-h{$x~z7@{#n=yeON}y_$Ty)XAbIK&S%4x(&*)2&XH6UpLkh zS>BvHZ*2hMuDXXn^W-%;j~2XBt`_FFzk_`zOT#>PoB zR`#xV6nBI5SkAHMV_-{FZ;u5%4LlJ&>cyBz%_PQ^27Zwrm%p|+IKb#VrJs<#&!FRrqFJaZbw$2u7k2I1ZEdoJrQ>iG4LMMS`uc*qXQo=g6DMB1PAm|(7 zGnwz}KZDTq*7L4QX|@+KJ=fWBKZIB>u-s@314+sl?5lp!*C$X2hSC;d#vm14g;3a* zhhs?H4{`L!rnDixU6-9Vz7qV-2}EJfj%L&=y4s?-7rc5V__Bo+oyQMiVJO@JUOydQpUD@eH{be%__=**f;JA#Hp3w8Ve5fSppj855SP2$ zsQ}R8A2O72^V45vvnkr)>B#|&O*xvl5Fs*g);k&`W7}Op;ujZxWJJyP+z?HrR0B;& zRZIw?VPR(q^OV7>T2!2ylfX|m>2!MN&CX4;>Z9Gc^opj3Hp25U>J&d@4z`~^G@3nd zJdr~j`#)!sa5Ny_7lfrYWBS-=@};TDI+DKxHgYlcMd%?B-V;JG4FLdxJkj_HWMz8e zhEuK;O~dNwMRwLN?oWN@I<+Q|4Q}(+;qgU%$>MqZML*!>@Ud6P0l(+5pO`5cQW0!8 zDdpiYeXP@W=NB^gP@D^Z^AUOW-%rcaDK+K!IK&il3USeb1G2-LeD|MHFkm zC52k%M!qANv1CIdrf)RHc_t?9jI@j)iqd-lGe3a_D}0BZyXX9ZuiEx0+MYYzTs-F5 zQ%I?>0&Stuk1CC*STQ)=Cx5)(?ZDsLy+R{(=$?~`^HsFN!}p`=P0}(2oRlMImIme| z7R-@5m+&8)Bq%cS>3buBH9o#pKZ4izv@z3px34XaHLSk5#KLSzB@PY1Dr;zDS>YCa zKLMKLn>4SbzqCqc@N)(5>c(<9fKvhdj#Jm)iL^CbhZxqnLqbI>&?=! zS+tS>NDB!|SK=fC(|Z7dcUaA-LN&XvFXab36jViX>SpDO|*iJr|#<4EpaA zqH;g~x((_!)RvCJ?V=i$4`YB?xfKHS0NKNu%scYLLJv*WS)rc7$?ZRGfiO`LWd&QE z>8Nm>uNX`aL?=?m%?np+YnC5=3m2r8{aQ~%9#7~W+&dfg7Y3X@^zeed9*aov8<4kZ9Xg9JCfq*qDREU z(AfL(;!HpK{O+pUH%*a+Le-o%zd^~r)pU3p5Nks1)sqp3SP$8#@L5OO4_{r$a#maD zh-z;^YHSCKHR5^#26|v!Y$a$9UqW*ef#cYN+CWmu*~K!u>RXRq+RaJs^$AWQWPj@q zGsZeIm_f1Sflk7l>zc6hELg1Yan-v3lgg!Mr3_z9P3c83{yn_Xbmc(wHDVch9@hpZ zKTzLTq!(J~W2ztEF@3Vb_-RL?JX-SV{XxguFFu4QF?n)%fi)^Snd@Vp4*Zt*4X*1^ zdV4m6U8H_8{6O%6-2=PWs`AMQpX?!jJNQ>(6#gnmZo*8=kFSHXxbACUo>@ zZ4<#{iqjQ6O14}WkLg895dw%&M?PXIDzApkqFY|;ob04Hv8L*MiDD*kXxF7+RnLIe zZh&3CQ|}XPpq7n!8bKP`5I`3NBI-E7xvhWf5$pks{P_Ip@%#ERMx(`Xqn8=1!`f8( z(^?>+(N!8MFD`A8D1+@lbdM;=F(em(Uf&n5Z+uf~zNcIG)O3|ababoM~W&$=`V3aye7QO?gK%1wP!K9-7rc6%va2R$G?9} z7)Tg=fuqLku{X0sYzXPQJ;Rt!I zCWPE-z2kyyLWq^zsa;N2@;#I?@@d*J@BLY*@!yxWbPk6YL=cReA$_D~DMWzH7Gv!t zRcu4#F=8r}JVjsL7)s7LXbeO%9OaLYQfBw$lq2l-VA-od&UnaJ-=3b}R(jdkYA*h~ zT_I-el)aaKQOPxWt?Ek|Sw%kjfopzDVv0WmTPoGMBK3#%eH{A!@`{fhA@BjyjcDIqV%7sJcOyLwAcnj`ef(~lgMx=62Ah^g zts_0Nat?oDJTh6*>H zsPTKW4libV6&Qt$X@Q|)AbN;h^g@Eu`96=o|927Cblgfj`cnlBdXzjEk6G*JN?=`6 znlK}#PazWF?yGs`yvzdI#)(6!n9eh=SYVJY`Mb!cLOLb{fV%0mMkr!M{+B)YY{m*a zqFTcni9-_?`Iqs#tJ9k=+~NLV$Wo0-lNt~6`a`3zK{91$*fVx*)mxK^%5n3( z$UAsHQ<2DsV&TwF1Jc5mdO;}Oh55_9p`UZLvVLBDi%*;xV$`?s9MmPl%8y0-tF#nM zSl z;zjwEBTB6TS@HzL+lN*hhsB>-3)f1c&jCXnU zLsdZ6fZ@UEm$vibepkHTb3gB`XT-yw;)aPfD7t6FoqWPH>8!DnubR1Oat^xWg^93t z-^Zm zJ0)6v**U`QeV64HBI>8rc;_3kU;$LPwh}IeM;!(px5RT8Kj@WC9@`zx0+Bxh3Yc&V zD9P!1Oohy6$F=QmL0CvffgAJVtDK^2Z1C}f2;>f)j9-kiF2sk3Jr7m^rSX+m`_r zoBDgt2Nrs9MK5!3Dssu&*X}DLPH3sc-Lc*^TpP*|rGqb?lnkdS$k1!<9H? zNh;%(B#y}u7C=uyN-mfL>lK4|(1Zp(SRuia+BvuA+~nCc<6eERuB#&2KQeQLAUqW7 z3zz8fbREk(SM=Um5LGI3qY}ZIS57YDx3FC{@OQ#_F0tfG1IH&_< z^{pgETkHZmt-Y~~L>9?I70R9k#y9BCB&hhS87hAOR*Y<1)#eQ$SmcQ<=+OVjy23Q z=lVB%9tp#$N9u3pj!{H1W7Z$KCbYqj5-f4Gy${vl#AxfKWT%5)i@GNDvr_>qzPOGhs zkdDiZKYwO;7wsK*&3&zJ6GtU5{DHR`2S?`2bZWr#w-`^g!S2u(W|w=Py|*@9J%=Z~ zwJ*<7Rw+U^|MFfR$^hDYSRmB<@o=ER^MSqkP?RCjs*u-j`OVR}jvw$ZI$|+h+60G_ zHSQZD!Zq&t9ShDe)yeUoWY4}PZ?7r9H7i^f5wG>J`NI=Z!cm3BHU@(_{S;rrS=#4feg^uB2(A-Y=rjhWrcY=P81d9Ki>IN@JjOZ+kC%h|^ICm*Sr-X3^T zvvCuVY1c~R#|A`rg!!8&f3!HAS5zIwGhO0u{bfa%pRgDg7}*FElhu<8A`2JjgsnvG{v_c}xM`uEEvYR#qhdlH7%N@b}{R zAnlF31V!zYs*y0$YdV<2Z!BtKK@37pPu7R`1WhhELg%mHO)b-e&&PS(-@IxIF+Xl}d2nwc5awW&asK7E5OE%~YZUWD zLWfp#O&0+C`6L}&Lbv|z8-h^}0@z-UhiDRLZaU1q*ZSd%m#R%;g3M;)Dj!g8n&^j5 zukTMd<^}rU=b(=uP?gzChThKX-b%`q9;yYB5+O1JjmEeUgNZa*Gw4;p{AwJc_>?B; zi{GG85+?8q_1z8ao0LK#Pe4K-Q|nuleF8Guc~rp@f)9wT_TlZX zKW#QX$D<*}GXf#I1$~j`GI>3-(|LYT3oz}?OU}rjNep3gY$q&b%us+U!;{?cH}Vi{ zwmpkDW|giJ3X+;}wooOBpoIY$Xug&C^!|QdUew{l=)z@Y0?uiIP7dnp?!H{k+}C>K z3|OXM-l2Q2wk+mw#egQ^yh#6e-);ca;T_FS%$D|jOaP)_XDAx@Jc$7HnbX12si)LR z%W!snQz0zqvdR&khfQsDtmEUb5_VS6@1Z#(m;^y&K0TImt2+jv7*pz7juvvzzOK{< z7owIbr;Ny-T-7&n(8a!c16vR3>IhRCnAvIPxqftRiX$6uq361&_*DlVKWGw3e0gc-b4)Y4oeG0{py%T zD(EO0L5vzo=MQCG;-@yr8mJVWh~_IEe|eEo{^s1mhz)&XIU`mD;guaj zxaOmRt{(u_L)xPj$Kx-P68XT}4Gmvp4_ zfrU?8D}h>p9>!)4yj&ciAT zDz@051?MXus7u6!KaG+!qR%+vlpG!S)RovkeK9BtBhcv|1ixk~>0l<({lPtvSe;W* zml}^<{o_GDO?!w1&{#df|4E+*`bi-9W@f~C%>`(-Ldu}!vdx!!2Au9`)-x-hhVQ_~ zYwkr&IBir`ojHz9+;tRtzyMj8{3B}U47Zar>KBuk1Jj*|?RIfjGA0j<)ler_nISCD zWk*U}(xmNn^&{Ks2$d(^(PdeOgL_dX+Jxc)nQG@%F5%KgySxCAJ3Vt#60|oKrX=2A zxuRmqmEf)rI;ezZ4I%NNKsaqLI{55c7C|o2__O(76S>sqH4x*$Eo&KSXV9w$#e{e? zQ)iLN);DFq{XO^9@2Si5S3GBs5=1Q!-On(CK-8~zAW{Pipd0r(C=$VRGv>ysjkrxk zOLD#o@!*NdKKl4sI<=MSz=4XA@7~l>Gb>d06d-B+4!0@^itKEKUUNk;a8c?C3@4rS z{qyIe#b37HufOtCS%~2(s7OTjL|V#)M?EkD>9PFf2H=H4_Ec@9~uYU1!Sa5y&`gX2oP_qu5{As8MjwE3o6ka}uqKQJfr=xV-}kX=^F-h} zbl%OMi|N>GvHp4Vh&_dL#mI}){s@mcr=e6JI4+5f*@T+F%LZY3Ays!Xdb?d#-va9- ztLRzrb@oe&pIO6R_cje(2jKAEO91n87^ur)7svo?iHP}WUuymDQIZvihhBye=ARsW z0w*77d+qZ>Djh=K7tW@MvvcOn=UVLquD}N={^UBok zcV>O;7cr)zf&&+_HAJntJ?)gxG-u%*WZB5ROIbQ&Fr;jg#ii_3df2wOGcfD>k|t(FGY;8fp{%BLz8g{K1`Cbc zsjzZCR5J36m0;$0e=*=QWz7@*eq|`{;!?n8*X0&I%n?%j2*SU53yH5dwM^)v*%mRr z5uW0$KArHftl}`qv(;g^rd0BOtMp}hBk_7Wbop8P;E~tKWECZR@zEPG2l$*GT4aMw3t1$T0uGr5{CSNf&cWfV-@d}wd(l5@|-wPQ~w9xk>m zIZpGULW4m5z!AC;f6K5}_SOkr5;>l+d)y5Hr1wsz2&}Tw4nq$+SaBFK<2-W`{T4aw z)AyQ`J^@iv$p|D~G>p9D^Its46_Nl7N)plCk+Qw=k>x)ai#ca$38&?SRQTQ|4$>tt ze#05GOc*5KN0wduh`vZ#@y4b;N;&imTb))>3SY&5v{uPBQ0B0fNBeMQ0ER+I*tH)c z9t`S=k!s8L>?=;#js4Wg&XOs;%P_>ODS~OxJM^``SOJ0N7Et7Y`J>lHzriyn1&s6@ zT$$kroB+CG7ec=ghKD~e6J&&ux;e_47}B$0c^+xqdsu@So@^duN}W-z{p9OaqvVf^ zKDQm^DL`F_RMaJ!@ul}F8MR@5!>aFk9co*aJ*e>>yDUr@PL8U3JZHX;C}VTN=v&VO ztT8hvyM|PFtHnhVUFQNB51x40K1!PQh)dR`!xFzFlgdDiH>FHli?T^1Ti^KF&APhN6o&rhuq16kc6x81Le zah%{$DtWkHi?bLY$L@OX10gFuW3*;lTW0K0M^1%Fsa{C@Vmyy1Y@yydujFh212J_2 z)}494fQ#~%8cjULTpLw1m(85j%kasWUm$!Z{8#)7(lVB4dl@W)KuuwR<407Q2c(b1 zGrtJ1BloK8a{5QihS|HiV-0*Bet)XreVIzIXwscxpaH8T0vOli1qfdHtnyK3z;1>n zs=+NqA;^!niKB`3cwv*AjaJAsx|*!hu1!}K!m?UJudqYK0v2p`FJ?5fMoSac# zbGJK)C0@8Buu!AtF>phiYZWd%13szRYy<^JpC+S|&l+nRnsUx#j+_kv#n5yN#s#HD0{(Y9Hfw<%@`@9=kbd0j;|y%o72BO|^o zPNixpj0SAKwum@Wtea*ur(o6(EWQ>z2FkK=!PPKk#?gwRu4Xb``VS3cdXL!N{l2tK zTPwqphSkUSQ{lf4E?$kk0NK<`L)n|Fuu?gVd>efpmll9%Oja z*-7bJ3ul2h#7lj_qQt`WeylAH5X3V*-{>cfO}85l5X}Dwik9|KnY)-q`W%Ums3(Sz4^fO*x7i zyZyR91bsqgp48spypj8M&VE{W4LXWk27H?+2>bQo!O_s}eCrudZm)Pu;#2zxXaGm( z5>MqZcD>F2n_>9x%8x1TyZN|(;6ybjQ9gz{BCBdH&s%6jxcKk zi}(t-&7v+0jxy+tOlB{42^rWqWR$3X_Er>XL^ytLz%^+;$wgvQBR%}ygftz8CiGS} ze5>e`Bv@_$R<;^Jj;0d#s&xPn9K-s-q2#R}=B^gpMsh+PUMx?g4Abrch|}h zP%;h?g3Qc&r>n0Nczk~Q23Z!>SfS;i&^w!0+k8*b&6 zKf@V^U9^34WAk&hYVT5$bX!MHIII-HdrGc;xO!jG9fPEtIII8`bxXB6^-chokgke8 zM93-paqiebD)-+KM+CQ2_FJr+6gYctn-3w9AiQ+#NHb~TqHLv9#iR1CT}kINP8Ogf zE{VXP90L*()Zp}ijFP5=h)vgm;_;n*#x_5O`TS$+BhhK|PA*dGTvIY^`t^Kak@qzN z&}1D!Gv!xng%s+^HgKMknE?1eSPnu7YG9#wY`EkOmx`q*=vN5k-&363F&BY4=_qdV zcP^}8O50OgL}uFV8RE)%Uq_5#t_#m#ssI9Jn)NrDoiaHye-@ARs86^V;^t*nfKr^U z#@AJF8>dL^3Ju}@KhH7%^)o*{9$rph`%DFH9Sf>Xj|{F5ZADSm_H-yb2Z;PQTzz}Z z`=jZRY@l+r6F*1mge}H?L8wd@c3jW-$*VEN?){B_FPGw09|zDsnX+%fAf_BLuD=5n zuiy)3u9BwtaQyDL99UyizzIxT-ZGM$37lNm6+Yz5{0Qj_82+5y9b|%PiSk*A zo}gs_*2DabNF2Du{q9@IS5Lxv1ae%C{=ABp0lb>PSb=8U?SZW9&PwO&;^-)SQ%iFF zkBsU8hh3=PqXb*CJ0(@gu%viX_iTj5d+eX`NkjW{?_m&6yjPp?U=kasLyyV1Ym5;O zZ6NeX*#Gf$^^jzx^4iO|l*4c8{9_!IwPI{#kIcnC>LAxb#9n_Fb`b$O~9PtfVz1fx^mW8|Q zQLKStnEUErfX#f}MfI?2hm=D!DV`aBB}luar(_@dW4>jJi78@Vdx%t-$L${lGuqmKDlw~3F1`CxapyTqVonS*MMY$K# zTnCTf=YbH1Gs%M|@3!#yr;0Zc>fpfi0;ZAnf(Ua9hK7Z&IF33zKcJJ+U#a0=zJ^jS z&Z{4GJ(=9IcYZG{m}I)tTKi}Xq(Z<2^Nmz6+18bODXLy)EB8}iUddNJPFspMT+#M0 zzQ*amU>B24q;6t8Vd3Q=>H^Ju-?>;p9AvlyU#+Ox($MmmZXU;3wc@d7qe--hRES4M zLU>MD)Wvni-x7ya8(7vMSi^|e&Q{)zk-Ga@*_HnQJ=p|A{?WfSkz5_Kb2)<^J9G(C z%+Ap?jHVjDbO*kCJq=wr>vuVjd~r~kyRV-9y5G9vj_<5tIX9u<1f3fp)NxTdr)p6d zENBdSpvOnBiPY|{QEH=|ys&)tDs*yed{VZPYxDEeT$*%TvexXv>w6qN3hI_l%Jv!a z(est4_hiM9I_3|PW6wP!j>i3K^HhlIe@^5@Rtr=_Ke>4k2DZDD2gF%!G2N}+JA$7| z^o=*;MlM6&;S$vdahV%1Sb!0&SrJ5B>LM~V=351}kO=u(PIrozw%n|pbod!3HcRBJ zp_V$Ee(4ZnhH^Uj)lP7?o%7v3Z(YOZ%E=m&^;M0%Mc`kluZxE3Go~}Pzg@pmiV-m+ z`VuD7{weWwKEE;RP_(xfIAvk;B0y$9Hir)cTAHTv@TH2ySzJeNDhw^cw>NX>e|G&y zNE!YUtxCLa63Y11<9Bf9{81BWe}fO3S|kY=OeW!iwH%kppDR+({$Jkxaqb;&4V{IyMsYGpQ= z&uC!mXj0UYokx%)e(eYomA_iQ;m~piDg=%r~!5|MeJ{-w)0bexm7=b`F z&es4Aj%en)t=J1P=3{v7K9uVO21W zE>1<{a%?G&gLtOI27~|*zLWa<=mR+&)1 zFN3CNExwomXY_v_<7JxQxJ}>j&HHe*(^w>d96V8;aJV>`%;S&3zh?F-o2z|X8-x4O zoLxsNs9-ew78#%$6X= zp)=8e#|Oc5{&FDOw6*e=F#JQ2e+N>NO(L-K3rhxzrmFl8k*O7{3;b7pk48Et?Q&3< zs8Cs3p;pa#t(zlP-hpB$RcR^}=0xH-``l5QUaeR+wpsA-v~lRCaSci&g$L~K5yXuf zJDe=)!NFGGF7%~8{NTS=aVFNmZqBdu;z*ik=OvK+RCMrbHw?KZ!i8yLZ^olOIbo(g zg+$ZRvjr<=ZgM0cWRYIRSe$2f9jH7vA3x&7Epd@h4EkdFdMaAvUrhl+(i{dqBxMmM z7fbL5>sj^pg~An7y@05T+0*@pfjMlbELT`ls1!riVJ2R#oqL^{Emq(2x6m zmT_T^uPt+h#+HUY4?B!s-8_-U{Ct+qq#FZ;>d6`d0EN4n=CB`r+hJG2Zz) z(rHRd1b#Q1e{}bs%SzAx!Wb@txXBVj9LiMW6$X;U;Eh;|gQ9TJuP2(>U7wEm@O9`DMrx>k8@prx!=63F3 z2*`fw+YVKUvlp@LyIA-EKTn#6F?)Z+woohKqg|ToQnAPC={U<P&gF|H1Z)H{g}-rqSdvR5CyHMPZdU++!(Nha2@!{656 z8A@26d8Fo7KLsE72%v{;Vy|=km>7&WGx=t=ezEcyR&hMfW;P$Mne_>sS7{3(^acAT zFZKN})2HE_WK;zM9UmC4k*8(a4n@BR?aq&}#ifvu%^tbSb+UKImKogQNxbh2lrTG^ir4Grca&IK z5AP^w9uk!Yai~e%pd+~jWVjd+@4J8<+L>nX5wioT7^9GOL8AcF_&<^8Pqjinhy$ZV z{y=m?W(46OGy3u2vz}m)NWcIId96#}AxPd=MnFa~?eDW$oUC%G6(UPi)M-*cv>Ev$ z@-{q&mnY|!pH6M0yttxYPSFh4=HaiGq24)0$Xf4M1h*~5mb`hL&UIrYalR-I-#b)Z z3lF38BmQrF1eWHPv%-&;O6`emZ>gDRdppy4Zc~d@HA)5s-uB(BKj{;5?G|up$Ra7c z?GbJz{XQek5k|u5>Ax30mD6Xu9$yM& z`J}ob4(R5ozERD%@82=}GgT#cb>!hk4RH*8-;}z1>D~B1l9y>IFhp8}#RX!Qkf04C zW@yvul-1y*sSb(k$$`pfw6I(S>?H4 zY0Y()1fY@{#Y~9hfAsaoB ziO~=|Y@+oV#7|-Kn?aRlbq1QJ_5B00<;69(yj7lO9gD;CKDif)yQI^&`tOe)XUf77 zK!_w*aw-HW3FM}h)uB_ihOaE*_(@$BFLAhTeQ?T^ZJz%BuHeLJlJ zRlvf)2CRKubFE?ad~$iT`=C-?&X! zWZ6kft1T{8JOis<(#3c3?jPcb@A#e-!S5O87VQ%d`50!^(fLsnctlG7eR$7ZzoDFn%r;U)n5Ded|$Ctv&oX9KPMDwL#o99>2hY?dptbk|Ou+ zAhL;>`bZA-z#2hby&(IHQtCxIi6uorsb|WZD3dV;293cXqpx4ziK;LtIZz82lj9E8 zuI8v3n;fYMj+iQGj_Ea1E6ssK^&zN{lIGOkU3?C( zyrwdJPrKm}VS!QTAIaf&!?;u_HC+abB!?mskK^dQo?*_<#kpE(PD)y>|Md*x+;?dM zY&i}6xtousJ&fi{z(JTw)k$d)JF*jkfmK-g6pWugA0$OwJTUPf?*9~QPi)PLjDY%B zU%x@9479MNNqAJ`Cph&dV7}}#PlA9?ge*lxpI_e-^q`C5eIH& zT-Y1Hi0y}d$LoN6w5IbTRd_^7pT9gXmLT%MO{|s-tCmUtVC(OM03-HDtfL{@N7)^6 z>t2TzIS(O+*W;`|S3l@NX;uY~d=-0t;V+Y+fl*h-98!5vK!UMwWcQ>Q_h9h_SaK-_ z*Uppc#dT+^fIB~c!bvx_28C@CPk8M-1R!9-4nl}q(adYJ8dQ)TtgoRj*UW2UFKee0z|i`3Od!lAf1%&4{pJ3nVlPucl7aFS_N zawHWTH6A#*Rnok`$d?IgHav{XhULM)DqQe)5vs7ap0Lmg4&9Dj*Vi+rRI<`kEigM8 z8^r-e%P+#%mYByo{$M?=LlGD`3Zv6rf~~KdZ-dX?Z(!m9v5gVuWW=SXar{V*jWjcA zHZUT=iz#koSaT%?SMsy=o}Z$4ir>Eoo`MjJZo-xqW9hjtGDu4`*7ROL@!+R%>|U{5 zm)n!Q21eAsu2F?V+!mG)`MyocGb1XS@kLT5$qmH^ieeM`=+~XbqD6jpi`rxzffkuq zd;wNIAAKvk`!=3CgggHZR+ZPgx#KPPd^GCGPH4O!QKs=0!6Q=o+*8%U37HvNpyic} z9Bus_m_BxM;$c*a7+i_|rJOw~*?SXakAQV6^!8=Hvmj+VW$nLZPr>Hj*VB^QruXB% zf5*&0VO`ZA^LE`jKHvzE7x`})*~nh|cW^b9JrzqX>_kaXid}N&SF!(>ZZbSG(32V} zatC_50f{K(Cv_?qHHE0i2ZP)>UT#$6rm2Sl9YvR-(EV_4AjIe!Fo%bYfg$Zr6o(eY z+o(>6BC({X&9<%C8GHJaT`WjAkvd3ni=(l;h`5E)I1#()b=l7_PDTbczZxs9Y3g^G z#$ZKp{60MKV@y1*nw7>)yWNJ3A_JqvXJKiwBAXLCaOCa)VC0cn)PRv;AUA}ouSITf zUX4Ge9>kun^Xq5JX9mpaXJtC0K&cUxn5yVWARS<&f{l8|u>RSC8+`U44*Xi+{_gCb zEh1Fgo*TsS3$fx-EIt!3JNVmdoKiK9-@X@x6N0=;%8s;xr441JJ<5=XeZMd7%nOOA z?+vWfNNJ9iltPm{$QQdZr7>=Y(;~M575FqXAuY;evE&l0ejfT(bjmcHq&@I2IDWrC z(P@i|T~I0_?}AxA6@C=;A*B!dls~Dmasya)0R|)jjp@Clnn$&WY5~;(tRgCT zLM`}G)!aa_78&*oW5p#{ehJ1->!fc|bSjfOaMwrb;ttATBcIK1Baj21^{)zvf(em- zjZFFXP$C}2Mf68{LkUZ~`ENQJyEm%RB7eR@K(azzGMvTor(*T<(YLGxw^u8A<>Vu{ z_gc>YTU$rK=V1f_2-&4t7#X4bSrjo52_a22U=+NIfc_*K;0(dg21a&t+RJJr2mmSt z9QYLq6XF23XB2}gFn5d;Pr7)BLsJeEXF#S4jGU~rn(Xb2KgICo1wAj7rt#qCaeTX= zzOM#G^9UFP$%};b3s`0}v>Gd}z|xD6nb(pccb{e4{h@lc2x|^%xubmD$;g?dZJYXC zs9C%M5{inbuv)6z)0)pv6H1CgQBhQdkztsF!{+cP>FL#XS(>yc1}aj=M$KnN^)+dJ z&YGFBXQ%m~L0NF*RTy~-r4es&G`E&Y#>GaC{lThdVts3}ic*R`5Bvsq{To|hH`=tD zj=qmH`6f^XMnkKx_R4h5$FAV<@4>DRr{}YAqYM}s2DbeXRzH1SjY50Bim68hc{;+J zodBs35L!b`Go#j_RWpa4QS^?XcLcp-=otaYM465V079#%%wqp7q{<{3b1omw!m4ka zF1rZJF2T_H&JF&a_#SqA+qD_Cy)YE)BAtTot~4qn@?KD1Nk$(b@_*5_o%r7-BtD8t zA{rVAMDL2ylr&L(kAN2O$!Hm@yaH=pfWD<|zwOfNn-ur_J*IYf+>3-lPT!a*H!xCR zI<138eqa=V9C`aak>uR^N~;ip(_V($000zDV*jsEErbTX0y(8WE3MHTse?Rgp(I@16~&$!eqf|7H4=GTQjz#b&j#wZNeD1n zat_8e#gkGJZy^FgfYLN}|BTS82N+4@=k+_Ke<_w=8@q(NXPf@>I!+8e{=MM8Ir09s^bvFa+U zc_I21ck=BR=Z}v37LRy} zrZp;|0KnFlBRhbpJvek5Y)h#kjHBP9lo7Fe57g+F)Un;-Y>aAKhSuWrKU;7H*mf09 zd=I=978WW3FuND`UyEv`E-$j! zFq)U{H++k1@7+=|w zGk9|B2xe3}LaAs?OP&uL%dz1#T@YN>T5%FL z`~@mS&&(V9IZa3AsYOV%$@HRcF|vKg_9N4eOfRwn$n?VOkzc?G%uefWw(bn z{`D9aNFFqLfDs`WT#3ux+lkWJgyG-JPE0+BQ;*c^runU}Wf^L5nUNM5r3{Q}|1Q4( zi_SlDO_6=Ss6DVJet^pYBSK)B)L{X&{{~iI$%R;a0eYLK6N%n!iWB(NyHJ|( zWu-~0q5wcjEkGzFxtl1BQA9P?YJvo~TgZHiGCf#*4c5H~ zJ>yNDbJ{V>+hyGOx5yudWz}_UemYN6H!uq2M=8T2Qu@rLf?ueSBSMJp@3??~hy&tP zPeFMWCmxXwY3d@Q+D7kUn6g&wu16;G?~;qK{`VHpbd*1Y2S1OQ{n7_Y8W_zxV5B53 z%JyN!rC4)ihf0cCfubzj|4~fucNL`d#A9D&R&UvR_6ZeR=iUy7QC|qc zzagr{wYsFpJ+?70WR8p)eY)yd+Q3Lig+fI}*HcphTFC^ zGo>gvQcxI~9-UDg5_uIyHT%r1Z^rnxP7Emh?ZiX4_p_Ki4A;e;nRx|eQ{s}!wqm0c ztbB=y_0L4FNEN>8dX%R5p3nR!3>saKBm_o;VC7S=?N8?Ez+_>1Cng`nWfc_OWHZ zP`kybdRLUmi%3AJWuX^pwij!kh4n8=uY0s-#tz!Wz8~X>Z@YxZ<9O{iFsjvA_SYg; zoX{gHgQCErAoxQ{pSdLBQN4ff33;WXo=0BfO=Js+dqg6mT74q-v7{SV5LY(oFW_e~ zQg+kC+81NVLNLIlIQT2<{5GtzXWc827kS51J>}0wVB|=O)?JMimvyM5sAX;cPq61Y zST-}nTb}XVeM;bUNn|XjsZ`1lP@#<9LIu$STY^odS5hl&79uIin8TyS@Q8M^ZVxb$ zk)tJ-VsJeG%*SVzB&6;?`ReS`RzG+_D`c`zZ~)E`Y=z<1ZFGg;pOYXy1e^s8ro$p3D56kkPBFpmI!#SmLc;gqvvx&!#6bFhh#zab=xj4xo zIqPd?feL9@;Flc<566{~^>+6F^DXM{xDG91bw-&iw!9I8^V$HrGK0szf)m>XbuqNO zXg&rU;q}oB10(Bk_G>YIy1r=0AIAM3L$%Zxkn)V*c6#ADluHVC((6K2AvjEd5e z6j5pn44NaOM*pDh3+<;x++Sq}u&EoSTK^M1m^m1eqMp z`v8o-_HZH>hnDfncVlvwD=%USqInlM%wU@U5^>vwcGvztWZov2re&#T0$QYcb46Ad zN##ZCXz8~u$_`-T3$Wou$PG7XTInE)wDT)Ca1)P;YM$#p38y0;b_Yq}cdZD9lw?Gy z!6OG4ML8a`FG9q1OW6%1$fH3a_s#o^qVeoe%#3(i#9Ch)UNyUEA~%GsZ$WN+eojG? zkK*w!qd3VIQboAD2-@ztJEM&d10%!0(sOXhI~JUQmw4*5@z5tQ^&~8-UW%1VJm!kC zf^n*|J!&K3A==SVk-tuAlTi^d3}blI93CZE-4j{}j1l1nghJnS~uwzTnr`^~nvdBnyJM=Hc`e(Zvs|D=%zEfZ>p!2h`bmzD{ zFcSK+4lKhZe+zTrC~zrecH{VcIB`FU6P{R+tDY8+s-{IoDcmD8$is-eNBS0F&6PoZ zTmI|K_!e>Z2w2|__;f~wK`Hf0ikzFJLGx=rZvF#|U(g;-#I~7(xbbbMme?w3Oy{&& zPUPvOLDOXrB=XO*jSCy*Y^?@{1V}6R9HP(rqI@(Jc@Z;g=7zB8McDLWWYc;K*?DM* zJ)`^)-2Gly6;!LRy~${PpE$}DN27inF;T`hErLfL)9e4dDBM7_jNdMvnRQR>%OANUeEON1{m>A z1JKAwf5x>KT&1rV037}$c73}pBCTmh9YLw1WM&mcwOhlsd6vqQy7yTdN?opfsEB%> zaVfb|2mL7FMV3kw}k-8Ih>nEsk z$(ow9W~O1g1QSe3nhT6rVG-dH%uSE)^~LYU&_;!@qUGKG_;x(;eM~;l7+4sEFZ`1) z_o#*_Jb3JMY$SCmP-jN9AJ;q`L#rEC^+6olE?yCNUyz0ylmHmj21jo73%Ffw#U(iF zjSFx5qL|r>WB20teJGp|Fr$F~w8$ua2uG#s_5j1L+z{4Yh1`(pk)HS=sm!`_gd@0X zEL@3m2Qk{BntF(r;~9j1FkgG;;tR3m_3cDOPJ8I9c` z3hD!J6JLCO6wZbs!y_*+ihDM^w3rVI{124mr|5-5-f70&j}rl~@nxNV3m+DVv(hp! z3@kbqYhKk&0=eLpKZ+e+bp5RzPQ<2D`X_#8&8p=^^AZ?2|2_9V&@-&B7)mE`|3^?M zxWLFA=q={4fW7L^Fc0Bn5oZ2Ny^I>Ef#6S7^9j{}l$~%S}g)<|Sx`Dl;*z|mW05*1fzvjRq z?cM@2TH@MYI$@-L@xvwBY<)FWUAb_Fve~^jdM}RMgW03f$BiPPI<&Jvl(8a?H@x%j z;KGuMFZ_=M<2k=f9pK( z15kV5=jTL0rq{6`kq`jM^VC$Fu;Iy`i#DN0F)(8Ax*#7-s4(gq!`4?~^NW$~C_QHL zXZh(Jxc{T|%4pT9mlg@iP_jB=wzP$KmRICQErdrBU=;CeI6YB>gtf5ml#1^o>Q+(l zr`~u{YJFL0oWiK~--!bthY#Y_8$L`wUs#K( z(rQ|yh=;fzNd~K5Ake+qWgPf1XjK&zb!1kW036ND+EbHuAzz!;4a0yJ8@Z;}jt4fI zl9tKfssE%ToK!6cl6ZtU@c?%J5T|xG1{PmBkK2Qv$!2bn&Y{mAqn(*v_tplY!#-2Jb3@&>M3+TLe= z-fl_LgG3?xh+hZ?Du%*bifDxOGusS7=(bBW@Ed%ln{HN)_P9gINkg*Yha9kByN)n91d%@qUF7^UmUmkK)54Qv8n#R0D{>^JsJ_UqogY z@(_Xu&r>p~c+>nm)YA|UPzMjOuQ_Iv$-p$xGlDbT-SrBi>Kt}`11BF8q)-MXqfxCQ zo{zu?!ny{Afowl6dpE}BtJk|)Up;<1p7@$qTf~g@)aLFWKPuv?q&1Em5Oz@S*|jA5 zESoh)#xldBFbsWD zmjI*Mb)cnUt)5!LGr{@40k&2e@lN7~i8WVY+n=WzE|U1;&xr@H_lKC;<8mVRfen$& zzi!k>AzWaGg@7H8jr_r0h!eK#^s)ok_#Ev2HS#B9UE2Swi?cCuRzdYGp(3OHCAjGS zEhICez&3Xfhws6WJ2AD-A5~IaY&UL{BD@Pia?@&U(+ki$s(SEKyK(Th3{|ng8^bfc z*Yz93t1vQ*20P#$%riI6s%x2i!b+@{=ialjUh1O;cAMfokIsy#>ePQxIX3^7e&W?&k`G+~&;G>KutG)ZGv zBZeuR4*Z134~#McSo=b(xEdrEezt5KFzWre(+33k>~{PPJocZBMFi!6kJ={hHJ;>^(P&sQnx5dhiy6tPqP#hJ!40oLxi>QNPOj|CcHW=B=z>Wrjz)U829zU;HUesg5Z930a3MVo<9 zu%2k_Z1k@{b|{gFo?S*|2KmF7d?*MNb!Ju?ccBPDwMwTa?V0HsVIgdxG|m9KE-&iA zRi8rtvSv_@Xyebx`?2@OnBMCP7A47O-c_^{O`;()0)04Zv zvdO_r5?LuYO}Tm^_oIpm=Wh>^t^}Ogvs6+t3uHfFkMgEw_&cbr)EK|Q+gYpU^ zZpM!)1q#L{$>5RXWJvMf&ksgQkwOh3?x`VeN5KIH4dL;GXGUxxURj~EY}N%vr@aY- z8@qJ^va2}wQyjb*)aL1?UtT1C76dIXnis%`i;07r>P9zU+Z!;vegUDPTHE%me*R^r zU`JlWB%^UI27>x%g2An6*a*!P90iMvLVpaO27>Ab!9O{&f+N9GMh+Mm2DbbTR$irt zHvzCa_*-oM5=zqnqV?63Q`-7{4eY(vp4Fo zF#@=RsmAkurlep^PXzEzMqpgAFZnwRu8ww{tP<}12=a${ROIJGtj?(RLD-EoK13bK zCi@`Veg4fBqy#Kvc1%gbQ)%zsW)Ym*OP=NDYxB35DK)JpT-L5&;4b{`7VGvRFs zDx$ss*7-iw>x1FNEeQS1;U2pF+t{*!_ecX6!M$GqUtrnz zansAN;f2vI*dw3Au{*__$U#DaYJCnfqOcpnC-T|@MTJE12vHy&%H@n) zj^uhsE=RJtX0f3pz-VM6&VOIW-D{jQiK4llO0)ZL-}^mjXdct+P=%3q(H%O0l1Rjd zM1e;^2jX(pNa54x1|uaE93>d}g6UUeASH!Iemw=@O7QX?My3ZCEx!V5U)q(E(TO{; z@B65h+=}3=06sA-(!glm0wXpiCIkaZaMo+F_}qDpiU3sec<5s&O*KaOhQf#)^qnET z;eO55VP#Pf6KAB3ilP`5nWi~1W{!;2-3oQqQ zwHKu#rY32Au0E{;$YekW&UiJ>etopCArbWO?eO@Wc>I6daf6K%S!M+AbFrtME^$Li z9(<697^|c6)P(dW_C_HXlHnwoDU3I049rS1P4q3s`R|0;uLn6nyY)jj`k)3zvEvgl zF!H8S3Z|7MXJgrU?w_4{634cql6QxCBZJY6SbPR@jTH5Rw_{?5XM!L;Y(WUMsW*9( zf(s)7uc`>1*m7h8F8#X*pa0OW@z@td$q=^4&xqEAQ7kj6CH%T8b58{{b|dRPNjY#9 z4t*3Lk#YhWF-_9bWAyaa2$7NNNpIjy0~kS+lGZbdOaBQ*wmWV{HxnRjMbaam!m+!Q zJ&eH4@*<13Bm^e5_=u<w{tL4?4__cW5TF)|F8 z8JziN7~0fj^O2RuzVBjUyGK}Dq*}H*Pnal42It+22wN}(OtloLdl6AN#m*HR$rMBC*hoAv$Wf7SZ-U1v0akjU{KHZxN0^jKlXd-ewEvsu%J$$Q9!d&yLcB6B#ih_LZOT$v-7+W*_eUAgX0S zikVPn#J$I6EA-iZ9^~nSej6hh4~hhj)F2TtGo-i2=;<|jdPz?&$z(b)bP4q^TK!CH zdQCSEgVafFqo6S(qPhLJ>piGe_+@ylM4E!a2tiV)l=xAq@Q4&Xb+-Ep62}Hfl^m&( zCTXlAoc?)*QSDz27`eXJ84ob(9mj?5Pc5u$O{<*3j{iXElm}<}-HXJjztFU3;V`4{ ztTbn)uLZ=pL7etVth}O~k2q3_y(Fva0`7Ysszp?*^|EN(Gl6Qgu4-l$cr{c6zDk-b z9}VK|iZUW7`4*8}PiAb9IXKh--;{2Nfe|5fW)!sylU2asU!XD@q%E2sz=#Fh)W*~) zIz5GLZ@`(qpOF0|ZI68(M{aAxg$=V+Xa2QpXBi_ZH_;Fl5w^B26YT`t-9+WVTL)qz zh|A+D;2|RQE>J~CvRY|S$x3704A#E{E3ek$oTnYzj^BKwwt!!F3Zv9M5s~+KDKlF2 z6!b5~k^3-v$Q|3=Y%*Vb(mRIo9IPTc5i~fFbdgoJjc1t=%ZWIO>A7)DQz(c4FQdX3&*lId(=jxYyJ zSS4-jOl~5$-voJCG^dYh z-=c*BjF_+(B9T!8u#RoP>957W%6Wx~oc83Gar6$xz{v8JHegVZWr0%Hzz`g&4a(;oCUZ?j#m~wu>K^^}d77Ph)9DnkZU^-xI zeW;MD`s7X!bE2AmHak!+Js+36GyLP<|8F>Sn@fLKBt*TLyzq&S9MMoC?~Eze&h|!u zW0A-1TAc?eKq8XK8hyP+Zy)LHYjc)a?BQK#W5hpoHZZ;oYhQrDP4jeUb28c_>1=Zc zaN9dwYYSGuVR697rk+tTSYgy8LX;F9k;1>86|(E5$&RAHCPB~>4vbiZQ6?iuNjv8+ zF}yL_>9rpi?Ze}r=H*2kEpnc7iprwNCUTFAF?zxGo zeRmovstXBWKq0LL%q4Y~C6Bx!lKlo)1g?@F+w~qymX@S);e# z=<7H7`Z`@klnO9nN6XREvHFD=-nsyWJRd@O@KZSWt2!a_Tf{I=lBiokr{;wkOJS56 zA&LW!Na0`4jJ@9!-HxIJG(OKlLc%p@b706EUu5+3>h3+Cfe`>RhqW(LJ<7iC!72;b(EJ8Q zwg38;;QSB4$gl*B+BDW+l3=E@OZdf~NVP>ZNJKe#k>`n`Zz66;s^i6W-Ik}ORvIN_ zd8`N^s87Vf2aUnj*A6hM1(1Yb(K$Hn4GTI_I=&tEe-4F-KpFeI0Y**aL@}5V82ZWF ziE8!Q!x9UqZF?h0%HA@r;E0tJaRW7NY`^#&Sb2%`I!x`xt?xyp)Ho{!208EuRLqG) zNC=F`sNV5>WziUTqz;M1+etQS^!FQm14du}JgGXOZe!$ML5yt1%I9I^tc5iymX71m z51}xJ{2VP7P$<;b6qYY5%{v|j7D{U|AyS1$r0|I|rJjx?nt^uQPpA;aNevO}^Sxmh zEIbMUMztw_^)=Y^$|N^dn}E>~Z2w1BQp6>uNq|vATGXkqEMh1bJGh(|zzCi>URExR z8~52jxt6xM{8DUs88XetfojutvHz#o^?lejtg196t#Kc*sv_IA-K=<<#Xu>cB5org z=Fo6vY!S)jpxgWaMhG-B>RpVL&rm(ep_@=VAtgk9PSjn%C^$&n`bSvyH1YWEEHk3b z46$an+5NcnZ|Y(qfOotmgwqzWQ*hnD$TuqmR!iwrY?SQJ6|qr*wKwm#8C+9^@_oJ=W3U!+B1K@taU$a9L_%`pn+2r7Kl2R| zQEl$V&B7$91*fO2y9yV+MSK;jdHnLPF?&RSiW=amvBjH=R*S$GerE}r5C67rMLCUmrmi1ch^UDHYJTsLS|Fa7*$cNSQ-_Q^%v&1?lf}4`YCY@2n_gYfcBN;YAm*uGu zrE1T|TfYi2fB=@m!3azaVP(RwnSo zwe=^^TpcP(QK_JiN3n=Po)(LCx#VyoA5tgbVPtX=6bBwTrq`d?DxRB_EsfG~J3?Ub zS|QC6CZhqQ=EcQ{idoZZ!$99SF8gSktjLlbv^KHxz`T$MB@8Q7PM*39{vv}7D> z`s2qgg+#Jwm<@}eo|wj2In?f&vD2~nchS4F3y@G~d+=Xz`~hi78g;WfYlx_^2(_ut zLs8Y7CpIh7y%Snnvw}yZd3GN*^C#rApy1#jnw)j7%-dm<|bjrq)5DeV}xcVbl zbOwKJPhOAv{>wW_F)@{GduB04g5(&%_yLeVJq^K`XrW@FOvdORF!~3~{(<^Xu%H5% z&{}ESI)Yt6)L!&1!SZKg>>`l)9?%~CHYRT8En+N-7jV?Jmr5uW>|((#7VT1r*76tv zCO#4{LAM4Td4N%*&>?0bT$G$Om570GlRatN&%}d@Oo774G_mek*!;U4a(70YvkSQM z-2(R_R%gUM@TqCzMZl=ZlQ&6q1Zysk>P~D#L$8%TEcO7S+MUR*pePkf^XiG5Jy`Q> zta?V^=uf-gvYosa+dmE4a;qEE^3fc(`1;+{AVidHwl~-clMxAj<$r@=WJX5Kv2kK* zR5Y)FQ2;qw{&e)OP(4ciFpm6Ml@oO%FoGy64Zya)#NrFYH+Jzj@&`~jh{8b>4x=)G zY6X)Mbatk`?5_v74LDx=cgY1f_b*{&BTr-xeIC1i>Me-IKqA|%4^91$hX0=C5SF zGJ%~RL$xf{aQMuOJTSCav`YoMShR};yHslA)GN~!CGk-Lc*L$K3m-pCm1!ZC><9_1 zvcAsAeVO}~qwjh)Vk0?vq5aqBx!oNA<``JLnI_?4|| zz`VBMQ$#B|So={CF-&uG)Eph3C#H$I1x)9`BBOOLM#ftOuXq9z4*gUJnQunlGE@u5AHZDgzx^m2hFuao{3HZc6{jZY^i-V?IoQZK z#G-Au=&xb+M4P{E`yh_oE09_MuHeX{JZD!epp@2PA+g(KJZyw{B{)hkkdV!iA!T+w zj(&z17?~zkKNricnzsX5wTPYH!MtdR^f> zif_M+NFprqct4r_1IFNxF)%=k1s{opPRFu^gNzv&26Dq#ay7;;gV8fTFWRB+;P`KN z4Tr=f#_jo>hXAE^xkQUayHudXQZ0Wi;*3v1fDs@~!w(ZKk-|T3iL#flQ$*8bNBo`$ z;8hsa2QRPS+%yHXQTrF+ihpWTK3d|yh*JFKtwHtCSg&ihKmhd?@nvT2c3uaP1Ovs8 z9jR2_?M6ZraB-xGX|)MHzyRAn;=4h!iRDklniqr=OzpS}#{6#Q|6grPEniS3v>;BFaMjqz>B4!K)) zeH*koALK;=7boDsrod(uVqjGJ&$jB=$hJM$s78^-PQ%6TLAF1_ob~hni`hf2dlCFK z(U>XMwJvH*G!5l15kz=u*a#`w8==DmN2!yekgT-e8PH)ywUWDL23!9WeXHi-P&WBE z9{CdHj`=D82FZ)iq4{V{IDxc2l(4Ej5q|_e^i??rNnv=eD#wSA+;ByvGpfI)OeHlp zwnW3A?b-i=#art;`pt*1|F`1V%A+uHLofE+yr_su6VT*aJoL>pje$XPaM0)*=!PX> zI|3LLk&$pIArNgC$PQxh(=ql`n0@o@qE$}eu@9qK3YBpL0VCgSYnO_&CO#?_X{DSp zJc_uT6#jX0(kLVictpa@u)Xg$xXIoSDT*bN{rYF)^xyBgJJ@}B%b&Y`>}-8BE>BYK zUKBEOw|U$op*$#ghFZWCXm?nX&e4UXSRD(A&8{2axTVm#Dc@}dB&#$7HB5BbYEhsLtDg{f0?Y7&<1Ixp0)(dZ^z z_I~7slqM-^<9n}!RblWDxSG)3XX0*Fn%B38@F7rVVI!n3IO+^?q?DBw;Ct;68D((x zJCPloSA&>U!js>_zF%?=Q9MZ0Cokdx&dvZvUB-z*^+YV1iN}c`^nPYmss2AAKGzUd zQp8G}Sp88wNp0~~T={-vdvVWa@Wf97OEgfgeKo_d+~?mmcm6^uv-j4qLQxI+md#Acz?m1~PpZzZ~OFgE=s-F502*;OMV;U<3$gyCN{+?q`V-A!l7THG$Eix-42WpCNkoJSHHq2Ae^mjrxa7C=+p$N7PH;J@LF8{LG&uD{O6A0 z`v2qBM{`3!H_5`E22L64BCl^z05g#u@8v)7vZW;cDMNOmUo~$>sLc?B4OKI){0A{bw-4zihkr9RnJHG z!CT0TS{pD?KGXg4Ml2qZ8eJ39%#1C{jE*jdI-|P9U`FZ#tdIwFH7yFhG6CLCLRuulM(`DU znLb5NO`uYBe>J!oS9}otVdWr?K7gCwE#yQ@E(HatgCTa&1DSP^Kg41O@HW^Rp+$nD zro zBt^lTh`~wDCc!+y46B6Q=mwK=%)}A}cF7`|Cs8Fzrh58K@Nlc`m86x>>*Q-GyMcGk8 zLZk|hYBkZ=ncg6VXX(o;j2a8wjnBgwuT2nRbU^#fyHJ>v8f#GsPyO%a7e^yaFNo&f zsQ5O=Kq4wOiglIRy%?i3_?oCPBg60{eeeT4;p1jPu;CS0a#_0rzleWRE#js(pl|kXb6W8O8PXNR(FQTr|HDU2l4I4Su zMbbb`5gP>ue~6o>hPDchT1$>103$Yrm4|`^MLI^QKlXTn@8e;gS1Ecg}J{1fU{G)uzrcnt|Hxx}%8ee40h$Tnx>x>*_sl$ncBBNU9 zyzV7f_Vo6}esRC42mT%VZmy%ETDdgOGgXT#j>i1RJ{HrcNVkZ<2npmxinOR3fl&-* zBt$~8jlpl5ou-o$s7br~7vZXFF}zk97~KDPJoF8g6ESIMjk$vmS6<{*8;OjH!mtrU znQQVGDOS)GI*E5RIr0Uj0l+9gUCRNZv8_10`}5K4GWPx$2X5xQ4p?RcJ{gV5lhM#z zc~R5Gv5w?K(uhu;dKN;FL|+XRvm}0_pA+$;wf_#L@BW`FM=ke$`(Df*_Q;Fs zeNixxbM?_^hYUT+z$gF@C9jeegCcbWAyULfp5V9<3J|_rn#jz^G(4fRVPNfxu=1L2 zLPfPU@fd#dUf1#3c_3h0j>3qwF7nwJ*>Y5r$&4?`43BD5q=T(!%qV!Y3}8U>fEESd zGZ{02++iVr4F&8lKT9W0qF6-FFs}JWjBOU5=Pmz$y}uM^qBRb{KC^Ltbg+>)L<BOwl_r)Hg=>*0`20BPv&(ee!v>t;Nn~Mm*8;TU9@jL@p<#1qSo>JFlo`oDk~%YT zN|`%}3*%Q|W^Z{ zS%Q=N@tbhs@kS6vo#hT^Q6y9wv4M;@j!KA)5WW0JoIJJGVxuO6D~$lsh!2c1Ib8UD zWV^Q3k6pq3pW@&zcwv#>u~lwegk~k9HF>=1T23U~kI&?YpAq=yqk*Ysp&LE-qFYv_ zo_fZ_lakZ+@I)m}B=p0EVVDC$=J1f&Kd2*)Rx_jEY1fa90*v9tsrA&2X-FoM8DEka9?_^sx0uXGWe^UP z4s|sxO2Ukw;L|DFgYcRc@^pF@m%R_m&*NwDuRR~78Am?Eu_559 zPZ!6L2I($Qc8R5z4r!L9V@ajEK|&A^DVJDSVhITeVd;<%P(tYjNu{Ji5s;RUc<%ps zvHSLZ?%bI(Gw1xy_qPR$bcXABc}xbx8!ItRvLzV`iP$ZgDX zYK@8MpB2jO7oqFos6K>uvayH|#XkS^?dd47lFyb?plg7!nt&Y395UnCuRkLuV;f)2 z--(vtqvsg+Nxwmw?gv+;d<0-?PY%%_y$p6BUia`{TVT_`iHH^E?#@iiocJ|B-x5-yeYk>ORN5)^ekK=-){s zoD)YmFm`TpLNu-`wFNqtwJ>JZ_U0QTx0hDYu9RWNy%L;-;EyHTexh&%Cm1)om)t(+ z1cyBmH4osDyzvf+w9y96|MX=(H>|d1h#J|x$=qL0-$5&*N16$?*O-PJKWrrECdNNc z7(m-6H^v9MEU6`^)aQ(jLL%6r-FNW5L^r67Ul<8=42rpi?nk3fJf3xnM2Iq=-b0QN{RPxQ0t|#pM(ml;cOJ|+qcFw-f+V5~Jz3`u>XKTF|8-{X^E>jDOM$nlf zU58u+nKKO^-YoQ>sD#hZ6%pKz@2MGc3g>Lgv%N(#*qU?lsOL+~*bDy5gX`DI$k|gQ zQccfvsr}2Q-ZaFn4v%#AHZFY1VI8=7@%(0M;SRiaXD1C+35lMXidFXytRGxH=e%3n zD)QkVk<;Rr(j7a^p|a6S&9QD<=E(_L zr_OV>@U|PU^d-chg5dq*czXDnQP5-RR2o52A+g47QFH+z&cMU|wU<$)%bkPVuWV=f z|Jj7O>IWM)d*}GS_Ei%BN!5zh~}lv2`OLqPzUf-q1Uc z7VnTVFzjO-|Bbt}Eqv*{pN-Liud0DjHZ`5!749H)x)ClpZCM9O=u7!6#C}}Zw`T?C z9XXN5_cwzc&voZU^e+vxn4VTRm7rdiB+;`4fMnvWxyDX$usRf(VZ_PFSC0Mdvs_}YG`gYYI?1^`yS}G>hHW0V5Hh|Z&NPu&xM&Y}VYrpn zWpi#+!$4yI#+@+s@K z=VanP@(+8@Sacvz>{pu*f;qSNy4&SX(6N>B9^di-UMiRJedO7~-+2$qg<^xlhCnNy(oGlV#W*|_Qb zWlGljH7`ZJD-^tOKI~B}p>1Qb@Q^b2dq3%VZV)d9url{`%xGRM2LO@B%fOLQ$gN27 zNKl5-<<|Fn$>`;m=#3ZO+z@r`#2ikOn~j5i>ITc`Cymd2LHHH1Q7|~J-=_cSUy``E z$0}3rUKuWCuu$7mP&mb?lX)R#=;t62%mg^#??lqENq!)5+)9i8xPXiHfD<;$SOw8~;>d%!lkQK|Ckx!b{qsXzfPG+^0pyn8gz$jUB zhE7T)2@3n%eKGG$dRSymGnz}|Jo8i!?$)oV%&)o5|M*uHGYSS@{>1E~22dM-gxsEn zF|6k!;=W?2{I;HWW$|SsbuFK$IwPxUk5vcDYz=J|qWhbl4`nXVjhMTh4QE1mdFe(k zev^UFWZJ-fHt+Ce)+AvNqiX_jq>^XW@05*D#DB0Ng+K*)%mXaTJ*{?`YQGv^t*xu~xKLzQEy#A9$7O11h#hAP;B6 zj>6In$3xd2xg;DY<=$(4wE2db>#eS)*wvMoD!6~OZXibGXmX6O zyTkLPL%=|E_e$H5-glr4;Oxzk|B|%De|HZ(_x6inJwTlJ0TkTCHk3vEkm-KkJNeM< z$*(WeY8hSfZ$&MsJd`cQHV&=hz^IC`h?XNY31~ZrxUjjLBKgzP@s8r5ziY?I-J@!GpjHArIKcoY!pAEnU&h7Yhq)?;?l?$mOlc(Y@@NHyvq$K48-dA8@f?=|Ns zQOME3yNhgAD{?#!>qN}SHqf|mM=N)^e~FKNPs;RluRB*l%42M7N=xQV0HS~O@4n6M=n_|wz1vO0$ckP2v>JB-)mO@LC}XZ=xy+`oL) z?Vz2^z=N{-JE=eI6i8MkO?r4mLW~A_4Q^sh7m+Bb`0ys-^5$Xt1KP)zkd@J{(eBE3 z=7x04lN3-6ZKQ2Cr-t%yXh%+?Lg~wzTE+sD|H`td99>}gs!^6q#O^JXDSeYYTVh$Y z9&XO$v3ck#9(}O43TWn#reTu;(qemhvn}>Bq{5u2he~|%)Rlr-vG#n zOrVvl%34JK{pf9h-N39|d3i$Qq}2QPzl<}@$H9*kJ+^+9&+J25_cU6q*wSYVr&a~i z`@LS!x|>?Zxr7UB`s@AyaJP0VH4t6Gytgyr=J*n12VO=S>NtL?(*G#y_lmM;wulsx zdBV5(Z6jkIMFmrB=FK|j{vK1(xb}{gm<+P*j02~R)tf^8vty`8$iKGd zKC@nSc36T%dT-| z!#&*(!wvkOwuB9`w@xMF(Ii0 zZ~b=X@np z6EuW%Ay@|y!-pCY)NfzP8d}U%x8S;b{$lYg<@2n=xPH*Ihub$RcPUcHKeb(I73_CP zqa9UHFAZ%;59*yGqF5t(b3)xo8wyaE5b$R<&OkdUiyI=a+1twz{sZsUmIpk`_zkuq z>iNp-!&jf*LTqhc!GAfp8#V?7;p2vzZwg&VLNcR@e-@qn3g74)Yi}QXs_UbTx+F!< z-Rl$Dthvt3D`?X0d(r@g88eC44jS<%`UJb<#M!O)-J#Shepu1#+W|MpS7Lf{+(P(+ z`AQS#Jn_nT0TS&`-S+x(F3O*Ye7$iHP**KwwWEiHLGczRAHVfOmZvLwU!MF)BgrJ#a%ji4 z>ubr@(V5-&8@(SpYC9VIsZ>?x6=53$?jP@+d&;zwevut$GXY;Mwe+|DG6q-Ji*m4zc=7=SLp>52L#u5J)GlJ=sR~SpwOJ zn;n@&B=3UicOsInVSYquT_8)H&$?eD_GmCF0LZ zmbz7_mdi1n%5$@wAGfOZy3y&C-i3{_kFBz$?J~9XzJ%g?BICZOX`amVnnm|vho8DS;|85&dz21{-``53-#1y#hvVg6D-@u&>9kI!KyBVzU&X-CN$1BFmGlm8 z0Skm16vXvnr{rx&OdKxg?hW*><0(0yG7!U4H8(8IMi4{o^Uz;!UpmH2m8!j1Jv`qn zrM2{YsO1AubH@2RGWn5Aeq`b!?knxd4;pTlm%ShBcK=Pi4y1)%U1f|dwpsC+1AA-O zSH5i$dfm0(^wXgjQVZI#rQS}kGuSt?6E{0|O-AUY>z}tL$Xig*rrkptF7`)MYyg8k z`dQZnL&?b1;ETgmvriH=HaeU4;K&IL*$M_wu}?JSgudX`{#01&IfGd2(I)v19A$b> z^rl&@m36Xs3D!UvXAB^)gm{1>wi~9 zOZaGaB=e+!TpsIdfU%ot`?VjQFn<%}N65kypupn}R@6$JD7J__EvM^%tps`mmdg45 z5TYkxWP&slK0ls$Cm>B@$w1Tb+`N6JW4jxB(dl|4A-zkP!$1YV^p}u%E7YGDpEBTl zRDo=un5&|++K3U_j}WmwMO4|(Pd-E7X>dFfy2ed;}01_X{KTC>F0<^g8_$0Hj*KPqag-pDG0r~plYJKhZNJNc6&R}O;{PEufEvrZ&DCutQdhopiy_q4Uu2C zepw`a=~E)?qm>S%tmG15|J_WWRuw`0xY>jsnDk>$jbhamWN18WH#yX+1xjx3Yc8>pSC#wq@D=C-xU%EJZwu$Vpn?j7d)aP){-Jr*Y?u z2{0L30nd-|0`@k!3E@3?Q*g4dlf|ZoT3DgfttrlM>&)D|u*fzdGuBzE2 zt@dy|E&(K`yAFc|s|d_eoyf5P>tjf@-Ae9*&W4k^b)dPF3hY6QX>)pbE7%-vF8A(* z$<4^+hv&)W|Kw;eh-*SkTAdZyF=5k>K`U-QYDa}K`OyJ&f>?KVd}euRG9X_4NQSX%0#ydk6v*Gx(7XJVREYTFGYe($z!HQD=< zVX{}2kVi!{q397G@|X8_gH$>o8$;#{Xvx9^d8c&0qCeRiSau7CMn5;TjBe^kBNV(6 zqzE0r^7&;^Q*kg0uF6x%Bi>Ac%H48g>6l*a?>z7YHNN>E>9Y0nhg>-FEn6^iV%hT? zMJaOf2cqpIKU8raY@4MUF%9Kri!sXJ3Y_{=XtKUwq7>B)`{j@|FypT?p?f3cH2Ee_U#`-4&Bts?{G5cnZU4}IC=aSkKleE}p{wD7I804pBj?{dD_YWLx zmpbW-Mi+%M!^b->o-)Zlcv?`gEI+$`abs>hgDTgkCk|0st2F-7ggh*tFDtXSe}M>D zp{_9nz5xGpS)A@`i2lj(Op1=rjDQ~YxF&AIS6~?Uw<~H zslVD=Gk5o(E2>*R5T{~>*e;nv^<)Nj!q7|*B1_0rZxdB%ZY~ zB@f2^9Hw2td(usRTNOORj2{!p7|QT@!;S}{xcI80^Y3`~`ylneQc;$KL^iVCe8slO z2bo@u4UV4FG3rWWVcblX3H2YKfh-~Y2KjxrZ+$kCQgBIGn3`;#3SEl?o0`+e(xP0V z^U?kA)?j<~DnIukS&237o%5M{bC>9x!2l9 zx%xmFBT@34O8rmo%^g>H@*7hH^W6F>U1a%vX#|WAX}naew#cjO>;n;#bN)-IB(-E* z#GQTCRM)JO$VOh>-`t_AmGJk~VhxI08%|4j{Tt(W=%@9E5 zON4QX$$_jFA4$>q=#rD@vlRo-V>!~~0J*Kjc6;WID(bi(0u^3UtEhx!mEDPuow3mW8%_Fm9#>Tkv9UB+7y1|g`o3uhTnaP4S)x)K&Z0hs$7+v)804p-Ix zW*+N?tN-bKb;nVY46iVJ)#&W;-Y5m!gilj4h)U7={S?;^rg}j=g5H~6d0bw%ig7e8~aGlrqM^2j6{(6(- z!k7@ygH_3AED6G6S=JuBpP}J;3N~UQQ0HV@ICOg#rIQ|XXLUfr#OxVD_$EKP8~@eC zONfpkA0gX}x>9kwDrCw#jZ5-CjVnYM$sCGYJq3uZ9z+LSllzDVwUhmz24BPTjE1T5 z-gLxa@3+s-+@HGPS6hB%J}NlTL{}$--Uia2hmrmZ6MRE=`C;l)yqhS&Stky&1^yqB zClO9oK2zDHYI#Ztz_eA5R_8NP?N-@Cyry4bP{9HRbfau(x6Jr2WtPJx-|Sf&ROiPb zYJSBHQZ)W@m_1GRxY1Rfvz$K34Qr``sO4^H9wJ#Mx1oVq;E z20m_l4aaO_pPGgc!o|OqXE}a!lb=ffak~@o#5bw^Hh*v&OE*YH?gGT{FJba>y9~5U z98i8;h(yS6M<@E*R? zClew>-6_ePO~-;GgiX!M6=rr6c+j#A$0U= z!*_;-lD{1sTIrj_#in$ps;@1bj*BIv7~>Lgc@0BXc9k?Zd*OgC(zzR)yuw9dgaf8|7~U9>=;~wcrrHaU0JxCw*9MjUqcB(VlU<2INN1RbZGe0 z#dajDSJ(zq=R5%k23I7xa-@@T9*1k{FfD!BORvb-_5n3-c71r?w=M<>ZJY;Rj1fUU zuRMSU+sIl7@cJ1}b^dw{aL}-ho^09GeB8t-)OHGk>TP696%$1o?pTdvl)d0%EO})Y z$}NX^hnvhttI6Jd8b<74ZwnTG>N6wjhaA+jFuZBt5X$L)@A&oBD)*++am9%qpxR#F zV++SAF;eE)3H5a*+|5sJ8rO?8nq4>|^8Ke6Y&f+^2p15IXS?6W*5Q?eDPCE$Rpkn!5Hv@}A6yAB{M) zT*`cJbWS(FF`z=gSi$wXc*uZxaT75K(8o$ON&q-mS?AvHZMJ8ti^bOebAH0*_)nyP z0qi02i4BfT>M4Bb`^!2Bza%~R&0apGtX3nH7SQjrrmaG(bn|6aK=F9y)_-+UOd-nY zXJyzNYQT4d$Uu%jX%xj~%;_)e*T16k2AyR8oGgafONY`V`1u`PDXzB?Omhqz zVZ_btg!~26Yf9?j(L5d2;jIG2YQ(4S;^;t?9d_)@rZiU>Wn<$M?oWYR4v^#uT^7n@ z`s_=E1DK6D=a3Ow#wju!4REnraz&^UYiu*h6)<#CC=ls7zNq<{Ox05m*Ly*G4{FH3 zoOkk)-S+u4HQT3YUDHYl@ar`brl$#*Z_=0ee^(+q#T&V|v0Rm>g*tH{6;NQ>%E!2t zId2mC8gZl z;C3NC3{lhFFYhyBKT`9&XU`N>-MA30%72)87ys9bUT+;B7ll0JW`FG!oox0ab5J4U z9#Ad#a7*l9F?|pI@%poeEnQBs^J1KXEuDOw8~aNwQ!>s_N>M#J>jNDOEr;AB*>Hu+ z?S4!)Gy7LGnx%zZe)RWukS%`8VEJGBegC?hbK_*c&CH-k+4^PMwNsTMarQCW(slYR7T9-;`Ib zt0I??=$5ZKKSsOt)}uH-TCzk`-BRq*9a}uS5&HA5+M+$eNma$zc$OKVwQI*uFxtf- zDw(7a;+8SIweYkw%?)#>R(v2I7-;9CmmA_|8F4F<_5b1i_}Ik7g)faZus#KcmBVkF zW&M&_jaywqH;|5L0RO_ehkojN9{Rf+f!VEM$uk#sVTzU4;B$L-{p%xx>(!;sQbxO* zleE$@-M!x0Wu8IxG*%`xe0asIvIh%?O1ynh(i+S_LaZn~2k&+E8U}r3B(?Nhn*x1( z9mo2|Jpr)a1l$CSWP8`%9wA{Cbm({*_|w8=_-~0>ap>=AC(Q@!Ce@+Ke4G2B%kR@Q zT|aF!Q5i<6TZ`~Kn;U&<^U@*nusSS!IosHxm`SGM1Zzx9!$=n&iGf*fPBMRsK$JxA zJnP+?d}oyUp|H3SqB&PA8QRMOBd&V1yF{{R@?ZrNqy|q6$_Zj;w7k5lTg&OxciDKa zzj3rH=g0B%cQ!WVg)}u__L;awEh^j3O7A<%@#GhEhJdU242vyYO6vCW51tRN-V}zC zZteJZ7TX81U*!=Ra}tl~(sBdH^^MnoPiA#1=KEBf{EC1B32XT)qz1x{&dLXW%ed*? z{_zDz&K~hmy1I%jz5+oThp4$f@B6UY(DHg8U?F}Xiak)i4cfrBzvLgP!#cvFIY$A@Ymihii>|3A7f8FQdzZiqoccbW$59#lHrFE@$L+l;#8B5LFG`ia1 zU?Z5iBh{nZtpeAm5B`Czn2?^7kROB{%KGBL)Il!06YrUnpUgNSIc4#W`r=^TulKvW zTt2hS^wUZeiI(|^-Jdt`k3fuGpek9!hw;EHfAf{p;kBkKX~l%imN_mWlyYbLb5|-m zB&G=$XGHaPl94gQ=pBhw*!%P$1@L^ejAT-w9TgU}(kG`S`A5_oAM-r!zVMs;WdA$1 zY=wyR&7QfJ^4#oNNZi-;h=?pmb2~A9i(PuDof%dHyma(D?X1?l#LvA!HXz>I)E{46 zL&8Kbf{i0;43E%9=w5!!`Yb~%lKxD8}81M z*4m{U1ia50D{ej=fZ ze<#u;a5o}^-VRB{e1j-d6Ny0do)5fdWPL?O8te^Mdyq}d9IQkW5Qo`4_PJd2>DTTn zGtcTI4}VSjIF#ah)5;>nzgjE##b%tDYT8l2BE=E^=5=L(_T(&g7B4Wv-!Oq5HABfP zZ`@AN!GWSj(EMu#fR!Ft#Y9w2m5nTq^U@aT{#^wPV?dJ)mmPM7ug={lk80q7mq5Cl z%~pB&mAN@}BTgJNAVV%h)~be+_JYJr_orm(NGrijVmjvF2R!og?hdRzUAxeB(k&Ss zIh5-8>aW*z{D#8}k0QykK18`;KJ*RR0=_p#(Y~T6VBY_HR*MIQ>br(sy5ib8F6!=f z)u%0$`*AYR(?}E&o73!iWUFD`20nUKHaVU4fPIWC8BlX!OT70{=eSYw^Wn3h?9O_v zi+ED@17=G+6G~Kk$HMXx;+ck8v}ivd$!xagWKyG2NRh17*BR@;iO(K6Rz(VkOIK#* z|5*$#9DTBY=^#@EJs}u;UV?a7l#_ZCnZnaLjVLTp#_W+RLY$M%jta&9);I>XSNlZz z=eXAES(-)6qqS72wf0%kK7d*FPwWEScl2*LT+{ zLnYnmA_jaaK9Sx|QMnu1b&HL;rMa^QbV)IxZ|@~%%ot7Keh=!VP$d&+quaGXcbVj` zFeGhkEB?eXyJ9fjnr|hfEb?=K@rlgUG{XPKqdixf`niN=lO>+HfOy{W~>4@Kufgv8&d86=MR(!vs(y)hG zN%046f6&SdRVOOW&M_uN44CnpJ?cx(J>cy`Eom4sR;w^ULeu#3mODbZTY3PJx5Uzr zE}pGke6{-K{UVUgJRxPDe`5lfyKFZZv?u||t>+CMLWpV9Lff~qS+)R!?k3uMWNJd; z3@8vLqEUfBZ?hJU5B&_}R8Ydep*E*)FZ$p!C)q~zt5AM1>Sypi2JK4#3&LoOj}mi^ywOVTszgh zW>I%8n_d=mh1Thy-9V}i$lKe`J3TqaJr!o`SnF3*4wj8$cC#(E_~D$Xone&rZHL!= zafdi*K_w}NEZLDu0@?I*iHJvU8TQM-V3Dbb4(myVtc6GbHmH`T3m6w1i(jCQ(c z;ETW)2}p)LSkX)+Z48ul0R6q*njKk02&djb6uQ!%m;yG+$2yQ0jXwL=B#6UjPCL#j zVVos$JBQ%j=C(I?1b6dX4PY7uikJb0TsnRhLHFik$kgpduPTSEPPi#cmZNxcevXHm zc=Ns5tN5Rfl5LW0!pTgdKflg3m?78lzyKoLLnt%5n*OttT7wgKPveh+=h{s2L88Je z9v_x{EiWrlmdv@>+Np`cDENid)QCT1CiS9_8_Afi<1&YR|H%)KU!KoG!F>LS!x3Mi z9)1uS;pIB>-a2OD1!z7cgJpk3K%{bLLj2b?sVZo0_X2Xw)y1`-2< z-;R(`y2PlXJ@qIfS_3R+7zOywll=gr-Q9xm1NW8j!#T6!Mq=u!gkpNj5~jiulLqMi zJIsWzn=PFb6MsgvN*CW|jY^~`zlditG71ZQr;1zFWgth#&n%d#u5^m~ad}njkb{FG zCBryv4-t(Y;0?5EEPE-1Vp)2Xax-FN*+KbLx@%%GqVF%mr8oL;42Cwo>#tAl&MnFQ zCDL2_V6NnT`ZuQs%fu36SME%s#zJah$tlj~{uoMH!*_!o@7g0-33g&23X4~jYWoX1 z%EFLQ5=?-=u^DFeXCE-E5QvIg!2lTTzDmz?Msc#8wFE2m+(x1c{$k*f! ze6;SNc%0N8=b-whAHEPYj4+s^prwyBiG&&7+%(u!v{~}|fo$r<`er`lybZAFVE^-c zH>sR5(!|$8_>EOW!tKI`0(r#%H)<0H`0XPo4t+2y-%fl1O zC8O@-)rx0eCQEEc+!X%bJE{Ti`}&rQfprGvy*HB63G`+TTVf_7Ru5aq&%=j}iZljM z;?OCdiXaC%t|#{3-HPshwYu)}lPiHkxTJI7Pw5e_p~b?!XV zbQ(zFKWe&bu;vr&ykg3K8y0zqjgqOM=fX!UDUE1Rj0K=l2|^2`o${H=k!bljHx>hQ|}s?qg`=j;p_BE5U#Zv-psrgyJ4k zchS3$k5P(rRG^~t4DS2Si@f!*{=GL%@ieUQ^wt?5cHFzeuX9W-Ves^S^wIy=CBOL6 z!JVHKAkyfl!R2u#DvZB1HfpUCKZd!JSyTIt0{(k0 zB$9I(=as8J5%Rx>bH`WxaSdDPMIvz1Y^ zJ-(eaLmr(TTakW9mF_p@I=5`j!L5a&z4vZr@XRqry+IYsnf6UJ$J?qOjdW{o@EL*D z1+kLRrX^!jGnbqBnsi%P===C_@_uWn<6%pQTudu$XGLe@3EWK_;*z7v4Srt}SG+M! z(8l0GQ^3bGpGO0P`Sh;l_fq1!yztjG=ltF;4{Za@3@XiVps6S{&~tsMBk+o4ba}As z`A;Hf%XEpFfTZ75OS8ySP4*a2b22Kc6e}xt;=P|lm0$S=$)-jcP}GHe6vnBclQyFZ zHqeZvM1?~X$4SqJW}IHb&?G}fR~d|JywB9e-<|e;IaQHlPV7@9mVm}9kRp1aiiX^< zv1piv0Jl~eEk=i1Ogi-eyGTgL8F8t_M_g?AsD^azl)*pf5bdud(sC6y#P8Bs5(SAu zn~BcyXb$rdPsaPE^|p-n&vHDxm7E7iJ8|xT5)me@i3~REniycL`Np5w_Nq|| z2>Mx92NUYuy}lsI3#B3oi@Ubd2RuMNJC8%9;_1hGr$%ghMH!l^KJDn%Y02?5c-{U0 zR%*?B4YzcTg45PdCU!@S*Hi(Nd&m?>{+`Bgw2CM5JQ2cv7j%ggz)gJ)rOP9`t4*mH z36!YA*Z;?_8IqGI{DnBtkM@1HhWCpa8WsfS({dP>)({D`aoQxvD-&s0Xn~&_x8Tj` z%#_`=Habx^uZTk>@46$ol11I!a7!PFxoRltaYG!FHE|nz0Xf~o$Q-YX_`pJ2dSI-S zs_36-6bmS5VIGzwq|g{~7Ws%GA(#GDDep=hi=q$2D)~7R4`T*X;=+>)XzOjF%j{Wb zS7vTq{*nVz7%EL>XEz&R=$gYIsAJ1HqAo_p1f-P`Pwx2Emf~n%tWW48J0T%8=r3jpGOC&yo{UL{t|iKYP^PZ17M$fb`FCl*0CRKy29tK@oOur6 zIZDLs^O+>)jDz*ZB4EUm+_RK2y<$#FjNe;5J<<&>R+q>dt>kMATQf!4pw?iYTGn1Wj9^vDS z_{=xCRij6w!}yba#0`7m+s?GH@fSqb-!~-walR6#-tlCXcBoN3R5ws687c}24`F!-<|T*iM9V85P-d|3x)iAi($J8hRHQM2l#vje z54DP7;dBp4_=xkT%+lW?w3k7fkah-6^jHgg+;NMEg z;*EI7k$Ou!$)yno5{~ETy#HK6tNI7FhD`yq^=|T0fKrhP2>I>cQGQ}RpIhR{v+WR3 zKwCaR?AJof7+INH>3yGu$(Ix?_L~3hK4%He0^jN{Vc+Y_x?$zcII7c{-Ub&tPYd@J z9LjLQ*svZfh#dWVx16ciyh10h@~cj8H>IMXrdCo){vI*uLXl4xj2QFAi+oVa;VU#M z_i1B~ZJF16hjP8a3PdOfPtuAU9J`S`FZHG1Yu;+7!A<(+anXe_1ind2&(qh;Z}_UU z6bL+nsE~i|6)p?I<{zWG(KPm0?@;0zy=WDwO4=d~0Zmf$Ef9{AD3_`$v2qOwYVH=l z?PxiMuZin9HM%FzoL>#nuIW+ug}5&=xOw91s+g+HlwIg-SC|-S5YOV7%`Qe;QsCTg znOVM)C3pMU(S?n4K_PHz@g)WdsOXriSje#Y#7FTlu?n4yU*yuk>hjc`W1l#2pmhu* z`O$0BM$L&A`)$w{_;0o}HK#i@J|=mZa@gc4nB?Vor?+sbM5$_6`q4$Dr9^QB!opy^ z6zWsuF|Y)Fjk#YM*GP@HM3t328GbQs|JQ^17dNQYs^sr;@C-%cY7K67b`{aKqDHQ{ z<^8q7bR;dgQw&@~vJCrZi9uE@N^VT@`DtA1#4oD<4EDARd!LxN*@_>$wGvt^i82xR zeoN3ay`dh*t*jE)Z_zYRbpX>yDvCqrh}jpLOhP4@Oh-m3lOthW5Z>(N- z3GO_s5>2qV3(lh&g2Dd_AHLv7tGv95QJts$AAqrR^24W}NAMlS-4PtMX;oeFk&eTn zTaa4u*O9ppwOfvb98^+Q)ed3J>fm|^SZ%IyB@}PZ@t)6kgRY-`~Yt`&7>Qp14 z{C8y$e@Fw2;Ur^dV~A!Q;1nxcWE1fJ28-DNj5o%SoWlp}ej zW@DomYP)&vBhfxe?6aj76FF@S9HS+6t4aU)p1M3Dd}En-OFkw$+#C4g4%ON|*nmSk z2;5X#1NMUKOsb}V(8=3otn-?UgJ7OIb*Gg2+L6zGg2o|(DXzpZ6(j?VB89F4U7#b& zs#sZa-mx9=^zbn!IUYY~@#{6NAe*BapqwdFMxb{w%vH>3)y7M2}&Qi*eg6}$1Y>N-5mt-sEOYEIODn-RiYAfN!j<9QnXV*$9zmkiJ zAq}pwG+ihg;(2xJ;$J*`l7{!-qQ>~a+$=L`lZIbmMxp-2u|GwAV^Qpt6+bHw`q5PX z#X9Nq21QIhovh@7X6v6P8g#sc(_)723BLO_4&Iqn2@yrS{z&+0nHvfwM{mn0HUMAw zNuw4)+uW*p;VY5)dJLah{Jm8Bq&`8`(6`(7n7Ku@m}&0yii_dlWk+&+2sf~-1k0eN z*Ytkk>(pEeNk?Nm;<&UlM;t9}8#hA%^6k=1ljj{{Zjb?xj@llRHX0i@DJ!e|AQq4Y zrs2Bx|3f@G3kLjtp&$h+uBt+ua9pn<0sCDEq7S0md!`E}QxHE*L6sHbBOxT5@m(ku zOkkibL`*u1nGJsOJ8!3g!@YBQD4=1XBYvl@iW>VSbYPi|Pv%7-2%Ass86tus7yLlW zD2h^BmElGG$(cCR-1WNLGV{@YUsuCJhMLoNrxB`2O;V}$AX%u)W~MpXD98|l;Yn9s z=W!eLz?SidLDQMp#9#i)DOsC3(tU|n+H4cf0i}_({s73&Y=Z=af^1%dYj?b2)e@`o zdh&*cHu2SdsP2DDR(?_~z`Imb%xmoH_c9)u=~jqD5I!u;Y#?jIP+#Bwwi6mvkMQ)H zfeL5lFl&Z+9$(YoQF73WscR4{)c3-g4upVbKVXq0BD7=TJ$XCK8uOKc-npW?T7ZpjWE&&K&)N4|Kbj6Rx4TYl7#*N&B% zQ;pqVL9~)PyWcS>V;D)y*K%$Rc;1YjQs&&e&c3~uI#jIqW4Fnh^`-f7Lc!JSPe64P z9%j2a$Fz0jGv5*DTW%~J|9E9%I3ZWXEqCz0=d`}iXxIm~?f~aXS*Zd399j$*7OPGi z{yjrhMXPm>Bvo{X7~vC>V{;1Q1&B1mjgW7e$|A=o5qf~T6QgcBVt6kxqw|U2fhYaQ}9%s883C6qhJqm z1JSraWXG2{w2L>UKcaTuhW&r_fM;os$4col2}2*B_~*DPH#>+GD>_fXU7SEQ&*esx(Hi~cI$)@QXLU7St8r8>^#< z^oen}-S?A89okZq{~b%u+qN^0(DhyEi~DBQfT+j&zTII_Gmy*7&Tigz-cnFE5YB|t zE!8@8RIX_GKJ+DMmtoO}hu#rd*jK5m(=<`{a+W;3R#_^P6o<#d;)*V02)7St#nPCW zMgcD|JiK0L#uePt#YSs-USYhOJqN1ZP%)`zshtP}rxxE>?|oeO?JVCMWI91mn8=Qh z&E4+j3xr@L4Jd%elN>EeM<&6$fn^3i0BBf%l-IPy6Zq;+lTr-dX&e!mPTeELK8gRc z$>RBtbJ$Vz)eYPxytH=Z*UqHTpb8Qe9~pG0Jk3%n*IBL{_r!KnQ&Vgyw2=k6a25OH zc*gri>)?*KG!RHLE=H-Tf4jB}2yB&=vZ$^3jvKVOEKOa*M1o0LXM5yvC0|-dc?wBvixG1`GSdm)h{G!WuzR1(RTqP%>gHmSDI#F^?}SEZ@8-Cc^)PA!;?!pN{s?R?wD?V{x888=RQBtQY&OjF zNF*GLaR{b&0tNp(I`HGR1dPTL~Xi>%^B?bDAjYr$4vST~Iu1=Ku*xA{vw zf5%!j&rzphR|9ZG)s^BlFE{?_T-}oGt{@yTLuVS4Whf#?`sV&nR7!-4RaYd^FkJ<- z2VDhM)fGZEg5R;o2KxespcK??JP5>@I+l^R-z&bGQXUTN27d&ONPlOgYO@fydd2J~ z6Wh{?rEP5sDN*+JaRjzjurg^f9#GsW}5I6rA2FQ7cGCPVgEysBs z{28vjnh>trwGa|qH=I(5#5luS#|{cp7QX+m4DjwB9{+Z~nRE!#c+A2Gop=;R$IdYk zr+@M7^GB@EW;#PW=^^CDXUNtsE<)D$_*km4^;jkv>Ylv=sek{(ZeIHiM*x$a&ngOz z$W(txC{rMfIL+e8AiI%Hz{WkLQ@)0P7X%QkhV`yWSF8P@dMMrmmn z-NH6%gmg)>jSYz*fz)V08boPH=>~(65CN5tM&JG4FVC)P-+oWt z_qoq~PSpkuDEdk#otm!nFKb-r*P{x5c~ydX-2Se#Une+y`opgdUVkPnAgj4?rA)cS z0@asVhzM5bqwZ_blrfAh3WbxB9j4ouF%H;z*7eO_DJ(_T+2VQc$3!02eiD#oA_m9z zq3rJNURKTWmRaDV1wyyX1xZ=!q_F8*hZ3_O=!K*FIe^Y?C9(S^h!YM5Bi9RL#bOm3 zm}cD#iC%$?tO|Zbx9b0hrlghuvN3VSE`NS}dX^hX>iXERWX;6Xki{5P+86&0j$%34Ybb!fY50)i_@_t*0I z;~OIH)AxYUH!2Ym*C!;zia_C99Si@P1cuV`33@A9a+VRczm}D9a4cuwmsHkKu))3p z>-UF7A4m#Zb?;UeT=qHYodfQQqo>=2bdO0mwkR6b+h_J=|5z3D{wyp}&4x%_?voJ1 zMMIvFb!*f{0mjks<_&=sFYcR1tIB&rUt8{ww^X-UK6ehh+$d(tcTPh6Z;3USC<5?B4}~; zq(P8LBB9hDgR__cytHIi!UDle`_b^^r;N)L(i_wUZ&)|HkK%u2QU@~4C1N`5=4vl` z(5Wi2AL+rAN^T8%6_sPKdTD}UKD(E8h610ylSjV8nFTi&?x1F&&o|D@({`@0($c5H zDXdw-^K-q&4SAVp&F!}&r{#LIq_KLmFF7dw^Md-%H7GU*{w>hTT%eOopGzW!%uo(F z9Fxns$s@3@3&4AT^cT%=Q({RH=nAWItFTko1~EqSpQ(3`-Ey` zF)&j!LZRuT@saO0IlEfB80FWgbra*uTa7taChLm_ItEC*mn~Z!QWr=)o$;%U_>CZT z7#rfvT){BZ4ssV)+;bOuvE9}$HMXLTZ(H2oQzXRTVzX8Rf%3K!iN5lkZ<~0fR^F$A zeTuB{oGrnagye*%+g>rTYQ)j#*cdZunGF0J5ISL&bN#Sk&ARtDI_7Jaj(e}E8wu!< z%cPWos&IKmSK82j8w&)35x{$fc#A`S^V`$2rCtI|tyQoIPhN$eCi*^kpV++Z+7Lxg zZxmavKAlm~BIo)6LPW?M{+96gLCtr9beXA}Rk%KoeD!&%L<2*Nx6PXC&?U);ZsPU7 z22qb(g8c%?>w?>lXCB>uw)9Vj=UkNGKU(I(2gH?wA_HrgnE3oE;)kzYKR?0mn^2C8 zQTBQ?0m<%=0scCe6pMvwOli7+dUKd4AN6IUogKge-TLu7Wh@%z14CsEUVR^h?r3{H z)ErxJXsW9dl>rVU)@(eYadH=2(0JeV?yetu`5DE7-(3SX0V)`t(-jtCz83!o{iuXK zZFY|(9l_s}N2q49GwtCD^MVjn-a(UXUEdMx+$)Ko{;8$P76L@I={Z(OkkP6R-v6uo zaaQY72{|psj9$I(DW80|O6LG-|J;T@9@Cpba2b{a>b~1uz^zvX8qbsbO|9mAN0kXh zLmkKnS*nfQrU-@>Ul6-J1)S|a606^3NH<=SIABi+r%q4lEhjjbNQ3xGO}Ge_l1a-_ zvERg#YB5u_RK9jW{SWWQL=i~3120-^iZT;+#>W)jF!3cafcLFurV`Ir#He0>aWnE1 z$W>9M{<(vF{+JpLOZz3M-Ej}?d~%YbrWPn@`liCC1*vmW8*_)?Habi5wHv2~{_t!8 zM*Jvy7>ObsG}ig0M3hlp|Lr+adrk|8M_Z_^3D`;rGsUXOMQ~nBL=Te|Z~kF)f1)9v zg^$y?UE4Kq7kd5*gZLKy7eX9W`jtaWsq?89mlVPHyZ}%WPA~Hj_Ma>2g|fc6K(vUu z)a4kAi)la*Dz{&Xj86e}2vBEJg}9Q(prLA>rWlKpDg0*=8$j7WymmqL$Y1?dW~c?< z^f_Ld`x1RTO7>W-m$uMz90FfUyWyO@m+myGO?&^+CrVD&kFx^ z_O9x+(lV3LmQb1Ljnzr&5($EZrFc0rfl%%rh_cLS(kCbzc05l1%%SJgq}TixPVk&V zzZWOiWI}~>8_0F3y6P%jm-O97@{S5ea@zC%3pGx0YMpS&MNw%~y)uDa@bF*w^BrEk z5zDfhw(6$RM_Y2TI3Cq+QmsSZeET?F(&cnGsBbjZ@2A;jIk9Es8_M~P zZz(R5#~^*f?)nM4!r1eD$YLHhIlRiFX}Fq2&Y{Ny^jZ4-LSLBdetz(8 zcxeR{z~vh;Ucc7FcLHZoXCEJaej;eH{ZP&Dq5Rv7E%r@BP7b@oaWzhmH(vN)II|ki zHY!6GmnPAPP|#x^kpYuRrGd$!xCmpkHYFM3UjMS=+8@5M5O$3Yb3aPs7i{hJrr+Nc z1-`O2jE7~dUSioTJ=AA_6Ui{hv-gj(1+4J|T898O&*784&?{q!?Q~JWmYh-4oc+qh zPiy4|QT+*B;CL=wPQEmbluJZ1&s~vVRj)@uk+Ts%auPo!Cd#-i1eh^-80((dniv=$ zFXs)u<@@6`)t#JNm}E>jU!P9gzvv5rXX!oE%7T*>1$P6A{ebW}XngX%1)?!jyDw?% zZ!(j!#$H(S>fddo+>Nn}2G9@qdG9r(=ABO(CwJ~`rlrbH5r7Cyj{Aq8qR)6)eA}wZB-zxIsp^uUd zM|^Ku{<$fD&o>Q}y)BardW}^YNB`_L@VAgjrJYkJF8R@aopo?Rupj`@jxT{9|iwDabAG zhA{{t`gg6!sO!`h^|kiV=$dj?>)SQJZDl4Z-99c}R#QqNz3H^s*~;*e=ARzbvp$m8 z#OeKZSco33!N9J^B*kh;Y0tC&@}^W(vLpHVt-uo%%*wr=DXyLw<#xvy45(AYQ0x_3 zuqLdTJ6KuRgN`IVKT3`rz-8P~p>3JzEmWv4nj4`0%ED~-#ez9_Fvmvk)N5Y-M)tw$ zg=UZy8nSFnINo2Oxig3fJ@-GWv^DnQ2AW<^T`e+4uTgn-YBA(tqrWWN|tU zKPzOkNRt4&oFIYB2)-c>2{A2N68leX^-{3FKH9ntn0e}H{NUxqH=%Z~2k(Y+JS_}m z%wTDzRnECCK9q|HedRfI!JtI}VLLX&X6x7j%)4A97n3$FvKyeDXWHl?_{!Q(5@rNu zk^sgTw4z6)A_FFrk-|K-`lk9FapL3RByVqyT$vq z*DRdO$%Z`$g^u!EwkrN6@HcBLnm&@7JM#S-7>9-w@b7Xs{X3(r(CkCuCr8A$+Dr&o zhbasj=j(kekY3rphHpN9Dd5+CM`U!l4(B_2M8i6b>tLc(9OF#Gu!|1-q5K7^p*I1v z6@i3>k0|5w*8uDiKzIVRHOB+@7}q%6EHJ3^(l9OY)pT<{o}Wa^`!9RVi>&a!T1S6& zGveINoc}$;8rVi+O%Fdwx|Bv0p};xV&m(yr;|qFY=+HZ}BsG^qa7&VSePQVJvR=)d zH?B?k3iwT#3qz~%hZ%-eBiiFymK-r2;OPD!S7Iq_$sH;v(Z~2C7R4oWplg|?2{qZK zrY3dlrjyBk1A>}j>%W0ZFzDFc1E^>7T-ok2Xw`j{4l$`>NHR)~fkY*U_om1SBU6Pw zv`Tj((kkO&lKg<%8h)dg{nX)4vi-CvnAA#i3ML-`DG?jh>5m3QCP5f5HVzxM=(^3& z>~<}l3z|E^)#Og4D)v#T4|(4R0K&Pjb%$|3x?IPfIrVdzrS2UFE*P1M=v@DLlKli+ zkFB@M6%Vdd|E4GE*Bj3f{Hd#yOLPuY6f!OW=>xTkSeN>c(GIw4>EN5o4xAW!MU29Q z-#wjqpe>r*r|Sm|@qVgt`hxkQxr*andY6KteiMW7VPi2cE(1vLW(TlHyJNu~)5$Q}JSDdJue^_k1d!dbvfMI4 zjJuzo29fC~=baLR=|}+~CAq;_RjG8XB?=k#6++jfx1^%X)22o5z}_yOgtNn?ulb2i z4m6u=c{Q3M)E1kR0O5<)GE$eW43Ek z)#s6WvRlf!^%m~pS8nLiF&)Zm04bMv((>j$YF+ooOs2~%cogU16i@*|2nI@q31r5* zZuTA+ph;RLFpMnFFIo&RF*tkp>=(5Fe2tX#|g?lBR*R zzG*drZ}E?+2s5o`Y4o6DlQtPCB%#QpygZo{o$&sR98w5`M#?Y!{sDxngaT5%o@=NI zO)3&;M(98iE;6bpDFE>-_*W*By+x<^7!@XdkB3K>&&GC$Sra_%h#}XTjl-_kQBdA| z*irCD7DEMofxFyMLi_HCT)>CN!&>>S-^+4x z2Y$cIQMURr`;II0E~634nd)B+6TdCQ9f+conXPr2BQ{R@VhRLqytR5Ajc&qyu#AuU z@u3BgWTzAXVC7Su0p3+U`VkU+2Cx#V9L|+LE@R=$UR15CmLA5QRv-f%)1senOp5@W zp(SW2BfS8RbgABz^y9XF9^&xX*~I2lv0E@Lg*bcqcAh97r9B;Y+k`l&e#U~Twz8{aRVD}q%5Tu`;<=*mn12`B*|QQ{_&7m;#9f=;baOB zSt+D_(7mq&qj;j>3h5&$p;H1ond(*E^m8^60MSx79-g)CA7Yvt0r1z95KPLg0LD>&#H26dD~Fn&{3^~EWAgGnU* zMVRh+(yG$=4S#$7aQn$`p-nSZV)&L0EQ79cVJ&VT{`>VX)A2Aa#0$#j660f%)-@PN-FMAo6A_=S$qQZt-T#q@u$EKSnjTA{SZu z8>$g1D?>9bDV6v3P2#^TBbf?Zu7u|LZ)b+RKN4n>W`Tbp0cCzj>Wia?CZ7ppKLRj- z8{aaReS^pGQ*8d*czFhx#?*VlVWo30&r^9pSCXA-J7*n%Z>1)bX?N3EctAIsA0~v&;sXIz7avJlJlTeWQ;hRhQii&t>^((OzKTD4u%^pK z=RN9P`DuHFZUVzIo4DVMNMrQuJMK!+Tskq6)%86S#^PF6I-a^hOA*bC<7dpJAqK{h z_hqo8U^8C(%DXAmAtdlJ@n69O98!%5OJ8(Lh8|=Hsyyw7-OwV-t;3wadf)GItGQ)_ zfZ2zhe0TNvVI%3Z34%0!xd*jCVa1T7ASATZ1`nb_4=|>HR-%HwK+!#~UVI!@K=SaM zimWMlZyh6q8s4H~VB!QzFyYm2Y2JP$enfkPFJ)KVJ>NaMrvGyJ7f1miGhnKb_lj9; zrs30LsuKoGl-7(5A0B<{IMpwQ5rO}3ml1(lN=bJllXBC@9#QYINV;yOTJ|?+4_sW* zo1*)CCoJmnDeZj40pkeOs_A{o!8NV??2a$}(j?qeuN5u&MQ%6M_8rz#=N8~oR|UCX z1~hhdQ!|(+L7YMdPi!%+*P>GREZM0W2ttd!Vmc!YAL0ujgqJC#EiCbtDF z#CVw+^d~u-ISPBaKmKh_c>hi3DX)y~3C#AfOFC#nmRzCQ)oHGqwDa>#@X#`|k{?Zm z>au7iHO3J3vLy16v6ux!&Awg+)Yf}TS6rJ$w~|F~{hMy7%YAWFrW}LK zR7bt|-OrCT7Pzv#J3k8!fkAdQ_-^&Pla(Y(h-jILIkeJDXJhhxq+Ts<8y>+A{4~Ny7re|g@btb-q(^)z zWf++GP}lrLD%vfNSMpurctlUyOS5^TVAY3ryr9d*F@u9cCXbhl0m?9Y5^f0^DH)YQ z8(|eieEY5Gr?qR9EO{zue7K@7=j{mW=YHUX$S@rQsGc+!e}XF6c5QAgsb$5YKTHu- zY(*n(Ep)SubEi7?oP6Nu=B)zUA-_sy4vY;;3gs8`c9Y3@^t_hsurJ<7)=;`%AEQkfC$II_gt51%^}%;)i*w%&lYsF#d%g>vMrFdo zzm-a8f*?GghJzVCh$3y7V2woP!&K1eKh`CLO(z}*2XHE1;Yct`uRR+vP@Y3FCSUEI zX8ZJ~8EFnI&>jEju-wT)6Vt@&;mjeC+se3=MBOg5H`B%S%#e8^mF{JAM9TxN=V?Z!t==yU%;4VMqcRd@1meQJe$Uy623X8SSjp5|Ttz&NUS5X|pSa+xr*#4WIyM+)S` zxUpH71FD2UaI91I8Qi2%C#7@TBcgV;(JI!+))m5&JLS^Ocx)ckd{3lEG2{j-pzbLl zorIpg6^u?I=&=^LH4baZ1r}G`zoL>4ezE!?ymw5xFao>CB^PFaqKotvoj+Zb5>H!J z#)=C7biKGg+1U!7s+N5mltzBOH64<$J3OWuwXT5-EA$8VNNP6y0;iFD@{Bw5s7|n8 zN!gr6DO8P>gG)ABWViT%5 zK$WO7L$vrW?s$GS7&ywEwn3!CG^JKxIcpM>s||@qQ70xMgo-Q6`G@FJz(4`u->TL9 zfI&hGIZz(1nlyt6kHyvh05+ebJE~?w)nC4#xHy@CQ?$$Z=^5S@J@pSLv)OlcBzBdsk(HFg@d1O8!VxjK` zIjb_n@;>zx+7?DWsY6>qWC;^bXjN!#DXFSTNPg|O3+olJx~8!+s$F9><(H}U_&C0$ z23C9&dr?}Y7PQu65CZo+Y5Z z*T_Ya6zt#DTjhLUmzOZWc-*4Z;@0(jU1fMnN-^akSX|Ocpz0O76&H3N_|WdipxiX* zwsQ-?WBOD0QeNm(W1|b8xeedRJMBJh_({w74p2dtM!5b`0{zCe^|2{sD@&W_a#$+9 zO6~1G*=CG^+?$gUF z^;&5>GF;B4!PGQGQvwMx@cv}-6&!DklcsL7_lr1nYo?q0tMKdn=4{ZW7svCtNKSy* zB2OjVmq zo0$OCimdNtv~25%CcviWVB19%&s(?FGr?0BSXG zeSFdoqg{+bS15xjPmpdkSfrze0eu38q{equKo3*?Z!z@G19gKD^%cM5{6ep=pd-;Q{(;hyh+moI zl>>wqnTjcxtd`ZFGenz93R%j|`sQ8oy|>@LNv~Pt|cTvjXnSFC|AdiR)m+Lb zRwBZ)TggH~re-!i2d~#z{p{k{bNu!5L0X)&FU|IC*;=VXFehL(QJh=i^YkB*==U8j zZVo^DZr(CsA)Hump7eCwWtV6B{qdz?GR(feVwm@4SGFnM88jHMa%W3ZF#)X&OZJ_aInK+QT8>YCsu42@ z*>3C@c`($5Jjer}tPdx!%ao-jAE3Q zL{ep{9(ev&eSyBSA9cF~$?oYmOb>Xc#5x#V{J z6_Y0P{?X~$!`pp*o!>(MIHSYAf8xRt)4N4mZN|5xq$8)L$L_dSSjdcW=r!um<~=z4 z!H)pen~scCLcL3?S8kV07K8tEw`KRoV}dC6Yl8e`u&C&JL=l-g#lH;2lh;QabB-cv zyoa-bQQ;&s0@$dO@|;%H`MHjKbNRnv&jqzh%y6yX^qhje6WTOUh%$1^z+i&LQRLYh zd=H$2HItaNp|LTyRx^q?pc&uv`qWcot&wjWorK~%*g+n2YgWH1CAznR@cm_`M6&=U zbnoBQNVfN*GA*V|sn4)+7#3EvEYlN8o#XfAXz1#B8;4zNy%1cwHalB^^%mUz*6$;k z1L(Hm^3`^qv?#%XW`Vh#LPox8P<4xZ>DOaRo5RvZG;xe6_%d>dLx?2^@_KyS8A&oL z`)Pav{Wd@R1K}iC5S~?hYm3cuQ6h|ba`BZ7edH~HN3O^mu#@E~;^kXFOmM_nD{NPl$v&la#w1sQcz_$s;y44Fo-VD0j(Q#h1aLd==}{^3}=?S z5rh^e-lw~b-Ak34$wIHOXN0Izd-e-_!8i0z_2XDF9;o-fXgF1gUQKU z4&Y~*=RW=L&}^6aQO_d<@JVCf%Osvcr9+mV=ae!dNP2A9Mil}L_D6V*Y{FjM)fozr zyIVe&JAJC?G(W3W@HcdE{oFCZtslz<7{>nSFmbc4C^>bPB?!NHPbYhH3|lveP3XJo z&>~o<1^5=j`kbZv1hPN$D-8~gYh~{P_#$)k*P@f4=q-bjN6T4Y@0)NF*8NUm{R!pR zoe~)JW9a5PiloQRm`Y<56t>QI88W?WnROm_i(~oNSh0hccPE>TZ&RAUd;J%C!oLVM z_~-3M6n{MZRGDO4{lyKbWA7Lq2L?KQw6(HCHF0o%I9HnCJW##=!%R)qJu>NIFXo!c z-f}U2__`bX=e3)XKX54FSz=TRDo9>T*o;b?N)t)m+7TrBWn`nF5GdG4XynH#PW?8K1jZ)S@d?NgQ3Xf^;w&G0BQ2|3fa*T@2q=1TIk+A zF;!`89ZS)!7}HB!e)2t9Dt$enjU_!D1g$FzJ&;e=fafrF47}02>w!sfd6dlLJ0iHl zb75J$z;Vd>UMa$jTeF!?pDmYs$0^o8-!X96IuB1V4x@yUzz4nY2(r-5tp|{Ax8QK^ zIe%%Z?u9~zR2B5@=lXM&;NTu6(11e8HyGug%hNw+VLUWLV`_2;{QMjk4wvpW5r>Q zo|l7^2s3*acsd6>D}K!!`Hs;oaQ{SWN{ZU~!`^hTR_KG+elvN+rQyFPhr+&}<5F$s zQd6@ONcP^_yenjMoDO6suga3tD2c!dmQjk#l=Mxwz2KalNL#sCy8Yz0H4N=S)vr|z z02v)ylt03fQ&c5TAy`)}r0=oBzfG7@Wb3eW{cZ0%NF1+C+|O($gpGhox|WL4rZ{URipsw zgvSlQtL;>TWr*-~`4gk@93x|!Z`=$%&w)WVRK)ogc4WZv9iD5D&?#;>4u8l`J%S`D)oci*>S%Us8YF&~FwYsTI5IN`)p{~$Zf6Rk0#l{Cttqxz({gGdcE#cLr~U;wskDdDpX>jqxjQT}E>@UDDYPEO zyBM!6oPpj~T9)E;v54x%@N6PTDpV zc#y?y*HBU~ki)J;J}uyVm{nQ{h<}OCg4jXb?Y_h3HUAb1o{T@DrzwO?SiobkBzoEg zvKv%jroff2HAh*a&fY8_yoobiTji^{%`~yOXby$rtE`nFifhkOo*42cf{(`8$(Q#g z3{6u@TK;_hrE6^sdmKz5)#&xPH#O?rw&QfyYr|)vX(>*BB~iN59i)eDTFKeBfGNny zxP)er!bPEngc^RTegzFth_}7@z^S>&Jtw?hA3jYmA%}q#TG!p2q+P!Im2>13m!*tU zqY$$mjFlnO^5OUk#bGf@GB_0boj*DueoZ&IgQeg53mK5tc8O=}|^%N`s zldRo9gUun8g22*vO2qZh&hIfv%Iu*@cP)QL0g$)lECYof`Mdy3YQvyGEUD9ri9ZV- zzCGkMH;Er-L~(vK9;&v^RzzF6^0#Fk^0E;~67mbQpsU9~DaiH2jy%yT>q^eVE+QXc`g= zU4vfodEOwBgn8Q2-Y>Y3s7Tozi{~F1hlLfye_gtIu<1X*d~Zw`Xq8V)*;3>~YNtpT zJ}K~jhtvFj4Sp*4#o<2cLKl0>~0#>k#e&2=vqb4I6 z4VFCj%KGD6Qg!>jy2Izi^|2eJ5FhDIG#y6MYnn(TAvVeH0x|$cURqfhp-yGfE(gr* zp8z*nlcOmMm-^zt{ht7iky+}^CwBy`(^Z~TUhY+JTS?W2kIPNQ=k}{b$ z{2C6+BHNF^6!4v#h~H1O@ad`ieRn4IxNH7*TiP4`HEM1Nwe_V{f_i4cE-XZcdFdQ* z*xjVlfCK4fWdNtP-XzHq^K!pB@~b(0ueW-V9`xhxa7C4%M|pcbrr#61fDcr&Q!J?( zy1H7qu;n>7_uY0i6!Ig3tJ=c6GfOtoS%;Z6e8F~D#zyFTL{cM%N_@PYSIw4*E70Gv zGTs8@m;?A`x>LowOC@essse)=MWzZyBKB;2?#r4Ve|H~B$Q;eTe_*?5l+|$1MA^FV z8>Vz9O#Am^hBFiezpSbt#sd|&J`5x1ka&vR-b|wSp2ZWxQK`!fwzGURHwWid!Y4(i z4-X9pIoj!)*f?zgD2Onz)c3m`LU@G#kqJ6}=oyxffOtoL`~IHAkHegBccmT=I6H2h zEJ)X$^tmK1?VKyY;i(fgos*3OVl?IlA8TYQ#)=CwLN<_&v{5=n@?!^Pe`JO5R* zO}{qsSfEg5U^PU>w3IZy1>voUy+id>6U%bu6 z+L~9w!J$dWKk~^R_qdPmP1}u9O@HX_S&}HH1>Kcu9OY;bGrAstTXhT6oBr_w-cY4o zUfi3DQ~pDrRFVhRMgbrRXD**L_!(Wt_rO%sm05<+yk}eNz8eg~!n`Jk%u$YMA-f{+ zPXyFK_#KvI_J&)k!M{@f6 zeJM#!lQ_0Mc%Xh_kR3FSiNnM4vR+R?Wy?uNOA5W>5b#W~MD-Tki_ibQ{Pv{n(6pZ| zPqZ5GQd!6^IPLwl9{Ee+!92xdTW?(JNw{SbDcKj8%e-N-cy_*E=dcZCkO;mLN_$AW z2;fK==N&L#)pX^nPS7Nicnxq&JXZlPfbxGY3s(1pIu4n4%Z(A!8Vn7t_z-$Cwr^wc zcY(4qw;ycWizH&AN@9*rX!_9>6U?I#IWQn7s+XIF0v}IYQmRMa>GDin`zUSql-p<) zrK9A5J@!s+4svCPOL#7x=}sM`Rby}4F690OPW?A9Mtj-sY_{PjQ~t#T@0DcvZAeK8 ziGrVZ#vsVFM1&9n4T~HF&-N!JT+q0<5 z@hK$*9qW4h=oh`CT{C{2%%rWA#HjY`VcS&^VxB8i9>2YuaU_2Z319V>)) zP{;kV>ex;c^q}~&7xjMj^M+l%fYNR|^zi8$yKBV5j+Krw_T8TEod1$EYwRlmd>Q?9 zryAMr-Oz{3JA5fROd33VZS6&M?~_~v3C8fpA`iLyC!>LrN^r4g?1B}T$^8*q+oOZ! zO)y1V>bQ1VV!E2G(5Ta0N|(QOWz>NQ)SxEvdwxa&k+b zepbW0W-PPfNfx6ZG-pBHDglC5EM7#FhCSk)uaM$N^6f^Q|LQ-IlL)dT;^!F1+9~$o z1>3$>ase|h`6Mxpv`Noiv6MWS#jhoJ^e}przGaF`BD|cvRbLs-A=BuXOQ^-%Y|@Iu z^hw-kp6zAYu(Mn()La8+RqR1tBF1>|lE?G==!(c=)R~+{Ut*)lcPUmsqnRTph zx4#I5FjX1^qzh%+)$bHx)C4CdcAI&rr=Ij*bw#OlFDPMoId+$Z`%-L>^RYa@P-Bs3 zsQou69dO)gs9Z!z88baCc*mKl8MT{@z6619f0MM7&G0gp& zn@`3?RqOQSd?OFHr4*yv1;$?W_Y?EHXFdkS8m!o-PynTXRR&SQ>y>1F{qi;lb$gG~ z;#q-C1cU_NN~g3#+Cm*4aG#@>t`4s5LIMwQ#!bXpyp*c_%cxa0oMkvS6SY8QM=%fy zgW?(B-&DJ zdd1-&?iDcxBQb;Ff=3crb@Rphd&*{5)t~VWyVi2T**HNic)7<>6h;+adca6=dGvoM{5R^C^N%--1j`*pEfHQP&_O zsI)p&@FW1=W?TgPLA0L`!CmM|rWfNwgnbIVVm#3GCI@z+Pk?i#k9F@Trkp6rFe6ex z^09MDGcGx*3CwX)@|G4#W^^B+A(J3;HJqTgOq?SO0X=+x&M7k>UM*8$l0vP$@wboUlGjweB8(_VhoG zo0^ouUWiUn+hlLluzJcU_XGQ_Rjc4nn1EHY!yo09fh{#9gRmzeN-a--o=~Vot3$Px zISWGb@tw=d>aPaBNkKFcX%7P&qyA>kZ^~!A8th^K7Ph%^WWISm9x^LVSfc*uVqyF~ zjNvoaYerI52J%0w_RB})3=I<`ieSGYZ48?;TUX7EXq9zc04c`8E80|C&%A~cJ}pA1 zZY;!ZyEaz(my&j7^AB6oc0b|3KWjXB=E=_C`)05(g!R)TYNbI|>G!>{13`Z|T!^W% z492}8wvdxmrHYOLlrDdcBYGC8M#mk=I!@_~SJ8r1FFc;+g)~;MbDEewQw(_ypiS@} zq>frH@%DCw283kYL~j9ccdBOk;r3}mzVm6nSl8YN7Yh*?k^UFq#MaRZvWXOli zhR=8ql5lU@i;it)3l?FI(-67zE1)0=OQF*U4R5J%iDQC-{DJ8zp4Z_qtTDno8pNwl29PPAD=E z$Yf4X8c$RONfMxUO02W-@ALYu$#!AWi^uSx=^Vq#n^cGnH z61Z9U%t6j`D!?5uiSBf+{+Yl6JxAJQRoF*N;)2qL1q(}=juP?WcFx*b_s5Go)bIaU zpOL%UPAb-y`JS+sm)BqS5E6iCj#)@(=gr(%N$C4o@`|2?etIU?qet_L_(o8WJ3F4x#XbYtHUPEqNtVu$GGfc>bB7Y*Kz*xIR3<*Bcr zt~|MVDB)C2T}ktRjs#@jz-8F`?1?QY_1JX)O2)NL5MY9B;7hVAp$X|T9oa-P^ZCL( z@|)2?K1*eWG`lduJ>SW8WBj_(-~8q@{p&j?!o_DhSl zF*FzQ^kkzxNqV#^mwbC@KVSZ12?;2-z>_TfLu8%>NX{?@$9{VwI)P|gMuU*!Q!7u} zx#$@bfO-0QAkD4wHmE)dcnr9TL~3RT-8Z>y3|caKzBus(uk`mp660>qaDnq?A_kCa zQzj)Bz3U+hK8j7!#C7%F`{$J{c3)GF0vh*W)VySZA)aFiJreIdWoxFfY@j*q?!9v>?QF4_SUfLHZj1(?Ij%L#j8V4x(WQJBY<7-RrwhUyVJDK?h1YmJPsAGPbc zl#$*$nheUFR>EfGutkWoR*<=M-twuK;{(t5`~`>IKv1(VC#Z;?ETz+4`UfY*LnS+A zv@o=>ROONdggi)UBN&9J3nfOG?zASgKnF-uraB?}02 z-uh@)ZLzhR=1R+rv90XoM{&M(Na@d&4+8z;6|PgPVxLA~Fr(I4BX+yyeFHFBI_IYg zCM--(rVnuFB*R&`a~#|FI_kyl)h5ztvpJEO$@YeHJaDOEy_$|AwBz#+BzmlpvY4)p ziPm!j8c!D7*P{s|-wPMQS^eg_*%g*LWF+)Y_&N2vYu&4vNPXxXs(43=B$aQl_c2J` zz4PCV#Qle>!uy*-@LsM%Fis!!@Kzw)4n;cZO`Q<|Z#I2RZh@kX*NoDVO6Si>JeKhC z(wFx3^yvMVSrXWsy++HDq`)PM)btPYM)i`7R}`PgXPz&7SRLq7_iD3?=!sV{1sp2F z&y+M$)}6=3atO6ol7DP&lx`378cCylaJR})5u0nIRYusKM!^X@5{Y6mWqh?owS1Io zQH0w0?q@0MM`!Ng3ttNdR#qizIk@F8N6OlR#o_~M0RRcwwx0+t?2^%>f z`xwpJ=M<1NBbkuc2WQTipV=C`eZAoJ&}wLtCu@~S*2gKNX^q2!=j)8pB}A2bhJ$P# zF$WU{Q_mhAJkWJfMD80C&>@#PIfGbJ%T{jJg zX#*|YRE{3S^i%mm?ziUunj{U92xz3#`dV<1D^st|pqg|ePt+NIVb#QadA0AEWG!E^ zH&a^g7d!I#J}P!TjmgNmpX?yDh+99zukOZ$S&V--U|ezg~Rk(s5=VWrxZ zNg3#B)l^H3?SQ5H`R16v)6*D}U0BX1lvJXd;S0lgAm0FYk;oVgWw!-;WEK@WSzhp` zP}a;s4}lGX_0*RrqM>aUe>!Lg&~$S>8zt$?Q#|q55p9X$Sm& z{|*kT$7)Q};0~_UuDH-<6Z5B{Pk)Evo^Xx+Qb!n|lO65=5ltnd>k73xJ!R*jKAv)o zOzL}i5SdW}`upAV%Q_M!EF2K7vN5i_v#Wvwzf23j)h+LkcXyjrspAu)OWz^A5F(xJ zy8yL|IZkMUrQ<41u(sCaaoQlJKV)AoRVnuF_@y-c#Pj>FYyh!g6eny4 zk?=N;WQIF^-Q@p4u2DQ+sn@o7C!`5dXkZ+rNK8m+WtcPo!cwqixPaJeG&`$llFGF~ z$HReUi!|)QkJ-|V!;kc0>JD`(M(05MZgVN%CTo7y@-+;kn0yqtW-25ajWSSH>foEl zW2kUjStl?k*_&d0&+kPnNy-*!iZU!C`H}b8A4nf^hNHrftR4?o7@x9W{#*1p|9u3p zNegZrX5!pTBhj?=>|OAxjq#W8o)KDm&6UdPyN-<$Pu{b`rkO*1Rbo~A#0P=etVe4l zZi`dKAsA{a3;7ud(CIo@X?!J2!i~~F;bH=c++H_(1eMGqm)rW3Sovv7$U$gE#Ifw|iLFl52)j`ZkT7;qYvz@P^)mWFDtm9mkFG;AtO z=FJccA`>u#fx+!M_=Sd9GA&Rk*hgg<+r>XQ&_<9hgrb$S`-aTr-U~j6C`C$e0Zpq+ zyCr9&zsb-JJ5;T?>7bN%Q7eW{2po>eQ?Uz_512rs5GhyC(BolYh42YaU?E?JHyHU0 zOKb3J)~9mq1QG1^PB4x%zD%~FO0YeYq*Lt1=JOGiFcPRa$5z6ipKs>l-#Rd0H`)u= zOlg@o%N)34C`k3gv;*6?%_vZI1gJkReZ^*?Dj053JA*PHLE-t-s3K`E*})-Acvwy6 z{s`~JW`WQh5q0AH@U&(~5?{z;du?507M z*>k;^Rt}CDVD#VOJo$%SYmr||v$#Z&br9ro4t*x2_U8wbm~C|w8#0?8c~M*%DT?e7 ztLr(KNKSB3QpINZxgPTv$}hk*K`&@02%cU-n76sdKfNf5_(h$$>khGCc%+!kSg0CL zqlzdCVF&UuF8ah2{H@Ad&iku?#ZGyw+CV-tEY7srSzdCPpX}gx{|}N>B6RU3a=1-; zXb!D;)$F$OB29<$j+`oAmDtMG9}P}=@o!}?gaJarTI7Q1Xv@|4fZ~9X#WC{Y7&-SlLRSv%?^$lZ3?~qB52j{LsHzZYPP%!OXs{5JBMvUlX}8^{{X8J_QU= zPIrKNwuEOKiq+_fdIekC!q8yCQTqxpK2{E{J$XeITH)i|zeaHPL&NZi$8AF;om2P6 z!k$<2I&l2$&5x9+%K8^M!B}&8&B^dK`udLwllD8h=M`4uhpHRy$x!3nr5mW<3FMH9 zu?xRUBLBx^5^{ro}J;cE1cpxVM7kJM3mE{Kfh9?j!< zqe}uSlbYyJF`QsesX1<|)y$CS#^BQUkSh-M8MKhP3 zV?WfsjD`vIT}84PL=ytvY}lH&#_qx8D)S}DW;mVRc&~vaF{T3SVJP!u3rQ*eDu897 z$LRuz(>Jh{2912rba5lgC}3=ksTKl%L8U9r<&j&_U(0nW#s6CHS2eHFd8{*ag>8b*7B9^e9q%^IZ% zPL{qzffO#Hr%cRKaDwbzO=HuT`|uQ;$fEvJJD1cl&J^GFhNWD8a9NKi=E8l^L!A=* z-s&@Y5rNCTlaC!~qQ>ipkiNZm2Z_9gS4xY)OL$jiABSZxgs6%&(of}q1REvIg9Z-I zv+=~wd|&X%P9EwS$yxa#(-sPEV4f+J#Sw#-XKRu5<9poE?4%aicpe04uR))6Gmv*% zACCXoVQ3bxHW|7U1^G>^~HqpUQb$j=q&6IJ0!!y|&iHI&s-Sdc6@PT_3nhc&_t z5qU=*s)*UW$PdsR3^Gh8XYdPxm=$N~~k`rX9`$6VGwB4F>h{8qGjx4FbVU_@xa z_un6ASU3nxEH2FkzQ^8>dl6lqz}0}Ba__ohNKjrVv!nZKjO&f zeD?ye-u_B-`Q+E3dTyz#VkY__w=rO`Do3(*G`ft)>mFdlTi1$F3in z{u>zVsr+2T{n?8Z8PuQl&(Mc5q`HRC`+2v^`s$6OZb5itsa0N2Qr;OWR9qrVH6AQ3 z{BpJi#)x{4YmAjlC;Hk?rBU(!jjTc;U-(?=~Cc6!rWp zp!cS^WuB1TD{wGo5xb^cZV@c&=h4&N*wJwCeUG1~ts?+=&bRVBfNw1K^Ozm$yI8W8 z1cvwZl(V`~8EMAsWCsV_2NW7dPp6l+{Qd>3Z~LIh8ql^meak*XyCGnuBXNTR#H-i9 z>JEGktqPHE;_Ly%R}3mZtO@74IQBi_iqI>7}aIN{Rr33_hu7S!Ol4ks7tuT1Qj^!w6|^ z5f=W_a#Pmi){@Y~1Qob;E3ZI%dYd!Bc3_u5uh+#f*+mmp={~DwtdjRz>P_q?b{n3B z@BdU7YRwi)V!_fq2)?%;%}O=WO;b3>5ybFaX}42uZkjF-c}HGO+SiGV9OrVI+LPG& z-2teP5%6`42~rQeRO~Y62aIY!TxoEbk$0_lg>`J%sd(?}*yg*@Y-AR3bSYBGdIo0y z)#%RYc=N7IsC&7kE{2yE z?m;m(r>_7KWE*l2^~LJ=f?PReicsw%@$O*o>xmb8|@{4Ykcs{Y)ammX%HtRn1XT|#!h~IT% zWkMk&v0=$p!=1Bz7CFN?$tJ@CdV1SCr0yYF!qfv8#=hk>&uXBr+-5eNvqk6QSylsH zV1W4-OY|YAtaeA1!kN3mv^#dqBY){}-!9)@X9d)gtqZWpzR_k>Sw&NOs_l=^h9_nX z9lMkJZgk6!%reBAZXcJ727i!+8Pk zS;je?97wS8ahq;2IsQ|GT!&Cy#ErI%O4@UY7du#ac!}LSbn?EI($=*LW*954SE2Uv z#frNF1!&{gJB!B8V?@@H>9m-#aGKg4*Ut22gm(5kC%wgpM@mtzWzh}()MG7bT`02< zMxxcgOi^=Lr1uMommGBrS>u~aLfX@*X4|}ZpVEzD=;-Nbt>ZX&Tl^0BIE6E|s#>fH z3IYQetef$DRb|9nvSMA{A5@hQ*Wzevxm@WaqB(y7Y`)SRFS}{#^=dcm0WJ1OgK9wG z1d{2jUx>5axnJ5|{83Haj(bDf{u2 ztJ9(edc46u$^b(SN)KPMsHoAsy64G5EALh@-l2HD(b9uR$-3DYzPvIVpNi+zYuBQ# zh8??unOcfVw?qzEK|=HDCJD99u$Vjy6ICOkTGNt>q0NCsyOg1zA?U00uQ;uqSs!RE z`oAT4t39-(%Uxe$N?tNj@!7fGiZqBmp4DobZEvx04hu=eTNSeWGDY?{%F^k!2yNoh`4N${Yb*I!8|v1fG&x~Zwt2Iyro)}$}7#{RhP3a<=<5Y z8sxq&I7z7v4%LcAB@n#0rXVr0_&X9lNkFDvdrpO3*05i^eV!-Jn`^-Ek1Bm2dI-m^ z&_E19yA0IU2Y8%ZB~^a-uw{+0QE{KSp(#Wg-p@;dE>vsrj92S*A zI31%9**WrL3gwkFTMj;o{_m77zh8a!J+Uu@L+6!~6@J(3jUsuIjsMg2_*Pk~c~d7P z^JrTRT|=?qF!@S*m~61|wi)La^i9;+%0aU;egu5~g^&{J83l~CMhBZ~sVx@cI7Wrp zd_2C8<-bHIl>3?OGm$J8*fxK&7%P8=Av#Oo}0Q*i>4((YbGHS0PoT=)Y}cRlpIz zN-63K#KxVswzP|o$MkOISr?TnNK!@qq;t_ayYyJq-}lvfY=+xsdw+p`x29Wg@-6JO z6f(s=nCOtmHW{Z2r(L@!TZ41DFZ1P2fP&cWo{a9b6LVw7~%oi z0n_F9cQoXN6LBurt20vQBAa08;hQ6VkICA99)FE&r7x9<{w?RBxgPQuqr=N5AAM=) z`fomut>;*!7>Rr`YyuHGY3b6a*LaxZy*{fC>=mlOD}U*VE#P!Hwf+%)6-d*~4A#rR zT3V_aB_p8>3L6!^yPsJfkj}N$+r}2DAqySMf>sKFOWDpBNQwh{MyAE@oEf^Na;bzk zPnr1ZcRP=KrL=Wy-N;^F3`x-|?vDz;&alu;;e)Y{i&Jp7>cfRFC88$<1{bnf)47T7 z-%9>(jSy7&kx^T=4#Y+%!uB#E>v)peUPW8@NQz|4c7l1g^I`4!gxsqY-7^434Om&o z$3Lne%$m8{tfUh+ZmRByIhPC=?syzYukBbEyw15n>4QE418GzAG9?#^B)d~OQL)7f zlMy52aHZ-CE=W0}zZK$Z_Pp_`-M$ZF)S~UfGl+=NC#p(N+>d2iWRk5Dls?!$%uav+ zRY~8{dc_tl(XaphVFJj1}a&m=Sh4M^$)N7U;a!Cx+ zVjNT6_^SLhdeqgeii1@=IWCS0vaw#Y@7j;7oP9qAC15}bABcb537^x(H83x{XZ!Ry zXkjr$A^e5Zz6J}~M3viS8as$P%F^jasJM8~IXmZ3AOH@ql!(@d{0DZ_9XBl{08^ge0RzP{Yq{cnFv&~oh+U5 zDx#9kHSdNm9kYz!RDKLDf6>D-@vQgPtEe%g93@f5=olS;aR80{x)Cf!0U?qF}N=o`8LoKHRV;h zVs>boz}ke%$g)luS%fW*SKdxgc8;(dSqgwE)Xk$*&OY1ZIXYrOlhqbl3GHo)O>`6c zCOzwF@RVgNjK zpLqPXW`dEukt^4kGt0Z|oo!h;>Q3v`?Z<>h@pjy>M>MCntU^6C)u7T(>1t~K53p!| z82h}Xh}=hv>1D#f;v%JvWfZ@TMdad_2o{;QZ>ELbW{5fG_ppt;R-K>m2Y{R)x;dkq z4`RFr->_vMQ+MzWxSH+SzS1Hs1(PQGn{{Ez(qUh7_M<$Es`w<>g;(N#!`uMGdB|3wrtqBzunfz zv5#9sNM61Oosg_Pa5()3k%pHnAD+hG{2iu5dl zstrfnI#x^GE=W{1(NaFP>`l(UV!r!M{hWn0WCXgXnAG$%BQx+?OqKyQ#}e0--XzG50sEWPJWaxV`$uyllXRl`a2onB@0m7UKOrbzLnt$*AXRqTOu6op#dK3%b87^@4tHfP&d|rB_!j;&rM6> zYPAu8#UYW^gA=fnEG`wbnDYk!XP%!o-;4;+D3q)OR@iJ&)P)f4=zm~t8`@s_Yf{ej zV9?8-k@2?}UdEl1`|T1?a}pnO2v^U?%I+@orSP)3JdjOTu}MMqi&G_AGCqH*5a**) zl}X;)6ILuGzyO2_bu59R%ZP|~E@B@l>%+AyO z)vx@fUt~o+OlL@QvJY89$ziyyDekFN`YF&Kf*9JXL2DBttqB3PlNXcZ@vH^i#F`5M z<_;>-q=za&1c?L25oQMN8SgLl$6IHUw(-{*H(dqyP(W;-y^+dTSWI~0?}27ShL?f+X8 zd{d>FfwV%g-A`di<>mEHAjRo4$&AyFS*H3Egq?>Eb3PW>jbjke@F4tbY*|$R3P|3} zY$V{9(#AqswYFaQIGwEd-%?U+Lf<;G&VQU3W%EFaaVqM!FKX^&@4|`-g;2&2VLrV>tU1qDQz|l>Byzj)xY!!>t8e~exaU3E2*osLgsk=-Ga_h zd4#Wq#CF~=cawxYvZQBJrs6@^4C>4y+QrY2F}P^7MZ+`MCDktRw|bvqVz2#Gq3(Pd z{Wbe`>pX$~`8?t=JvIe^=sxZu1NO#Ej(U-m+iGfYdnwyDHlx(-aA-e`IH1N<+_|W1`W_vJfurOwB|eLd zbg-WWr@GIbq-DB&ovcs8JTQ{XJ|hz|Z~14jrr<+TPoD>PIyQcnsHBSloA=P2eMS(N zPom(z6zVa{vgVgA`LXip4`!YN)N=meU;g^SHNkJU?LPwJrvAwGmP~EBPLcemj4k$| z4doNMr3Y^1s|DsH!%P547@k=}?m3Byv37d+S5jg@AJfa|5|mu3)8W@aZ9mFfX`$5f zg=Q}v?1|P(rNC-%W1%7UcE?s4rs10jSsYB`c_x6H#_OOLs+hoOHhn zjeRb&B_A!xMKIoUwfU+@7QoegrC1G39E4X|7S@CcUNP*DtVzvomMj& z$vS9<7@7g>+K5mtnQJTK=txZ|bTR5+K_|(R6&M(hqe%TB@cTh0ID3Su#P1tTzce=i zIEuMM|KnI4g6Jbqk$D=)1)CtvD|Z=U)Q#hVj?LhMThMWvl7}-Ew03HU@T=x6UW`?% z%&#vv??U6hhWZ7xxWz6nD-T+g_opx@@#_pL7nBb5XQ9eg22||+6Ybk^UHnSn{_^Aj zN=~wUCV&SsPT#$tlhWi&(*@`9-eGCU!qC|%6LF&?FXor7I0sjPyFA*MbMnnhE0P|m zwmYaI(o_c!I7r@Bn|Z{!VjtAga^_uOqF$zTUx^LBV+aA9eIjFWdV5KzJbfsrO`#YO zLGX?seW^v;MXB`m_3KZ0?Nl7w$m|kt9;SQBSZ1D=w=o_*nbop8J|PHi)yYoc*O(q^ z#^vTi3`zIo^#FsGefHoR8aJW32T;u;0_;}rNq>2)S^8e;o;)%6(*8Dab;O2xi0`;@ zFUmWMC*B55Oxyc_5)()FbtVHZEfU(*_VNNzGMKzEu2hO7_$7j+1}bHM9wLd2>Y}#V zA_N_L{mBs5Cl>rv2^{$`3In|^DDV`$Z29A#o&8TLBYBumY_0!iP(5!!f-vdPb@A0$ zQo*`!cTRXX_|>3ECttuN9K>jIZtV_E8nd%gTa4(J-Xn><%23t2*E?vO%zc~8EJd7Q zx&c8TA58%@R1ankb@2reRzFQAW=b%s$pN>2$7bTGcBb)MR0sSRO)mM=%NWf!FT?bf zc~38=ftd|L)`obhqcpvpbuBj>bpY(4K0x{7f`870Xe-^rx^IpT@I;u%b_LGA}R!5ZQRf~w3oyA@8+2Rn@HEh>|%opD6E;r zp17>Z+I$kYZb`h|gL zdg`vjQE5dRJ2pCL4XRm1l0A(OxN(yM8Bo&%eJ{6Fp&HJrA5&?I29w@be|xA(dVH@s zF{&)~mG#-&o1;t@)php^T;2(M6YQ4gN0GDi-*i8hA7}}(djkH%H3No%44h%=!$W`p z&hSj2c;~e8VgL{C=j{(Uaw?oE80bF|+&@uQE{9#Jb#y~8G7kYUAh##&NC~>6P|^B zd7^@_JX0Cl*ti;Xwy07U&88Uk{=xvfdZDlHwJ8KED8|s;qhN%BEf32-BFr8czEx3q zFnnPQyDNTPsc%9cBdo@3RjU#_semj%6u(p%;}b4rQ)Tso8kvle-QA&9S6>09GE+^L zLHqr`Uy10xlh4B$?7>4D1IYvs?dzwKS(!P~d&nfjJtg*`Xp4n| zFzl5fLhllre!B}dYcJ|fjEcyumr%ZhRH@{}XG@MNF6VgZij%KIg+a1ZE@>}w+K+Z! z8lS_<_s;%wr53XCCc&_fh+7sD8h1`3AhY~;A<_)l)lfcE zoo`C)rNydulHb7hnv|XW6wzyDzL8~9F#>>~ISK#6O1fXqB;Z_f^;=yBW=on+g>FL7 z<<9e!`7P(v+c-&>{_CZ`&)MU2G@x1OqVGN$EFGavI<)KP*Y{cWd<{a5@R2`%hbLeO zlI;b+al(#=zo8GnUF;@YmEMEauQU^vXMD$@w$hX|788SQ7nn zvQte?tBDD>kYhmGAs6+DgbtnZ`HSOqb`jX%9{g~VhGUXA)8DAIL5K zTl<5xT<2*VuGtbjm2?yKw2w?cir<+Ly(AISyYa{>Em5F2buM|$wW9lmTkiYRW>#2i zIyFmi=mBODEp1WYU2oZMyYocmJE>=nYeDFOE@^0qazGWBy?xmcUxtm=O0qrk2Je52 z(?K-ou;%6~o}hqY$0Xd~80B7-fR|`sTgO~dU7hV6LE%F?<~9uR+18}XQw;HzAJt-S zHZvE&@P#v}P!g|4y}2HJhJ_F5+ce;>n%>k}J&z_3!C5&Ha!T9ko3;vU07-q$*#MsF zL;ri_5<#llMNt1Sq0HstG7w;sB>(5(`Q8+rw#*Q~MW5R1X6eFp<-B0~e}<8Rq@3Xf z%W=x$d|PS1Iz%;(Ls(xFS!k(kkERF((VGNE zNMoK#r%KL8kROV_1@au7qOMj`I1G!l?Zfr{;Nr%EJD!AY5W$;n;@kIEC&!bH*nALj zL5O66J_U6s{v^*3I;JthXPnmiF+jU8{y;fHk8UQyAS9~GTvQP<7+a)7ScxD!W&-)V3hkKSBI5?BO70!52mhF)ed!d0;RQ^jHic zXb*cGX+s2*)K1aBVkzst7aXMHPpy%?ie{zN=2+X4iaWkEj3Y}qravI`j`fb3{UGhU zx}e82LO8orFkwIp-4|=TE-t#~M6(Ig@9jA9WLK+3k?d6oiYum2JM70p5`KMyvz+P8 zK@y?fMdLSFl9-;-Grj}@T(hT}wL(Ep!lU>27qWm`%w`TWk)U__k)_jP-yKugQf z1@d1DsU91dv1ya>ntsB&(N0x2gDpBOW?>qoFoDh&^soj5In7z!cU%m~-TH$bbp|C| z`Y^l{tEiLUT@3q?6V~BapROlYbr0TO^5^vBTo(NfY-6L?f_G_XP|A<0P&FLV1QbE& zWX?D{|pUUGstNyHVhp9q>4QF-mxb{#skWHa3*Z_7=c*gp zOr>d4w_+;b8W;MW()g{D8a_0XIzSw<_AG>i{4bebqd(k%R|EVqIz4*o^k6Ag;m7?5 z%^iQVyLMA3RkO{uu(ob@pNruR+ZzL7 zlI0!=TaEB<$ecwPX>+#sCCj;K&GdAnM5P_L>(&isKd=N#E2*(xeLrqK7xsDh?D31{ z*F8NsQ|=|$2M_Q7p<&ih7A_TCp4h0$16O`W@MX( z{OMeFELHhMCvz251i$=kY8z;uEt+_vlnX1r?c&|ONpsob)oj`?L4FW0_nN10f@xIDxbA{LiLB{;Uu*=}QZMz|b zXp=Zl)JoZICSfgN-8t$vO&>4kXqwX; z%+2|(jWS4okMM-Xpm}~#?;neMH5Zlx@CU8)u$h)QjudS81bZU;39_`mFI@MF0`}&? z?FwkJ>j#GmodWX<@%*w0dtpW}J=Q+hfWgwzgIir-+GjEArGC<1!2+`)<2XnJBDWoc zfUhTl1QUZDD}4@dUMjJNr;!V;@F(}Tpx8@1^m6!QmMR3p|5gflepX;#$#$qIaHYK~>1Hf|p}oR?8>tt7^*Wv=F@asBtZ=+MmP z*8vSiMnbYl@fs781ASqmJEKa8F2YO2@jbW1NW|DYRjI=mS0pU>xdp$QVLRu{TvEF! z4<+$X(}m@DU?zn2;osFLZ%Yt@0KPNMXSx6$hC@9(0CO%&f@VdRG~B1L+o4;4fjJJj zD>_{j{A1;JT36w)I?LCASpTgYG+blrpdlRTNjD)$xG4$z7pD~J4@O3mYF;Y76YEJV z-4(^EL@*V?biRwH?*Dm>=&%$mHi5$1(YsG@M$wHZ9R9u>NZI=9EmXorDh-d!vmsh4 zCQ*-wV68>`vGI^aX;*P0etP-&v$8DCj#*QA3>H<_=+&9ZQ(HditmPFty^cHU=*a9@ z#7%KA*!yR}VXZzmxDA=7~J!Y`H4k>5qkHbDTsuEujh1Z_GWI{4bg86%(a z538(0fJAVMc3N^orjVrXqQH-CJ0qt+N!(H!Kt91Zd~Aud8VvubvqiBUnKc=(F1g)Y z$&!gOy1Tl5Y__ywk8IHOd&5Rn5{a^2KrcUus zU@Oda1lMaIaaiWd$+m)(RCzt6WjYdR?^s7)TNw3Kt4)_X2|m!VF{7v5xp>VE7UXfh zG6rwUww%)<8hy@$3^ODz>5bB{2UqHvfVW2|78_mt9GMLM5X-(v%RIb5Efpghj@=Qfk@veu3Pdc{4>e%%alqnu|V@5GE89tv#;p{YswGA-l<>6Y&6T`KkXOfkNlvh zbk?qLqf&Q3@v)V%y_rBf)k^uJ3Hh+CHk+WUL4ql5Y z|8ovS&!s7RUC-Ks$LlM4EgAY3s^l!HiV7fmcQt!7XG_IrI0+y9j-+^PlE+FZ4N`kU z4I}p#hQ4)~IzDx62sil6fAZGG-m(17WjkdnVwz1>$6C2wMiA1*V-eV#Euoog`wP!0 zg+)Y-v42njC~rB!LDJWsj;T=L}!47!2-sAJ1fVc;0gwub%9a0L8sdx$;j z@o=jBv$M29Mi#T{vD5Pg7`>I8k~3}D!ws%@Awl_7f9%0i^yP)K+z|avcX{5QrWObj zoR}m9G)8CJpknpGrI*!t zQhKMdcrk~X;AsgS8U40c-FOqL{xio^l!=J<$)a8=m{B08pZPO1aMg9OZ1MYf||S5qJLBu z7deGP6THH-#3Sm1@eTE^bcB>=$p47e*+f`d{(%2oRLwZK*W>%Ofk6VMM!vJ93TV!u zfy;ddKkS$Oxz53Q(4b7EeF=P!tSMxlju~yI>Xi`3b*+Pnv+)*RS!i zULWL42pg+;X@^J5T3i$lIouA~K{k*^Uafufw9C9U8bp9Vz$^kH=ku4O&J3|*lfvwe3=g>O)@<1^-=Y1_*#IiNgKj`-<$HS z*IYoW6lrky#rsMv5z<>_ZnuN}_siP?T5^AAB1FIA7&3;qQMQM+AYWmGOe;)_kh;|m z(7*mg%5*14VQ4}}OhXF@-k|2MvGeW{-cjjoEfp2-jqOhfHBq&xM-QIlGUZb(dwbeh zHJ|-A6Ug*!Y^gN@d7b?<*xc*x=rT+{f=Hs6*);e`M?zs^``l53ew(G#6^?-`5xh;#1@*m^K64V2SA-M_Nbb2qDymTlkxEI7v!xz;#4i@ z+($u32}&yP<0V(N@Z!d9{R$3&Z(D5!Ui->mPX76?r@BsyFcMk9z9IX!zR(3F%n!*r zzon(X*Cqy!EPKnMN)zT!JM+u|c6!+=YE28zdcX9quqg45)zdHg`7~eGMawawpOFcE zj5z}Mfvq#IpLJ%AvK@qMdPLQ?`$SR6v*&+)HK)obBXw^OjZ7Zd49DLdl>3_H?I~s5f6f z2Abk1zky#j5w5!nW#PkjNCY7jG<>rONsVeF7XH2t#*}frvCFcmx(;h_?J3#m3E63q z^=ICc_RwK7OKn?QP@)DefQC}9E|K>}uhJH2{%2GrnFs||TV!#HO0d$gT3sOJDw7XY zTacbbFebQY7ju`q*QvxH*xvYWUzHm*rPdQwfwUBOC5Vi#CU~7ciq?Y3cL<5RT8}v$ zq`H(LuU~bq#2G{x0w-cU`Z00)xdNjGVKjnIfypK*VjPX7BYZ%D>V8x*IZDRh6JCo} zw8|%pl()ZJU~Ta1wvX%UG4hMOAVz5`EUDqm`vsECnR>1D?-czL76w^}^jzBK<+rVN z2koYx*YoTV9V|a1_sn~&i+%>2le3xej`l>w|39&&dn{@$cstdUNmoVwARNmT< ztQSD=PCjX(PGcA$E+x|Wu}A_W-)*?E*~Qzzqs>8|^+^6PC$H)I*`z!8+axQD zyp4`B@Cc-Q&zt-N_bsitQvG?PokUD}x|996H4yD%@XY6fAxI* z92x2rp67T3Uu+<0v}3}hAkiVxBp_h%x@m6Jt{?xW9Qze_vdb_Fz8_K;NJu zx!G@-EFXEMR#g%3P-Yp0QRa><2=R2e%mPpY^Lw(oP>qoGcLjl1xL;MdHCjh$GGAs6 zGYzjnas+|Nl9RD*M=AHHL)s*3{`_~e*Rf^RH@tvD#5JswQ&|x!3NJ39H?T$BWwG13 z!cdQM*4bhZPMA4jB)wwOQ#l!AHD>NTV7TF$Cm$Yh$2R2FpJyTHhD~i;z(VU z%gNJBRbU`jwJORq@n{&xOZ79Q@0D1qQEN^&{n)m-OmYmlzVE{PoAIIm3%1x~=_INW znM2hIv@-R4LW7GF8659{Exg-zihdg8eZXA@0cPkc@t)qQs|!JLnj??6;4ldxFi<-f zSA6+UaIr(~!%2(HX8xq4eLA${^`@W-RO$dTl`7b2xLR<WX zqvY|@ccyPu7{-h}W~TO@(DvzLOX^XAb9C@>fk{ z&riNQa%TOCQ;pBi9dSe|B|>>T(fWtnaAeP=gtP7)@}5TMAKaxmNtBgsIuk&S_^C( zCJ!uq0hrbdy$g`WOC&o5RJ++|Y3}8cV(j_3xiBd~9k;fcQhr>4;Rd$$>x7`=y^zFj zy>@%47ujTe>S$$}dWG>rF5gJSd+0FDKAUkh=vd0cay3BZ1~}|hJT$SLl;U{cDk|!o z{vVQmT4Ohc_sFaATabykVZj~N#twWvv7l$fOAnkXo!Pg?GS{rUL~gfUC+uazsGf#Q z)ggl5Gw3+uYs-%D*P$V{&}%f%tA#!%L2wTwVD25Nt#VKA_(u8dMuw^}7U_3lUT}3` zK=f=*W37xZi07_9{)WVRH$_Izm?#RzqMUNF$K^=Th}mx4FR*nauTrp$#TrlZtCkx?zS^cG|4|&Ddc%Hgu$U-@!W!noM*Wiz8qPEc(~}UOH`Q#yswLXHxR~b!}UdkMGp;5S?yo z#*Z&NVCCLG=khBs6=S7{>oN8a)2gZpBtvMqwJubot6ZU27s`rLqtMLbPnHrAUEHwS z#qbUjBZhz%LclXO=AW2grTUZ{cHYPzY2gB2{LZl_)eyq>B4B=Ctmk(b&AaIEgr$VS zhK-o*q~|VaDQ%swDV!_>bm6nwr%u znjLHw{a(@}(cga6a;CEa@^$Kuj^rafxgguij3gqYi~z)|lML-T`?Z0AxudR^V1kP@ zRjhS!G}&>$vQ;{jm~c*73kB|J^fxd4XX8cXM9Y24b7o(P7 zoPL~5bjbe8`)Z-g`W)M@5O#l7moQ{I!SHRyS~8$E;Ml^he+3)*b>}3`vgz>2RV%6{ z@}KIEH_|ZA z<$v#e-_LKq@0W*97tF<4=bWq0bsooYmwEvs-d5&CG7cgtq_^|uBy`Q|s-YXgcwc?g zZ9QBts$y}C9y2vzk_^>e*ub$q>hG{n6v`qJ-OP2&iMt85uW8~ApNFK;%8ugIEhO=& zuEY&8Q=fiNZzb&<2ul5FqRgY3BlAl5Xtip8qEEtN-d_BHuFw|K7wYv@^@#z3{}dtI zha*np>h3G;gy>)Ce##X0(F%aj+x_K~@f+=a4v#T&uYZ>vw%WHPlf1jCSE*t?Peu}v z%m-oTIQBZnFRTW$Z8;){U+ym&fG)&)`;60$VbjX?fF6T4b zVzupnzw>y*cAWJNiTT8pN3$`mWIj>yn>$h>>DlFyK9$Wcxo+3G@~){DEF!dS+{<2bKv1;LkN-9F0xRq3 zOjM-zE9TO<%~lc$wN%S2uLC&QLWl9Q)??j3s@KO;?QX-6$sd@oX0`r^+)%3WTq21k z^q>Id^wAkaZnvyzBB4-?H-YJ29n&(p+u@To=PYz@b8TDK%%QM7%Faxe!UJ2%ezMK6 z+S_k-i^p(G%^IH@)s3{&9Ugy_CgS;szGfbm=Rn9L zw*cS#l8$RO2Rs|oxhn=-jILdb>7P;{tM#YDL!=a_1c6Btg7fr6n7P;|6bka|Q%xOyZ6_QFyh z;Sanonjn0vDAzc+1yTfJ9U7AEvl8%F3CUWAGhUl2YiYTS_kW~fl@611aohH9VrTBX z*O%a>x%bCUqwBlh{@}xm7*DPbS@5Wu<0L}IBU|nIU7%AHK={7bF@7tGxwyWH`rrq7 z?kQZ8_EGE#F6I6wW%|_uQn6(O?~%G*PR>n@kNqtl!H-g90#j53%NE!37v7++;Dz$e z-NnTojZ<3ipX@N~7vCQUJEkpfacJ(I{Z`fg2=lqqY(j<3^oAe^wxf*Wk>z@s6Uf6+ zbk0`HYD_{BYlAfqGM_-1CU_$1ZL5xOZyq;W5O@#eqdt?OBew>v?R=_SF9%lxC+l%r z1uQnE>t}%5{YZV^?4JEI8aCizpl^oo&u%S3QVmazbp>Ynvs6h^p-;??ag37wZ z`e|c?0#+pG z=mW)qU<|TN&253tY4?D&(WMfyT=(kRdFgU&N>h}^b?+uQ723|J7av2N3p~v|uwfa^ zu2(INQj5H~T@c#dE`Tq#i81XD7R-o?HOD?+zFMC9&W!x;tR{?Tmb=uRy}O_qb#IGD z*G}<+=|BdEgl@HMmo9!W&VTc#vnxS03hN#_e>N%no3q@`h#R%9&X4GW#B|w4qV@PM zxvE-@s6j`^36{G~fUey$W9?(|@A5>?QvMpMsj>fnuQC)JhuYhT%h$gFLB$ zm1#bPtjiViqjpEma@caN*UQ+bWIG&Hl+?CYY`&nbSIDIK$S_a(Oj)OK&ubE%PE0dx zFSE;hN&*<*y86wa&URWcAn92#8HSif3nsBP^8UQ@hAY?C6|CjY698695t=Rmgi<$J`&M0Vtm9 zfDq|nUWYgOMj3&<+~;r+)fW+$IKSg9x9Ci4dtW3u>3pQ_co9ZQjtT=ygo=5a9V`Zj zy;KW8Ex&Wxrw15Z(da%na8B$gsnV#i$Bm*PwIjlg>+ z`pUBfbiB76FaghkY>nuz8$m^h_Wurk-d6Ie|%5OL<70}QtE7Gb?bg1 z>4#s)k&@BbZhGE_yrPr_a6P%M@-X>5v`t`178^$hO7b3S6*R(Gx7_gR23FW=d$7)a znzo5Ek8=pROgy%=j`w6AY3VHrLeL~V1Ly=l3o zBI~p}G>3`ni{uH7%s)3AWNAmU4Vr*kzIb4!YFqIowiGJodBHrP8!lhX+2coW04=A? z`~5}n5U7I(?;%C0$VV0dmDzyPpy$SPT(#W4;Au;1|23VRgHs85rbZN&(muAv$DW<2&! zaK55-%+T97R7=T_!de^i6uuMlu8WfAO>)&vJ!tlv)&hiXk$XPaqC!^}SmQ(CTDx6p zqkCW)OYG;{->V}}Pi@P!Jnq@4J;))>oq!~)yP)65x#;pQvr{}3YP-)(oTJk; zur^!r*?xmi8!rZ-d&EVc+VLVlQ6-U0uL z&;ne!uqf*+GJ!c&_&4crwaVvHqL_{RliS03=t*Y?t=%`mIcdVWWVD46+gt>c3<5NVKiy)as%xjE_vfwB@tLNa<@eY`^f2=UgUsaQ z?!M4qm^}`0pFSk1zIqYSvJyWm-b51@NqScDT5oiF|LW2;34)LH$^wPBvjnDPN6_+s zHs&d+o0af%L2yJENq~^kO4|RlX52S$Rv2uO=Jk`UjejpqBs5Pf(fCa{z=-H`nQr>B z|9Sl#HFIt!GJv3G4hFG7aT?6R)M8`MhpI|g%6l-S&Kh9HN4p}K-bb7Q(a1tB+e0`0 zZuY}bOU)Wg4_RZ&~rrzV`a4k4Fx%(0i`DV)`t&$m#HgJL9hOdYBV$vpvDWyk~RY zW*J77k_}96K_)!Y0wy9t0M=D`ICo#+c5HQh1b0LjYFA4+d&1)AXcQ1CrY-xbl%JI_~6hPLk= zns*zSd%wP%6zp}gkQvIeh4@!`&G#iu4DXM-hRyd2OA?hQu)3`nLbTwRz+uAd?f0SO zAZk3LQB)W^nbjsj!M>?Bv92gLmZ13>VO=A6X3>?!DW0wNmj5wwHEWiZUjAwABdbTw z_Gn&?FP55-*6=0I9YKFOgPH+Bn!WrRgK)pR#d>8x1_ zCe_{Sj=M8#W$-ebiBJ>Z2*UN`mvMLNs+&HVAvP{OQ5=l(H(!jMw9m)cAXyD|=c>wM;Cp}piz@;hG3 z9wgHo<@#LrHhGVQYdemr2}#DHkpZE=8Ms@^&|@6QkM0~0*6-6RI1#_hiS21dlSrzS zciqt$ur7%ee1QZ3H^V(z_MZ3x5M;a|xmQU?rm3A6#Nw<&7|?19GF_AKm23fMyf*6f zI}9SVW&~&nHhICFp*HdF+bt$EZkK5PI98I#z=Vi(C1l8_4_4YG#B-NQ<-Q@bT0doc zE^7xEzxtvB3>L6G#&6nj#_)~rI4|{Iha?94ZaR~*J7!!`O7{)V7>Hq3d*pIcwAHR_ z)v<@ESSf#vk63{&y3E{<)qHPOJ-B=!{y<*1V}#)}* zh`^@vi!wmw8QTzh2J~}WN2cFc3$}#Vcyq6RjB(t;I_25Tbyf&ytrI3ZE5m?kXfh(U ztaBlJM>d?R|GxKvwPV3P-}sJ^Ix7E!s=SpzV&@t7TU=#2Rrl&@#1!m6xjGLBC26=m zUJ!URm82O?*=bk;y1*btfeTQ?W>Ad6H(PdJYU>R#g@_!(Vp7;v=QXYueDIMl zP|Al{sQRz2>8tY$M)tM}<^phQzS6Uv&rCG5ha@u4EyUw;{hkQZBB8nwn9v$h?=r&j zaLLjgOG`Yl^^sm^=c;M&d zrpHU~-8fk*(vu`vh6oUAkkMRIaHsv<}QE>w=W(M35) zD~`t~0BQ(FJDYiMAZ@2iO^XpY*t#4uEBRedfoYzIlQx+W%yG7(KPvX$iWk_6ss*xr z!+E)+MAAMxr}gU<7xW`#^$sMcZ;8jdVa^Q^l9M?;S5xI;iH3wBuwLh#_jc$!cWnIr zv}ZPP$nHalRBY6Ie}o}T{i^J=AveM^f*)VuJ@=ey4=A^F{hyn8t&_z+Vy9SfWRt%Q zpw?~a>4}Bh+%LXnolOCgcrw@T3Fel_YBQiIF`IeB2xGl~#aA(shveH@SwXLGUqc&F zjcp6=sp@jg315^eE5F)whR1JN&4+-3Ly8N+#7KF5s_2@(`)-sfs(^(mJI=Xj5ylt!_$ToKL7`tIoqFdvNp?+?f~mrMxnZH5!=`OJ2_Tf_I6# zY|?!Pb>|Xn{cvrJ_e6>fnWoX^yG(9cpwcK+l>X(jptoE<9C&^&EFGh8MCqv*E3Qs= z&v)C=4rWB8S&=+lnysTJLLj!c2v#*0#@JgfO+EXaLcP;by;LlGts^JXF&((5(=1}V$N zEk?j3CH_>NiV^>K?)>sTeaM{^-MKF$_vIjwoyH6NPNn$$qA*sq*Nm>V{iNm1Y1hW8 z57Zz1sX3ST2j`h-h-hqN3l);WLu}XUgj^GNpD*2^S^dUai3-Z-BvSP+xejMbxe=F^N-VUKvvKh!&lNINpQLhT z%nIUhhi0}K4WX7h6H9kXk7(T6?=FRmWPgy+ISastW8%L{Txe{LKj`kww^&xQ_>ui` zrX7)u)8ny7=Gx4|Tka(qq%uVKJ4L{_y>ll;YCfS7UQ6YVIhiet75#iozer0}bJdrz z0Ij1Uq2MEOJ~hp%_MrJ856-GuHlyxJYV!o~Bm_}nP$3nJjWXuVGXr;*Q`-O(h;Ewc z8EUM+O**X=d}ck3IkOVzXNWEStm1>s{>P1B3xIkFa)kjJQL%qS?)SLH(^{$n9@eKj z(qUW>R+h+@*!{(eRGmu$FbOyyAXXSF@IA)iyud5l5RGxIGy_ULAMAVo+<1Y?^4v`k zPmBg2m7eN^efi8oUsbl8XxwWU-+>Up%X@MnK_HbRmRIxR+&wnBc+YDk^`KPI&4z7W zv=RUWA}Q2?Xxvh|&9-`BPt}ynt~h4b8FjN0bAh4g%TF1EBXd0kbh1Y|?FVMP#Shhg zlDC@2qnQ(PLJU~O2Bb9*NN(Aus7Rrox7hsLs4pnM#Qw`9!biAH`6{RhLDXSEwsNO^ zbJ@kv&ZC!RWM88fi@yhtm&T5_&)?twPV(6o-Quf9fx)f|z5sg1gLd6zWAO5yNClFc zYjDjFjR0&gPak$ao#i4M$bsi^wHK7tN``Rfo%eP2Qr(M2?dcO6VTrW!?>S!ZGd@Ts zF=6hlV7ZqP2US2x$&@*{7P}>`!Z$w3pSJ*&0M9dEk3~6*G5iKH)>1rgJ8SU88++s~ zG_@BB_)Re(uj0kpx?s(D`iJ45eu&UHDlfQmCd_iWo zV#Kj3BoaYs8fTPI^&WYucvnEl6aefUak9?va9iZ%e~Hr}BWgw&rs}FTYM1aVc_IGmQ32vu4I8glrK7$o`(Mj*KRol)x9_yzH%9^s5CC}2 zQV=e9K7IWAHGO+^Al~olj*W^k<3dki&ge095V8U~MBiT|<32H?C+DEaPD2I}a!OJ( z->>{WFl0uW{=KnjSxPBCLXf|t>Wb;;A3K8B7+ewYv(NMyeM(-S`=Ah`j3FM~a_t!7 z1(j0UP|S1BrvpxI@p2rXaz> z4gZsoK+1K^9tTI`v`Au(yT^(i+qcw&~@=ZQMel)g=8? z%S?S0D>5{*SA)g5yfa0`lFi&ulh0#2wX3Rm-xqunc_)$-=Dm4^pV&sn!(hSRvGr1p z{|&>SzUvEZk}z-0e(tN`V$SS(LmSzwym>|(*zj)M_2*64{>^vRKsZ;Lhymc5L-4q- zhO51rA6obI0x5|%IVs8Xf{Awd{YT}+4@kmCxpb(pJ7;6B(r7(i#`*3zQT+{V zF2%?QnY#}A!b*Od1rNY{!X0Dba(#U5q2F=0W>^0%UEjKIx}uaL;AFa-W|sm$9x(YQ zzA6CjKK^skj+{IM!9aI&IAMA;vGssR*e?@cudFV>z7b ztlJclxm;F)0|&^BfT4!uuHkwii6F%pv5uY=8w2d=QG)|IN{{4v{Un-Pl=_U|Mhv=+ zizD{?H%@AS?BX7SWMkFS>wnaiVj|nI#xxS!&Brs%5+GlwutwJu^&>(+ zuJ1k}&z);(Wyg2#jR0C{-0mo)(w=!b6HYZfrFGI)i`BTc0WN@J3v&B+Wu`9a#rP)h z%k!rcwbkS60!Cb#ztbZcj18GVQy#O6HQ^E;$%OalEfPJo^@B+|COh6sJcH~+V%cY{b+&u7iqK~^bG3DnQz|a8KT5uP zW#{xA@*K7x_B(kW5zH$>*L1nuV6C^agOrG15nhNG=Jd8r^fyf`$wXbF8_$FcJcPdE zo|O|48PU&;Z-?m`ggSpK-l58=R~?6d6k=m2b0edRxx1=M=KPsN8#c_2xL9X?3~uA! z{fQg-25tnic*H%Bd=j5tZX&e2dfb|bL0T)6^BV~gX7IyVjl8dzX&3%R8Z5uDP}_g1 zTgz1L&@HJbJ?d31JlcD}6~EEx+<1qkEmJFw-!{Rn2zAVf{RMBu**WX<6n=1Y^V3+K zEjW{%$|@OXj6Aw^6tc}VPD5*U`F56PeA3&=S6v#R3^X-~=3C3{|n=|H`vo|RErMYYBjh5(nTA|blj-~ zv$&n2G_IA2D){Zwx}yH8 zp1?e&H~u19Rk5?VK3HMiE+n6IWPG_2FeJnV?G`)Cr)yKz#qn07mw_M`GDHw*;Bg#C zqs-f?3&0n-*YQ`lO;60GB4MA29_k2DFgz1T#&NnMHv+PN#8lczIK5}Nd(etX@lSGgiDNBwP=I&2VLm%_9Dg@N1JZ;pw z5lWgo0J~Hopyy+KFu9k0 z??HYS^jxH{I59)k0V@gCo*h0*+lsuYmUL3dIu+(VMQnNPiHTBmKx8Lo4UD8N%%vyV zxJ(mmEIy0K85rGXFZZj#t{~N}`V`?NXLQad;=(n_uuskMbPa~ z$>|mo>N+k&7x4%ubw@wFvHdXWs&hO>Z||@DcD1`=1XbPu9iML_h+;HqdQG$71usN- zZEh(cwtfHZBI|E>Tv|cDmVtDd|kjiJDssCYkvMKxftpBf)4I(7C^=V4D6QjN8XjW^aoy`VC z>8JVi-rN;dxS`7_zOWCGz`+D zNxo|Q{laHQCv9Og(*)(Gl5GJGBxh{F^;wBV>4+KBMQd%;oeSH5;Z1tTxSn84LDicd zq7e6-Uoe;jw2ZSmW%l-=`Wj9U)=*U#YV1mX{}B3)^wp>axwRxXwk+FoOraXibnn4H zU!4{av{0)8=IaQYg5NYor#h07M_W#ZPoXnpeeJ_Y!j6RF@3Jz=8 zp@n5_^MbF~sR1M$0SLfm@Ax(=#ZWdYu7xC-V73-Wr#C=XAk;MpKAuE0+ee6h=nWyw zyQ9E3RWK!zS^A}CvFMn3Bu?{yOLI>{cU;ddCcwlUTI~6{|Db9{&-}J*lq!MJe~h?J z;V%mzE-(oNX}>fKuVyeEOM;EuiyP?PUSPUoH=YD2sOsM*8g7{(a7Bne7&zrD)B7*6 zRx4+GmUAk00Cc19sH}|wIr6hdoJIXHyxT`Vm7TCuZKvtW7RbeMi^s~z{eY<(+|8Zm z7|DOtSSa|x(yZ3Kfnt*&ymaE!&+aM_yNky;8Jpr|{%3OB7YpsAYrv^usKyWAg5$`S zQskZ3T%b*S2qcKD0v#DpZ8gufpYyF#U`PSD|?QS840J;*&) zIJxuu#3-ZUzN

  • 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 Q5P%;=IaS#TX|vG(1MkGfH~;_u literal 127176 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,o z@E!mDeW%&1T_tI$;n$k@dx(GeS}b)rp2VmrrPL+c5x^WV6)ajf1( zI)&f9ex$VM2*EpEgF4Ls~H>WlBsIV>UK|S-37^6e?tGO9$bk({tadf7EFI7<09w-f5t4#p<^HX zmJ1I72M<%%Z6kXfo2y#=d9|b!r$mngnvcqyyPDzsHk(&LukYq;r%+n?HzP(=ur5@Zaw8Z8=wPR zR#*#h5B|Z4m_>yXBOE9*W%20efrJO|!K4x!Eme+}K-|JhJxEN{d}~$Pw>g!etJz(> zG0`PGCAIg;4a=TwjS2Vr=F}J)dRS*)E*4KpGEhr0qfwit24Wo&@kxYS3y?P1dU#u6 zIoVDY9D+=-o&-QA?cRl{YUSC|J1l~tDG1WV4~LkH=0<$Rr%2M%y)7Cygy^)0LH^ed zuvfAKZPz!uL{1YsP%fvvv2Q>I3A5Jka%GSyc?vY@Q)h(Dvudq{0^m=tNy|*eXVw>l z@!D9qr4$&o7E4{?FI^>qXflU?%BOG-n6d}{-Xc6~VW_hd4bHIYg3<$GzC2I$Kr)=F z(db0LWB_9T?2Wi@l}>(RW@f4`PNod{2pd1F<8U0fIu{-Qw0>pi#PG*x{|f;`y=Pa! zTw+Ucl6ds>$s(4FNh%hugi2j;SONf4_I&9a%>m&paTU-t0sojK{|HT|Q)YfOJG;}? zi5Gk?j=FDH_fdY;9!%se&Ba6r-sHx5aOk{=OKeA|$PuOiASZ6;{ML>795B0In z%^3Hh&R38`|8g+{ivCPFdC;^U)3!KY)ZZCpvi{mGXm5e>@&hOIIiggvkd`O}wyt|O z$AzsDOJPW`6^mcA4fKU}6ek&fhkRO80_&KSS z(Zi&*Va9z64RbZ4^@l&87;n!plCpB~goCSuOq#P_onCa2Nz$o3HW!~L{!~nCr89pc z+rjoooBA=)A8>@*8cxwpHon{ZYM#I#(_rGbg*KFA!{7jj;$gzV?C(49>=?Y~ci-Sz zKEaxJ=rZb%E#=E)*w}B5|4{bVcYh7t`XF^QD-W)ev-nB?wc~N|Hal_S&(zw0q;F=> z$xP>9r#ER+fLTYI*?_PSP0HhjM<-=4T8Y_}fegSbUV@`QO@XL*%71Sf<{7}K8^6Ze zmdC*&jF3&Ux_-Ls^+5QkL%}*93{jLJYYM`7!ZRmtIFzG%RUY!pE!fWU+#*+`TscAr21!?Gh>_$uC{@sDpWX)?f$`4mpfcA(osY5O#ev&aCz9G&c9rH_RZA3Lb zNVu;#E#flsX;2EpuboNuy*|%_!Oc(c;@)S3H%G6AVxP;EpU@AsP>6ZW9~X@YDqcSr zK6rr`F&63C?HaC)ayY>&@?g5r^_hu4W)|#MFwr~n9*#r(g zb3nQ1WL>VG{5{?fpBNKj{^e}DIpKr}={D|u;5+F?VkY`jHv#!-Y}*C!W)PnleCC2% z^|g1tny1Y_{Solk$QB?rSoUi{&a_FlvG~u()hzwu?2^T_nq*dYn8da9o|{73c@3sb zo<9+Y#F|4_2>>$W@h$F}wtvu&5{{~{+-k%93w}4&fDdox(8XZ@|D(CjqfoKFo?hul zamrtdSiW#GX$HZhX%3Cjt%4n7IR3_Y`+mhh28@~m8VT!Kju5C}Uht5makrY-=XQ%M zwU0C6X-mOejzXC$J0fjWkAPmBLd3rn;8-?S$3a3TQ0C6fhMH$*_s@e0BaZKf=8%J? z5jpxt51IOF;eszn_kDMS2>_lFXm>wxF!{wLt_7;S%;v0JIKA(Fy zUDl|COq7JV5ZVhtq?%?HUF>l8j&qNFv50fo?ZMrO7AEElj*u5K_zuc{<>i1n;I{VN z$3gC=qw9%)qqnTJL88_i6T8t3Z%!4ln+2+0rXQ)Wf<`)UnjWOXqZ3|aWl<~}E)lh@ z#V%YrT90B)U}Av~3yI3#%F3c|U*lIUC<$LJbz@3jWH*Igo`rk9A|~WlR|@2|Cm#lT zE<@;+2OQ|kbhXnr{8d>ja*@_OK(RkRpDI#%
    }yM>XgHr~ImQgC~?`F>Gh7oE%p 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 z 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

    5};$)8hHQlgnx)m3IOv=u%u;#}^7!mm6_O`JX-W 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 diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/special-skin/taiko-slider-fail.png b/osu.Game.Rulesets.Taiko.Tests/Resources/special-skin/taiko-slider-fail.png new file mode 100644 index 0000000000000000000000000000000000000000..78c6ef6e21ef6a4830e57aaa218b73bb333d783c GIT binary patch literal 61156 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 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/special-skin/taiko-slider.png b/osu.Game.Rulesets.Taiko.Tests/Resources/special-skin/taiko-slider.png new file mode 100644 index 0000000000000000000000000000000000000000..b824e4585b4bc678e5a5165b97f647b9ac0d7fa6 GIT binary patch literal 65012 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

    0?Y{WJF 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 literal 0 HcmV?d00001 From afcb45f28b0c650e472fbe9826c88498bc1fad37 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 May 2020 19:48:31 +0900 Subject: [PATCH 0568/1117] Move to playfield --- .../UI/DrawableTaikoRuleset.cs | 19 +++++++++++++++++++ osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 7 ------- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs index a6a00fe242..c0a6c4582c 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using osu.Framework.Allocation; +using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Taiko.Objects; @@ -16,11 +17,15 @@ using osu.Game.Replays; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.UI.Scrolling; +using osu.Game.Skinning; +using osuTK; namespace osu.Game.Rulesets.Taiko.UI { public class DrawableTaikoRuleset : DrawableScrollingRuleset { + private SkinnableDrawable scroller; + protected override ScrollVisualisationMethod VisualisationMethod => ScrollVisualisationMethod.Overlapping; protected override bool UserScrollSpeedAdjustment => false; @@ -36,6 +41,20 @@ namespace osu.Game.Rulesets.Taiko.UI private void load() { new BarLineGenerator(Beatmap).BarLines.ForEach(bar => Playfield.Add(bar.Major ? new DrawableBarLineMajor(bar) : new DrawableBarLine(bar))); + + AddInternal(scroller = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.TaikoScroller), _ => Empty()) + { + RelativeSizeAxes = Axes.X, + Depth = float.MaxValue + }); + } + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + var playfieldScreen = Playfield.ScreenSpaceDrawQuad; + scroller.Height = ToLocalSpace(playfieldScreen.TopLeft + new Vector2(0, playfieldScreen.Height / 20)).Y; } public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new TaikoPlayfieldAdjustmentContainer(); diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index a5edcc1357..5c763cb332 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -50,13 +50,6 @@ namespace osu.Game.Rulesets.Taiko.UI { InternalChildren = new[] { - new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.TaikoScroller), _ => Drawable.Empty()) - { - Origin = Anchor.BottomLeft, - Anchor = Anchor.TopLeft, - RelativeSizeAxes = Axes.X, - Height = 100, - }, new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.PlayfieldBackgroundRight), _ => new PlayfieldBackgroundRight()), rightArea = new Container { From 510df8b282c97c046ee86a309f25681d30ce2d27 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 May 2020 19:49:01 +0900 Subject: [PATCH 0569/1117] Improve tiling logic --- .../Skinning/LegacyTaikoScroller.cs | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs b/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs index 6276eb1e8a..2bcef0223c 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs @@ -21,19 +21,28 @@ namespace osu.Game.Rulesets.Taiko.Skinning { base.Update(); - foreach (var sprite in InternalChildren) + while (true) { - sprite.X -= (float)Time.Elapsed * 0.1f; + float? additiveX = null; - if (sprite.X + sprite.DrawWidth < 0) - sprite.Expire(); - } + foreach (var sprite in InternalChildren) + { + // add the x coordinates and perform re-layout on all sprites as spacing may change with gameplay scale. + sprite.X = additiveX ??= sprite.X - (float)Time.Elapsed * 0.1f; - var last = InternalChildren.LastOrDefault(); + additiveX += sprite.DrawWidth - 1; - if (last == null || last.ScreenSpaceDrawQuad.TopRight.X < ScreenSpaceDrawQuad.TopRight.X) - { - AddInternal(new ScrollerSprite { X = last == null ? 0 : last.X + last.DrawWidth }); + if (sprite.X + sprite.DrawWidth < 0) + sprite.Expire(); + } + + var last = InternalChildren.LastOrDefault(); + + // only break from this loop once we have saturated horizontal space completely. + if (last != null && last.ScreenSpaceDrawQuad.TopRight.X >= ScreenSpaceDrawQuad.TopRight.X) + break; + + AddInternal(new ScrollerSprite()); } } From 6ff31fb7866f030bc6c617dbb58215435955dd67 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 May 2020 19:49:23 +0900 Subject: [PATCH 0570/1117] Fix sizing when gameplay scale is adjusted --- .../Skinning/LegacyTaikoScroller.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs b/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs index 2bcef0223c..f0bdfa4e63 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs @@ -79,7 +79,10 @@ namespace osu.Game.Rulesets.Taiko.Skinning [BackgroundDependencyLoader] private void load(ISkinSource skin) { - AutoSizeAxes = Axes.Both; + AutoSizeAxes = Axes.X; + RelativeSizeAxes = Axes.Y; + + FillMode = FillMode.Fit; InternalChildren = new Drawable[] { @@ -87,6 +90,14 @@ namespace osu.Game.Rulesets.Taiko.Skinning failingSprite = new Sprite { Texture = skin.GetTexture("taiko-slider-fail"), Alpha = 0 }, }; } + + protected override void Update() + { + base.Update(); + + foreach (var c in InternalChildren) + c.Scale = new Vector2(DrawHeight / c.Height); + } } } } From 3033ab80ced82d8d73bab1865d3db897809ca8f5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 May 2020 19:49:30 +0900 Subject: [PATCH 0571/1117] Add passing/failing test --- .../Skinning/TestSceneTaikoScroller.cs | 7 +++++-- .../Skinning/LegacyTaikoScroller.cs | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.cs index e4673430d6..19661bbcbb 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.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 osu.Framework.Graphics; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Testing; +using osu.Game.Rulesets.Taiko.Skinning; using osu.Game.Skinning; namespace osu.Game.Rulesets.Taiko.Tests.Skinning @@ -10,7 +12,8 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning { public TestSceneTaikoScroller() { - AddStep("Load scroller", () => SetContents(() => new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.TaikoScroller), _ => Drawable.Empty()))); + AddStep("Load scroller", () => SetContents(() => new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.TaikoScroller), _ => Empty()))); + AddToggleStep("Toggle passing", passing => this.ChildrenOfType().ForEach(s => s.Passing = !passing)); } } } diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs b/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs index f0bdfa4e63..90fb1934df 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Game.Skinning; +using osuTK; namespace osu.Game.Rulesets.Taiko.Skinning { @@ -17,6 +18,23 @@ namespace osu.Game.Rulesets.Taiko.Skinning RelativeSizeAxes = Axes.Both; } + private bool passing = true; + + public bool Passing + { + get => passing; + set + { + if (value == passing) + return; + + passing = value; + + foreach (var sprite in InternalChildren.OfType()) + sprite.Passing = passing; + } + } + protected override void Update() { base.Update(); From ff1d63060dba823c0ea149f13f9b6b83e795944e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 May 2020 20:05:56 +0900 Subject: [PATCH 0572/1117] Add and consume passing state in GameplayBeatmap --- .../Skinning/TestSceneTaikoScroller.cs | 2 +- .../Skinning/LegacyTaikoScroller.cs | 33 +++++++++++-------- osu.Game/Screens/Play/GameplayBeatmap.cs | 12 +++++++ osu.Game/Screens/Play/Player.cs | 1 + 4 files changed, 33 insertions(+), 15 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.cs index 19661bbcbb..3d1ccadd6e 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning public TestSceneTaikoScroller() { AddStep("Load scroller", () => SetContents(() => new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.TaikoScroller), _ => Empty()))); - AddToggleStep("Toggle passing", passing => this.ChildrenOfType().ForEach(s => s.Passing = !passing)); + AddToggleStep("Toggle passing", passing => this.ChildrenOfType().ForEach(s => s.Passing.Value = !passing)); } } } diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs b/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs index 90fb1934df..f61ee0301d 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs @@ -3,9 +3,11 @@ 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.Screens.Play; using osu.Game.Skinning; using osuTK; @@ -18,23 +20,26 @@ namespace osu.Game.Rulesets.Taiko.Skinning RelativeSizeAxes = Axes.Both; } - private bool passing = true; - - public bool Passing + [BackgroundDependencyLoader(true)] + private void load(GameplayBeatmap gameplayBeatmap) { - get => passing; - set - { - if (value == passing) - return; - - passing = value; - - foreach (var sprite in InternalChildren.OfType()) - sprite.Passing = passing; - } + if (gameplayBeatmap != null) + ((IBindable)Passing).BindTo(gameplayBeatmap.Passing); } + protected override void LoadComplete() + { + base.LoadComplete(); + + Passing.BindValueChanged(passing => + { + foreach (var sprite in InternalChildren.OfType()) + sprite.Passing = passing.NewValue; + }, true); + } + + public Bindable Passing = new BindableBool(true); + protected override void Update() { base.Update(); diff --git a/osu.Game/Screens/Play/GameplayBeatmap.cs b/osu.Game/Screens/Play/GameplayBeatmap.cs index d7f939a883..0afa189e66 100644 --- a/osu.Game/Screens/Play/GameplayBeatmap.cs +++ b/osu.Game/Screens/Play/GameplayBeatmap.cs @@ -2,11 +2,14 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Timing; +using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Screens.Play { @@ -38,5 +41,14 @@ namespace osu.Game.Screens.Play public IEnumerable GetStatistics() => PlayableBeatmap.GetStatistics(); public IBeatmap Clone() => PlayableBeatmap.Clone(); + + public IBindable Passing => passing; + + private readonly BindableBool passing = new BindableBool(true); + + public void OnNewResult(JudgementResult result) + { + passing.Value = result.Type > HitResult.Miss; + } } } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index ece4c6307e..eeb514b4be 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -193,6 +193,7 @@ namespace osu.Game.Screens.Play { HealthProcessor.ApplyResult(r); ScoreProcessor.ApplyResult(r); + gameplayBeatmap.OnNewResult(r); }; DrawableRuleset.OnRevertResult += r => From 811874773288186d4e93620e44024331ebb59615 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 2 May 2020 01:33:33 +0200 Subject: [PATCH 0573/1117] Make PeriodTracker actually immutable --- osu.Game.Tests/NonVisual/PeriodTrackerTest.cs | 54 +++++++------------ osu.Game/Screens/Play/BreakTracker.cs | 8 +-- osu.Game/Utils/PeriodTracker.cs | 35 +++--------- 3 files changed, 29 insertions(+), 68 deletions(-) diff --git a/osu.Game.Tests/NonVisual/PeriodTrackerTest.cs b/osu.Game.Tests/NonVisual/PeriodTrackerTest.cs index f033672576..62c7732b66 100644 --- a/osu.Game.Tests/NonVisual/PeriodTrackerTest.cs +++ b/osu.Game.Tests/NonVisual/PeriodTrackerTest.cs @@ -12,10 +12,9 @@ namespace osu.Game.Tests.NonVisual [TestFixture] public class PeriodTrackerTest { - private static readonly Period[] test_single_period = { new Period(1.0, 2.0) }; + private static readonly Period[] single_period = { new Period(1.0, 2.0) }; - // this is intended to be unordered to test adding periods in unordered way. - private static readonly Period[] test_periods = + private static readonly Period[] unordered_periods = { new Period(-9.1, -8.3), new Period(-3.4, 2.1), @@ -26,43 +25,40 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestCheckValueInsideSinglePeriod() { - var tracker = new PeriodTracker { Periods = test_single_period }; + var tracker = new PeriodTracker(single_period); - var period = test_single_period.Single(); + var period = single_period.Single(); Assert.IsTrue(tracker.IsInAny(period.Start)); - Assert.IsTrue(tracker.IsInAny(getMidTime(period))); + Assert.IsTrue(tracker.IsInAny(getMidpoint(period))); Assert.IsTrue(tracker.IsInAny(period.End)); } [Test] public void TestCheckValuesInsidePeriods() { - var tracker = new PeriodTracker { Periods = test_periods }; + var tracker = new PeriodTracker(unordered_periods); - foreach (var period in test_periods) - Assert.IsTrue(tracker.IsInAny(getMidTime(period))); + foreach (var period in unordered_periods) + Assert.IsTrue(tracker.IsInAny(getMidpoint(period))); } [Test] public void TestCheckValuesInRandomOrder() { - var tracker = new PeriodTracker { Periods = test_periods }; + var tracker = new PeriodTracker(unordered_periods); - foreach (var period in test_periods.OrderBy(_ => RNG.Next())) - Assert.IsTrue(tracker.IsInAny(getMidTime(period))); + foreach (var period in unordered_periods.OrderBy(_ => RNG.Next())) + Assert.IsTrue(tracker.IsInAny(getMidpoint(period))); } [Test] public void TestCheckValuesOutOfPeriods() { - var tracker = new PeriodTracker + var tracker = new PeriodTracker(new[] { - Periods = new[] - { - new Period(1.0, 2.0), - new Period(3.0, 4.0) - } - }; + new Period(1.0, 2.0), + new Period(3.0, 4.0) + }); Assert.IsFalse(tracker.IsInAny(0.9), "Time before first period is being considered inside"); @@ -72,32 +68,18 @@ namespace osu.Game.Tests.NonVisual Assert.IsFalse(tracker.IsInAny(4.1), "Time after last period is being considered inside"); } - [Test] - public void TestNullRemovesExistingPeriods() - { - var tracker = new PeriodTracker { Periods = test_single_period }; - - var period = test_single_period.Single(); - Assert.IsTrue(tracker.IsInAny(getMidTime(period))); - - tracker.Periods = null; - Assert.IsFalse(tracker.IsInAny(getMidTime(period))); - } - [Test] public void TestReversedPeriodHandling() { - var tracker = new PeriodTracker(); - Assert.Throws(() => { - tracker.Periods = new[] + _ = new PeriodTracker(new[] { new Period(2.0, 1.0) - }; + }); }); } - private double getMidTime(Period period) => period.Start + (period.End - period.Start) / 2; + private double getMidpoint(Period period) => period.Start + (period.End - period.Start) / 2; } } diff --git a/osu.Game/Screens/Play/BreakTracker.cs b/osu.Game/Screens/Play/BreakTracker.cs index 79da548336..51e21656e1 100644 --- a/osu.Game/Screens/Play/BreakTracker.cs +++ b/osu.Game/Screens/Play/BreakTracker.cs @@ -16,7 +16,7 @@ namespace osu.Game.Screens.Play private readonly ScoreProcessor scoreProcessor; private readonly double gameplayStartTime; - private readonly PeriodTracker tracker = new PeriodTracker(); + private PeriodTracker breaks; ///

    /// Whether the gameplay is currently in a break. @@ -31,8 +31,8 @@ namespace osu.Game.Screens.Play { isBreakTime.Value = false; - tracker.Periods = value?.Where(b => b.HasEffect) - .Select(b => new Period(b.StartTime, b.EndTime - BreakOverlay.BREAK_FADE_DURATION)); + breaks = new PeriodTracker(value.Where(b => b.HasEffect) + .Select(b => new Period(b.StartTime, b.EndTime - BreakOverlay.BREAK_FADE_DURATION))); } } @@ -48,7 +48,7 @@ namespace osu.Game.Screens.Play var time = Clock.CurrentTime; - isBreakTime.Value = tracker.IsInAny(time) + isBreakTime.Value = breaks?.IsInAny(time) == true || time < gameplayStartTime || scoreProcessor?.HasCompleted.Value == true; } diff --git a/osu.Game/Utils/PeriodTracker.cs b/osu.Game/Utils/PeriodTracker.cs index 49b372bb27..ba77702247 100644 --- a/osu.Game/Utils/PeriodTracker.cs +++ b/osu.Game/Utils/PeriodTracker.cs @@ -8,41 +8,22 @@ using System.Linq; namespace osu.Game.Utils { /// - /// Represents a tracking component used for whether a - /// specific time falls into any of the provided periods. + /// Represents a tracking component used for whether a specific time instant falls into any of the provided periods. /// public class PeriodTracker { - private readonly List periods = new List(); + private readonly List periods; private int nearestIndex; - /// - /// The list of periods to add to the tracker for using the required check methods. - /// - public IEnumerable Periods + public PeriodTracker(IEnumerable periods) { - set - { - var sortedValue = value?.ToList(); - sortedValue?.Sort(); - - if (sortedValue != null && periods.SequenceEqual(sortedValue)) - return; - - periods.Clear(); - nearestIndex = 0; - - if (value?.Any() != true) - return; - - periods.AddRange(sortedValue); - } + this.periods = periods.OrderBy(period => period.Start).ToList(); } /// /// Whether the provided time is in any of the added periods. /// - /// The time value to check for. + /// The time value to check. public bool IsInAny(double time) { if (periods.Count == 0) @@ -64,7 +45,7 @@ namespace osu.Game.Utils } } - public readonly struct Period : IComparable + public readonly struct Period { /// /// The start time of this period. @@ -79,12 +60,10 @@ namespace osu.Game.Utils public Period(double start, double end) { if (start >= end) - throw new ArgumentException($"Invalid period provided, {nameof(start)} must be less than {nameof(end)}", nameof(start)); + throw new ArgumentException($"Invalid period provided, {nameof(start)} must be less than {nameof(end)}"); Start = start; End = end; } - - public int CompareTo(Period other) => Start.CompareTo(other.Start); } } From deb87517d01019bca4b9a121118d52172600e74f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 2 May 2020 14:35:12 +0900 Subject: [PATCH 0574/1117] Add local beatmap lookup cache --- osu.Game/Beatmaps/BeatmapManager.cs | 68 +------ .../Beatmaps/BeatmapManager_UpdateQueue.cs | 180 ++++++++++++++++++ osu.Game/Database/ArchiveModelManager.cs | 2 +- osu.Game/osu.Game.csproj | 1 + 4 files changed, 183 insertions(+), 68 deletions(-) create mode 100644 osu.Game/Beatmaps/BeatmapManager_UpdateQueue.cs diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index b8dfac0342..22451382a3 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -17,7 +17,6 @@ using osu.Framework.Graphics.Textures; using osu.Framework.Lists; using osu.Framework.Logging; using osu.Framework.Platform; -using osu.Framework.Threading; using osu.Game.Beatmaps.Formats; using osu.Game.Database; using osu.Game.IO; @@ -78,7 +77,7 @@ namespace osu.Game.Beatmaps beatmaps.BeatmapHidden += b => BeatmapHidden?.Invoke(b); beatmaps.BeatmapRestored += b => BeatmapRestored?.Invoke(b); - updateQueue = new BeatmapUpdateQueue(api); + updateQueue = new BeatmapUpdateQueue(api, storage); exportStorage = storage.GetStorageForDirectory("exports"); } @@ -446,71 +445,6 @@ namespace osu.Game.Beatmaps protected override Texture GetBackground() => null; protected override Track GetTrack() => null; } - - private class BeatmapUpdateQueue - { - private readonly IAPIProvider api; - - private const int update_queue_request_concurrency = 4; - - private readonly ThreadedTaskScheduler updateScheduler = new ThreadedTaskScheduler(update_queue_request_concurrency, nameof(BeatmapUpdateQueue)); - - public BeatmapUpdateQueue(IAPIProvider api) - { - this.api = api; - } - - public Task UpdateAsync(BeatmapSetInfo beatmapSet, CancellationToken cancellationToken) - { - if (api?.State != APIState.Online) - return Task.CompletedTask; - - LogForModel(beatmapSet, "Performing online lookups..."); - return Task.WhenAll(beatmapSet.Beatmaps.Select(b => UpdateAsync(beatmapSet, b, cancellationToken)).ToArray()); - } - - // todo: expose this when we need to do individual difficulty lookups. - protected Task UpdateAsync(BeatmapSetInfo beatmapSet, BeatmapInfo beatmap, CancellationToken cancellationToken) - => Task.Factory.StartNew(() => update(beatmapSet, beatmap), cancellationToken, TaskCreationOptions.HideScheduler, updateScheduler); - - private void update(BeatmapSetInfo set, BeatmapInfo beatmap) - { - if (api?.State != APIState.Online) - return; - - var req = new GetBeatmapRequest(beatmap); - - req.Failure += fail; - - try - { - // intentionally blocking to limit web request concurrency - api.Perform(req); - - var res = req.Result; - - if (res != null) - { - beatmap.Status = res.Status; - beatmap.BeatmapSet.Status = res.BeatmapSet.Status; - beatmap.BeatmapSet.OnlineBeatmapSetID = res.OnlineBeatmapSetID; - beatmap.OnlineBeatmapID = res.OnlineBeatmapID; - - LogForModel(set, $"Online retrieval mapped {beatmap} to {res.OnlineBeatmapSetID} / {res.OnlineBeatmapID}."); - } - } - catch (Exception e) - { - fail(e); - } - - void fail(Exception e) - { - beatmap.OnlineBeatmapID = null; - LogForModel(set, $"Online retrieval failed for {beatmap} ({e.Message})"); - } - } - } } /// diff --git a/osu.Game/Beatmaps/BeatmapManager_UpdateQueue.cs b/osu.Game/Beatmaps/BeatmapManager_UpdateQueue.cs new file mode 100644 index 0000000000..aa8be823f7 --- /dev/null +++ b/osu.Game/Beatmaps/BeatmapManager_UpdateQueue.cs @@ -0,0 +1,180 @@ +// 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.CodeAnalysis; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Dapper; +using Microsoft.Data.Sqlite; +using osu.Framework.IO.Network; +using osu.Framework.Logging; +using osu.Framework.Platform; +using osu.Framework.Threading; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using SharpCompress.Compressors; +using SharpCompress.Compressors.BZip2; + +namespace osu.Game.Beatmaps +{ + public partial class BeatmapManager + { + private class BeatmapUpdateQueue + { + private readonly IAPIProvider api; + private readonly Storage storage; + + private const int update_queue_request_concurrency = 4; + + private readonly ThreadedTaskScheduler updateScheduler = new ThreadedTaskScheduler(update_queue_request_concurrency, nameof(BeatmapUpdateQueue)); + + private FileWebRequest cacheDownloadRequest; + + private const string cache_database_name = "online.db"; + + public BeatmapUpdateQueue(IAPIProvider api, Storage storage) + { + this.api = api; + this.storage = storage; + + if (!storage.Exists(cache_database_name)) + prepareLocalCache(); + } + + public Task UpdateAsync(BeatmapSetInfo beatmapSet, CancellationToken cancellationToken) + { + if (api?.State != APIState.Online) + return Task.CompletedTask; + + LogForModel(beatmapSet, "Performing online lookups..."); + return Task.WhenAll(beatmapSet.Beatmaps.Select(b => UpdateAsync(beatmapSet, b, cancellationToken)).ToArray()); + } + + // todo: expose this when we need to do individual difficulty lookups. + protected Task UpdateAsync(BeatmapSetInfo beatmapSet, BeatmapInfo beatmap, CancellationToken cancellationToken) + => Task.Factory.StartNew(() => update(beatmapSet, beatmap), cancellationToken, TaskCreationOptions.HideScheduler, updateScheduler); + + private void update(BeatmapSetInfo set, BeatmapInfo beatmap) + { + if (cacheDownloadRequest == null && storage.Exists(cache_database_name)) + { + try + { + using (var db = new SqliteConnection(storage.GetDatabaseConnectionString("online"))) + { + var found = db.QueryFirstOrDefault( + "SELECT * FROM osu_beatmaps WHERE checksum = @MD5Hash OR beatmap_id = @OnlineBeatmapID OR filename = @Path", beatmap); + + if (found != null) + { + var status = (BeatmapSetOnlineStatus)found.approved; + + beatmap.Status = status; + beatmap.BeatmapSet.Status = status; + beatmap.BeatmapSet.OnlineBeatmapSetID = found.beatmapset_id; + beatmap.OnlineBeatmapID = found.beatmap_id; + + LogForModel(set, $"Cached local retrieval for {beatmap}."); + return; + } + } + } + catch (Exception ex) + { + LogForModel(set, $"Cached local retrieval for {beatmap} failed with {ex}."); + } + } + + if (api?.State != APIState.Online) + return; + + var req = new GetBeatmapRequest(beatmap); + + req.Failure += fail; + + try + { + // intentionally blocking to limit web request concurrency + api.Perform(req); + + var res = req.Result; + + if (res != null) + { + beatmap.Status = res.Status; + beatmap.BeatmapSet.Status = res.BeatmapSet.Status; + beatmap.BeatmapSet.OnlineBeatmapSetID = res.OnlineBeatmapSetID; + beatmap.OnlineBeatmapID = res.OnlineBeatmapID; + + LogForModel(set, $"Online retrieval mapped {beatmap} to {res.OnlineBeatmapSetID} / {res.OnlineBeatmapID}."); + } + } + catch (Exception e) + { + fail(e); + } + + void fail(Exception e) + { + beatmap.OnlineBeatmapID = null; + LogForModel(set, $"Online retrieval failed for {beatmap} ({e.Message})"); + } + } + + private void prepareLocalCache() + { + string cacheFilePath = storage.GetFullPath(cache_database_name); + string compressedCacheFilePath = $"{cacheFilePath}.bz2"; + + cacheDownloadRequest = new FileWebRequest(compressedCacheFilePath, $"https://assets.ppy.sh/client-resources/{cache_database_name}.bz2"); + + cacheDownloadRequest.Failed += ex => + { + File.Delete(compressedCacheFilePath); + File.Delete(cacheFilePath); + + Logger.Log($"{nameof(BeatmapUpdateQueue)}'s online cache download failed: {ex}", LoggingTarget.Database); + }; + + cacheDownloadRequest.Finished += () => + { + try + { + using (var stream = File.OpenRead(cacheDownloadRequest.Filename)) + using (var outStream = File.OpenWrite(cacheFilePath)) + using (var bz2 = new BZip2Stream(stream, CompressionMode.Decompress, false)) + bz2.CopyTo(outStream); + + // set to null on completion to allow lookups to begin using the new source + cacheDownloadRequest = null; + } + catch (Exception ex) + { + Logger.Log($"{nameof(BeatmapUpdateQueue)}'s online cache extraction failed: {ex}", LoggingTarget.Database); + } + finally + { + File.Delete(compressedCacheFilePath); + File.Delete(cacheFilePath); + } + }; + + cacheDownloadRequest.PerformAsync(); + } + + [Serializable] + [SuppressMessage("ReSharper", "InconsistentNaming")] + private class CachedOnlineBeatmapLookup + { + public int approved { get; set; } + + public int? beatmapset_id { get; set; } + + public int? beatmap_id { get; set; } + } + } + } +} diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 5e237d2ecb..839f9075e5 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -245,7 +245,7 @@ namespace osu.Game.Database /// protected abstract string[] HashableFileTypes { get; } - protected static void LogForModel(TModel model, string message, Exception e = null) + internal static void LogForModel(TModel model, string message, Exception e = null) { string prefix = $"[{(model?.Hash ?? "?????").Substring(0, 5)}]"; diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index acb7fe5fbe..81818360a4 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -18,6 +18,7 @@ + From 917393697cf36c61c3356719bd39fbaac2208947 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 2 May 2020 14:38:46 +0900 Subject: [PATCH 0575/1117] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 336479c40a..8214fa2f2c 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index acb7fe5fbe..eae763c412 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -23,7 +23,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 6662e57dcd..9ff7e3fc02 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -80,7 +80,7 @@ - + From b9b57792514d43a92dc5d2de7b84b248a487ad28 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 3 May 2020 09:31:56 +0900 Subject: [PATCH 0576/1117] Move deletion to catch instead of finally --- osu.Game/Beatmaps/BeatmapManager_UpdateQueue.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapManager_UpdateQueue.cs b/osu.Game/Beatmaps/BeatmapManager_UpdateQueue.cs index aa8be823f7..be4bd0d30d 100644 --- a/osu.Game/Beatmaps/BeatmapManager_UpdateQueue.cs +++ b/osu.Game/Beatmaps/BeatmapManager_UpdateQueue.cs @@ -154,11 +154,11 @@ namespace osu.Game.Beatmaps catch (Exception ex) { Logger.Log($"{nameof(BeatmapUpdateQueue)}'s online cache extraction failed: {ex}", LoggingTarget.Database); + File.Delete(cacheFilePath); } finally { File.Delete(compressedCacheFilePath); - File.Delete(cacheFilePath); } }; From 035b513b68347b6144fa06c9727a2bb404c46b29 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 3 May 2020 09:32:33 +0900 Subject: [PATCH 0577/1117] Use QuerySingle instead of QueryFirst --- osu.Game/Beatmaps/BeatmapManager_UpdateQueue.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapManager_UpdateQueue.cs b/osu.Game/Beatmaps/BeatmapManager_UpdateQueue.cs index be4bd0d30d..84901f9b50 100644 --- a/osu.Game/Beatmaps/BeatmapManager_UpdateQueue.cs +++ b/osu.Game/Beatmaps/BeatmapManager_UpdateQueue.cs @@ -65,7 +65,7 @@ namespace osu.Game.Beatmaps { using (var db = new SqliteConnection(storage.GetDatabaseConnectionString("online"))) { - var found = db.QueryFirstOrDefault( + var found = db.QuerySingleOrDefault( "SELECT * FROM osu_beatmaps WHERE checksum = @MD5Hash OR beatmap_id = @OnlineBeatmapID OR filename = @Path", beatmap); if (found != null) From 6fef4eeb8f9fd1e46e3b8553b03496c201d911b3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 3 May 2020 09:35:48 +0900 Subject: [PATCH 0578/1117] Rename class and extract out lookup method --- osu.Game/Beatmaps/BeatmapManager.cs | 6 +- ...eatmapManager_BeatmapOnlineLookupQueue.cs} | 83 +++++++++++-------- 2 files changed, 51 insertions(+), 38 deletions(-) rename osu.Game/Beatmaps/{BeatmapManager_UpdateQueue.cs => BeatmapManager_BeatmapOnlineLookupQueue.cs} (71%) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 22451382a3..19d1162d23 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -60,7 +60,7 @@ namespace osu.Game.Beatmaps private readonly BeatmapStore beatmaps; private readonly AudioManager audioManager; private readonly GameHost host; - private readonly BeatmapUpdateQueue updateQueue; + private readonly BeatmapOnlineLookupQueue onlineLookupQueue; private readonly Storage exportStorage; public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, AudioManager audioManager, GameHost host = null, @@ -77,7 +77,7 @@ namespace osu.Game.Beatmaps beatmaps.BeatmapHidden += b => BeatmapHidden?.Invoke(b); beatmaps.BeatmapRestored += b => BeatmapRestored?.Invoke(b); - updateQueue = new BeatmapUpdateQueue(api, storage); + onlineLookupQueue = new BeatmapOnlineLookupQueue(api, storage); exportStorage = storage.GetStorageForDirectory("exports"); } @@ -104,7 +104,7 @@ namespace osu.Game.Beatmaps bool hadOnlineBeatmapIDs = beatmapSet.Beatmaps.Any(b => b.OnlineBeatmapID > 0); - await updateQueue.UpdateAsync(beatmapSet, cancellationToken); + await onlineLookupQueue.UpdateAsync(beatmapSet, cancellationToken); // ensure at least one beatmap was able to retrieve or keep an online ID, else drop the set ID. if (hadOnlineBeatmapIDs && !beatmapSet.Beatmaps.Any(b => b.OnlineBeatmapID > 0)) diff --git a/osu.Game/Beatmaps/BeatmapManager_UpdateQueue.cs b/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs similarity index 71% rename from osu.Game/Beatmaps/BeatmapManager_UpdateQueue.cs rename to osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs index 84901f9b50..2bd7529ab0 100644 --- a/osu.Game/Beatmaps/BeatmapManager_UpdateQueue.cs +++ b/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs @@ -22,20 +22,20 @@ namespace osu.Game.Beatmaps { public partial class BeatmapManager { - private class BeatmapUpdateQueue + private class BeatmapOnlineLookupQueue { private readonly IAPIProvider api; private readonly Storage storage; private const int update_queue_request_concurrency = 4; - private readonly ThreadedTaskScheduler updateScheduler = new ThreadedTaskScheduler(update_queue_request_concurrency, nameof(BeatmapUpdateQueue)); + private readonly ThreadedTaskScheduler updateScheduler = new ThreadedTaskScheduler(update_queue_request_concurrency, nameof(BeatmapOnlineLookupQueue)); private FileWebRequest cacheDownloadRequest; private const string cache_database_name = "online.db"; - public BeatmapUpdateQueue(IAPIProvider api, Storage storage) + public BeatmapOnlineLookupQueue(IAPIProvider api, Storage storage) { this.api = api; this.storage = storage; @@ -55,38 +55,12 @@ namespace osu.Game.Beatmaps // todo: expose this when we need to do individual difficulty lookups. protected Task UpdateAsync(BeatmapSetInfo beatmapSet, BeatmapInfo beatmap, CancellationToken cancellationToken) - => Task.Factory.StartNew(() => update(beatmapSet, beatmap), cancellationToken, TaskCreationOptions.HideScheduler, updateScheduler); + => Task.Factory.StartNew(() => lookup(beatmapSet, beatmap), cancellationToken, TaskCreationOptions.HideScheduler, updateScheduler); - private void update(BeatmapSetInfo set, BeatmapInfo beatmap) + private void lookup(BeatmapSetInfo set, BeatmapInfo beatmap) { - if (cacheDownloadRequest == null && storage.Exists(cache_database_name)) - { - try - { - using (var db = new SqliteConnection(storage.GetDatabaseConnectionString("online"))) - { - var found = db.QuerySingleOrDefault( - "SELECT * FROM osu_beatmaps WHERE checksum = @MD5Hash OR beatmap_id = @OnlineBeatmapID OR filename = @Path", beatmap); - - if (found != null) - { - var status = (BeatmapSetOnlineStatus)found.approved; - - beatmap.Status = status; - beatmap.BeatmapSet.Status = status; - beatmap.BeatmapSet.OnlineBeatmapSetID = found.beatmapset_id; - beatmap.OnlineBeatmapID = found.beatmap_id; - - LogForModel(set, $"Cached local retrieval for {beatmap}."); - return; - } - } - } - catch (Exception ex) - { - LogForModel(set, $"Cached local retrieval for {beatmap} failed with {ex}."); - } - } + if (checkLocalCache(set, beatmap)) + return; if (api?.State != APIState.Online) return; @@ -136,7 +110,7 @@ namespace osu.Game.Beatmaps File.Delete(compressedCacheFilePath); File.Delete(cacheFilePath); - Logger.Log($"{nameof(BeatmapUpdateQueue)}'s online cache download failed: {ex}", LoggingTarget.Database); + Logger.Log($"{nameof(BeatmapOnlineLookupQueue)}'s online cache download failed: {ex}", LoggingTarget.Database); }; cacheDownloadRequest.Finished += () => @@ -153,7 +127,7 @@ namespace osu.Game.Beatmaps } catch (Exception ex) { - Logger.Log($"{nameof(BeatmapUpdateQueue)}'s online cache extraction failed: {ex}", LoggingTarget.Database); + Logger.Log($"{nameof(BeatmapOnlineLookupQueue)}'s online cache extraction failed: {ex}", LoggingTarget.Database); File.Delete(cacheFilePath); } finally @@ -165,6 +139,45 @@ namespace osu.Game.Beatmaps cacheDownloadRequest.PerformAsync(); } + private bool checkLocalCache(BeatmapSetInfo set, BeatmapInfo beatmap) + { + // download is in progress (or was, and failed). + if (cacheDownloadRequest != null) + return false; + + // database is unavailable. + if (!storage.Exists(cache_database_name)) + return false; + + try + { + using (var db = new SqliteConnection(storage.GetDatabaseConnectionString("online"))) + { + var found = db.QuerySingleOrDefault( + "SELECT * FROM osu_beatmaps WHERE checksum = @MD5Hash OR beatmap_id = @OnlineBeatmapID OR filename = @Path", beatmap); + + if (found != null) + { + var status = (BeatmapSetOnlineStatus)found.approved; + + beatmap.Status = status; + beatmap.BeatmapSet.Status = status; + beatmap.BeatmapSet.OnlineBeatmapSetID = found.beatmapset_id; + beatmap.OnlineBeatmapID = found.beatmap_id; + + LogForModel(set, $"Cached local retrieval for {beatmap}."); + return true; + } + } + } + catch (Exception ex) + { + LogForModel(set, $"Cached local retrieval for {beatmap} failed with {ex}."); + } + + return false; + } + [Serializable] [SuppressMessage("ReSharper", "InconsistentNaming")] private class CachedOnlineBeatmapLookup From 68d40cf79064efc39f89631a80873f0886df99f9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 3 May 2020 13:25:57 +0900 Subject: [PATCH 0579/1117] Fix test failures due to online cache download --- osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs b/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs index 2bd7529ab0..2c79a664c5 100644 --- a/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs +++ b/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs @@ -9,6 +9,7 @@ using System.Threading; using System.Threading.Tasks; using Dapper; using Microsoft.Data.Sqlite; +using osu.Framework.Development; using osu.Framework.IO.Network; using osu.Framework.Logging; using osu.Framework.Platform; @@ -40,7 +41,8 @@ namespace osu.Game.Beatmaps this.api = api; this.storage = storage; - if (!storage.Exists(cache_database_name)) + // avoid downloading / using cache for unit tests. + if (!DebugUtils.IsNUnitRunning && !storage.Exists(cache_database_name)) prepareLocalCache(); } From cea6be5e52324d706b6120ffdaf340609b923447 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 3 May 2020 23:55:44 +0900 Subject: [PATCH 0580/1117] Expose as JudgementResult instead of "passing" state --- .../Skinning/TestSceneTaikoScroller.cs | 5 ++++- .../Skinning/LegacyTaikoScroller.cs | 10 ++++++---- osu.Game/Screens/Play/GameplayBeatmap.cs | 10 +++------- osu.Game/Screens/Play/Player.cs | 2 +- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.cs index 3d1ccadd6e..e26f410b71 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.cs @@ -3,6 +3,8 @@ using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Testing; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Skinning; using osu.Game.Skinning; @@ -13,7 +15,8 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning public TestSceneTaikoScroller() { AddStep("Load scroller", () => SetContents(() => new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.TaikoScroller), _ => Empty()))); - AddToggleStep("Toggle passing", passing => this.ChildrenOfType().ForEach(s => s.Passing.Value = !passing)); + AddToggleStep("Toggle passing", passing => this.ChildrenOfType().ForEach(s => s.LastResult.Value = + new JudgementResult(null, new Judgement()) { Type = passing ? HitResult.Perfect : HitResult.Miss })); } } } diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs b/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs index f61ee0301d..027fe1f302 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs @@ -7,6 +7,8 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Play; using osu.Game.Skinning; using osuTK; @@ -24,21 +26,21 @@ namespace osu.Game.Rulesets.Taiko.Skinning private void load(GameplayBeatmap gameplayBeatmap) { if (gameplayBeatmap != null) - ((IBindable)Passing).BindTo(gameplayBeatmap.Passing); + ((IBindable)LastResult).BindTo(gameplayBeatmap.LastJudgementResult); } protected override void LoadComplete() { base.LoadComplete(); - Passing.BindValueChanged(passing => + LastResult.BindValueChanged(result => { foreach (var sprite in InternalChildren.OfType()) - sprite.Passing = passing.NewValue; + sprite.Passing = result.NewValue == null || result.NewValue.Type > HitResult.Miss; }, true); } - public Bindable Passing = new BindableBool(true); + public Bindable LastResult = new Bindable(); protected override void Update() { diff --git a/osu.Game/Screens/Play/GameplayBeatmap.cs b/osu.Game/Screens/Play/GameplayBeatmap.cs index 0afa189e66..64894544f4 100644 --- a/osu.Game/Screens/Play/GameplayBeatmap.cs +++ b/osu.Game/Screens/Play/GameplayBeatmap.cs @@ -9,7 +9,6 @@ using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Scoring; namespace osu.Game.Screens.Play { @@ -42,13 +41,10 @@ namespace osu.Game.Screens.Play public IBeatmap Clone() => PlayableBeatmap.Clone(); - public IBindable Passing => passing; + private readonly Bindable lastJudgementResult = new Bindable(); - private readonly BindableBool passing = new BindableBool(true); + public IBindable LastJudgementResult => lastJudgementResult; - public void OnNewResult(JudgementResult result) - { - passing.Value = result.Type > HitResult.Miss; - } + public void ApplyResult(JudgementResult result) => lastJudgementResult.Value = result; } } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index eeb514b4be..f20d2504f7 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -193,7 +193,7 @@ namespace osu.Game.Screens.Play { HealthProcessor.ApplyResult(r); ScoreProcessor.ApplyResult(r); - gameplayBeatmap.OnNewResult(r); + gameplayBeatmap.ApplyResult(r); }; DrawableRuleset.OnRevertResult += r => From a1cd007cadfdb120f3a977e258676fedad854081 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 May 2020 14:43:47 +0900 Subject: [PATCH 0581/1117] Fix song select tests potentially failing due to difficulty panels not yet displayed --- .../Visual/SongSelect/TestScenePlaySongSelect.cs | 12 +++++++++--- osu.Game/Beatmaps/BeatmapManager.cs | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index aed8e19fb2..802c324c90 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -110,7 +110,7 @@ namespace osu.Game.Tests.Visual.SongSelect createSongSelect(); - AddUntilStep("wait for initial selection", () => !Beatmap.IsDefault); + waitForInitialSelection(); WorkingBeatmap selected = null; @@ -135,7 +135,7 @@ namespace osu.Game.Tests.Visual.SongSelect createSongSelect(); - AddUntilStep("wait for initial selection", () => !Beatmap.IsDefault); + waitForInitialSelection(); WorkingBeatmap selected = null; @@ -189,7 +189,7 @@ namespace osu.Game.Tests.Visual.SongSelect createSongSelect(); - AddUntilStep("wait for initial selection", () => !Beatmap.IsDefault); + waitForInitialSelection(); WorkingBeatmap selected = null; @@ -769,6 +769,12 @@ namespace osu.Game.Tests.Visual.SongSelect AddAssert("Check first item in group selected", () => Beatmap.Value.BeatmapInfo == groupIcon.Items.First().Beatmap); } + private void waitForInitialSelection() + { + AddUntilStep("wait for initial selection", () => !Beatmap.IsDefault); + AddUntilStep("wait for difficulty panels visible", () => songSelect.Carousel.ChildrenOfType().Any()); + } + private int getBeatmapIndex(BeatmapSetInfo set, BeatmapInfo info) => set.Beatmaps.FindIndex(b => b == info); private int getCurrentBeatmapIndex() => getBeatmapIndex(songSelect.Carousel.SelectedBeatmapSet, songSelect.Carousel.SelectedBeatmap); diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 19d1162d23..1b29f14c9b 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -140,7 +140,7 @@ namespace osu.Game.Beatmaps { var beatmapIds = beatmapSet.Beatmaps.Where(b => b.OnlineBeatmapID.HasValue).Select(b => b.OnlineBeatmapID).ToList(); - LogForModel(beatmapSet, "Validating online IDs..."); + LogForModel(beatmapSet, $"Validating online IDs for {beatmapSet.Beatmaps.Count} beatmaps..."); // ensure all IDs are unique if (beatmapIds.GroupBy(b => b).Any(g => g.Count() > 1)) From 02b9f51bdd4c2e0c2b07cefea40b54b4f0bbc0b2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 May 2020 13:31:49 +0900 Subject: [PATCH 0582/1117] Add failing test --- .../SongSelect/TestScenePlaySongSelect.cs | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 802c324c90..851801d38a 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -24,10 +24,12 @@ 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.Play; using osu.Game.Screens.Select; using osu.Game.Screens.Select.Carousel; using osu.Game.Screens.Select.Filter; +using osu.Game.Users; using osuTK.Input; namespace osu.Game.Tests.Visual.SongSelect @@ -769,6 +771,70 @@ namespace osu.Game.Tests.Visual.SongSelect AddAssert("Check first item in group selected", () => Beatmap.Value.BeatmapInfo == groupIcon.Items.First().Beatmap); } + [Test] + public void TestChangeRulesetWhilePresentingScore() + { + changeRuleset(0); + + createSongSelect(); + + addRulesetImportStep(0); + addRulesetImportStep(1); + + AddStep("present score", () => + { + var presentBeatmap = manager.QueryBeatmap(b => b.RulesetID == 0); + var switchBeatmap = manager.QueryBeatmap(b => b.RulesetID == 1); + + // this ruleset change should be overridden by the present. + Ruleset.Value = switchBeatmap.Ruleset; + + songSelect.PresentScore(new ScoreInfo + { + User = new User { Username = "woo" }, + Beatmap = presentBeatmap, + Ruleset = presentBeatmap.Ruleset + }); + }); + + AddUntilStep("wait for results screen presented", () => !songSelect.IsCurrentScreen()); + + AddAssert("check beatmap is correct for score", () => Beatmap.Value.BeatmapInfo.Equals(manager.QueryBeatmap(b => b.RulesetID == 0))); + AddAssert("check ruleset is correct for score", () => Ruleset.Value.ID == 0); + } + + [Test] + public void TestChangeBeatmapWhilePresentingScore() + { + changeRuleset(0); + + createSongSelect(); + + addRulesetImportStep(0); + addRulesetImportStep(1); + + AddStep("present score", () => + { + var presentBeatmap = manager.QueryBeatmap(b => b.RulesetID == 0); + var switchBeatmap = manager.QueryBeatmap(b => b.RulesetID == 1); + + // this beatmap change should be overridden by the present. + Beatmap.Value = manager.GetWorkingBeatmap(switchBeatmap); + + songSelect.PresentScore(new ScoreInfo + { + User = new User { Username = "woo" }, + Beatmap = presentBeatmap, + Ruleset = presentBeatmap.Ruleset + }); + }); + + AddUntilStep("wait for results screen presented", () => !songSelect.IsCurrentScreen()); + + AddAssert("check beatmap is correct for score", () => Beatmap.Value.BeatmapInfo.Equals(manager.QueryBeatmap(b => b.RulesetID == 0))); + AddAssert("check ruleset is correct for score", () => Ruleset.Value.ID == 0); + } + private void waitForInitialSelection() { AddUntilStep("wait for initial selection", () => !Beatmap.IsDefault); @@ -882,6 +948,8 @@ namespace osu.Game.Tests.Visual.SongSelect public WorkingBeatmap CurrentBeatmapDetailsBeatmap => BeatmapDetails.Beatmap; public new BeatmapCarousel Carousel => base.Carousel; + public new void PresentScore(ScoreInfo score) => base.PresentScore(score); + protected override bool OnStart() { StartRequested?.Invoke(); From 06f58dd3e35a6d233845dbd73482e2638d22156f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 May 2020 14:04:30 +0900 Subject: [PATCH 0583/1117] Ensure correct beatmap and ruleset when presenting a score from song select --- osu.Game/Screens/Select/PlaySongSelect.cs | 6 +++++- osu.Game/Screens/Select/SongSelect.cs | 15 ++++++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index 179aab54a3..21ddc5685d 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; using osu.Framework.Screens; using osu.Game.Graphics; +using osu.Game.Scoring; using osu.Game.Screens.Play; using osu.Game.Screens.Ranking; using osu.Game.Users; @@ -32,9 +33,12 @@ namespace osu.Game.Screens.Select Edit(); }, Key.Number4); - ((PlayBeatmapDetailArea)BeatmapDetails).Leaderboard.ScoreSelected += score => this.Push(new ResultsScreen(score)); + ((PlayBeatmapDetailArea)BeatmapDetails).Leaderboard.ScoreSelected += PresentScore; } + protected void PresentScore(ScoreInfo score) => + FinaliseSelection(score.Beatmap, score.Ruleset, () => this.Push(new ResultsScreen(score))); + protected override BeatmapDetailArea CreateBeatmapDetailArea() => new PlayBeatmapDetailArea(); public override void OnResuming(IScreen last) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index a7e27c27ba..2b373ab7e0 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -342,13 +342,17 @@ namespace osu.Game.Screens.Select /// Call to make a selection and perform the default action for this SongSelect. /// /// An optional beatmap to override the current carousel selection. - /// Whether to trigger . - public void FinaliseSelection(BeatmapInfo beatmap = null, bool performStartAction = true) + /// An optional ruleset to override the current carousel selection. + /// An optional custom action to perform instead of . + public void FinaliseSelection(BeatmapInfo beatmap = null, RulesetInfo ruleset = null, Action customStartAction = null) { // This is very important as we have not yet bound to screen-level bindables before the carousel load is completed. if (!Carousel.BeatmapSetsLoaded) return; + if (ruleset != null) + Ruleset.Value = ruleset; + transferRulesetValue(); // while transferRulesetValue will flush, it only does so if the ruleset changes. @@ -369,7 +373,12 @@ namespace osu.Game.Screens.Select selectionChangedDebounce = null; } - if (performStartAction && OnStart()) + if (customStartAction != null) + { + customStartAction(); + Carousel.AllowSelection = false; + } + else if (OnStart()) Carousel.AllowSelection = false; } From 81889e0034115e5a22f4882b1f6e98703d9acf50 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 May 2020 15:19:36 +0900 Subject: [PATCH 0584/1117] Fix tests potentially selecting a deleted beatmap --- .../SongSelect/TestScenePlaySongSelect.cs | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 851801d38a..81fd1b66e5 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -774,6 +774,9 @@ namespace osu.Game.Tests.Visual.SongSelect [Test] public void TestChangeRulesetWhilePresentingScore() { + BeatmapInfo getPresentBeatmap() => manager.QueryBeatmap(b => !b.BeatmapSet.DeletePending && b.RulesetID == 0); + BeatmapInfo getSwitchBeatmap() => manager.QueryBeatmap(b => !b.BeatmapSet.DeletePending && b.RulesetID == 1); + changeRuleset(0); createSongSelect(); @@ -783,55 +786,52 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("present score", () => { - var presentBeatmap = manager.QueryBeatmap(b => b.RulesetID == 0); - var switchBeatmap = manager.QueryBeatmap(b => b.RulesetID == 1); - // this ruleset change should be overridden by the present. - Ruleset.Value = switchBeatmap.Ruleset; + Ruleset.Value = getSwitchBeatmap().Ruleset; songSelect.PresentScore(new ScoreInfo { User = new User { Username = "woo" }, - Beatmap = presentBeatmap, - Ruleset = presentBeatmap.Ruleset + Beatmap = getPresentBeatmap(), + Ruleset = getPresentBeatmap().Ruleset }); }); AddUntilStep("wait for results screen presented", () => !songSelect.IsCurrentScreen()); - AddAssert("check beatmap is correct for score", () => Beatmap.Value.BeatmapInfo.Equals(manager.QueryBeatmap(b => b.RulesetID == 0))); + AddAssert("check beatmap is correct for score", () => Beatmap.Value.BeatmapInfo.Equals(getPresentBeatmap())); AddAssert("check ruleset is correct for score", () => Ruleset.Value.ID == 0); } [Test] public void TestChangeBeatmapWhilePresentingScore() { - changeRuleset(0); + BeatmapInfo getPresentBeatmap() => manager.QueryBeatmap(b => !b.BeatmapSet.DeletePending && b.RulesetID == 0); + BeatmapInfo getSwitchBeatmap() => manager.QueryBeatmap(b => !b.BeatmapSet.DeletePending && b.RulesetID == 1); - createSongSelect(); + changeRuleset(0); addRulesetImportStep(0); addRulesetImportStep(1); + createSongSelect(); + AddStep("present score", () => { - var presentBeatmap = manager.QueryBeatmap(b => b.RulesetID == 0); - var switchBeatmap = manager.QueryBeatmap(b => b.RulesetID == 1); - // this beatmap change should be overridden by the present. - Beatmap.Value = manager.GetWorkingBeatmap(switchBeatmap); + Beatmap.Value = manager.GetWorkingBeatmap(getSwitchBeatmap()); songSelect.PresentScore(new ScoreInfo { User = new User { Username = "woo" }, - Beatmap = presentBeatmap, - Ruleset = presentBeatmap.Ruleset + Beatmap = getPresentBeatmap(), + Ruleset = getPresentBeatmap().Ruleset }); }); AddUntilStep("wait for results screen presented", () => !songSelect.IsCurrentScreen()); - AddAssert("check beatmap is correct for score", () => Beatmap.Value.BeatmapInfo.Equals(manager.QueryBeatmap(b => b.RulesetID == 0))); + AddAssert("check beatmap is correct for score", () => Beatmap.Value.BeatmapInfo.Equals(getPresentBeatmap())); AddAssert("check ruleset is correct for score", () => Ruleset.Value.ID == 0); } From 46b0526db7afd25e1a962420ee8235df1f109801 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 4 May 2020 15:27:04 +0900 Subject: [PATCH 0585/1117] Remove hack limiting max number of ticks --- osu.Game.Rulesets.Catch/Objects/JuiceStream.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs index 19219cc1ba..01011645bd 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs @@ -58,8 +58,6 @@ namespace osu.Game.Rulesets.Catch.Objects SliderEventDescriptor? lastEvent = null; - int ticksGenerated = 0; - foreach (var e in SliderEventGenerator.Generate(StartTime, SpanDuration, Velocity, TickDistance, Path.Distance, this.SpanCount(), LegacyLastTickOffset)) { // generate tiny droplets since the last point @@ -75,9 +73,6 @@ namespace osu.Game.Rulesets.Catch.Objects for (double t = timeBetweenTiny; t < sinceLastTick; t += timeBetweenTiny) { - if (ticksGenerated++ >= 10000) - break; - AddNested(new TinyDroplet { StartTime = t + lastEvent.Value.Time, From 6d3a24ff01cdeea9bf52d164087279989b277b6d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 4 May 2020 15:55:42 +0900 Subject: [PATCH 0586/1117] Reorder tick hit results --- osu.Game/Rulesets/Scoring/HitResult.cs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/HitResult.cs b/osu.Game/Rulesets/Scoring/HitResult.cs index 0c895bd086..b057af2a50 100644 --- a/osu.Game/Rulesets/Scoring/HitResult.cs +++ b/osu.Game/Rulesets/Scoring/HitResult.cs @@ -44,9 +44,24 @@ namespace osu.Game.Rulesets.Scoring [Description(@"Perfect")] Perfect, - SmallTickHit, + /// + /// Indicates small tick miss. + /// SmallTickMiss, - LargeTickHit, + + /// + /// Indicates a small tick hit. + /// + SmallTickHit, + + /// + /// Indicates a large tick miss. + /// LargeTickMiss, + + /// + /// Indicates a large tick hit. + /// + LargeTickHit } } From 6621d363da05951e3208f785f472190802638789 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 May 2020 17:01:05 +0900 Subject: [PATCH 0587/1117] Add basic custom data directory support --- .../NonVisual/CustomDataDirectoryTest.cs | 88 +++++++++++++++++++ .../Configuration/StorageConfigManager.cs | 30 +++++++ osu.Game/OsuGameBase.cs | 23 ++++- 3 files changed, 139 insertions(+), 2 deletions(-) create mode 100644 osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs create mode 100644 osu.Game/Configuration/StorageConfigManager.cs diff --git a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs new file mode 100644 index 0000000000..2d5f1f238f --- /dev/null +++ b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs @@ -0,0 +1,88 @@ +// 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.Threading; +using System.Threading.Tasks; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Platform; +using osu.Game.Configuration; + +namespace osu.Game.Tests.NonVisual +{ + [TestFixture] + public class CustomDataDirectoryTest + { + [Test] + public void TestDefaultDirectory() + { + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestDefaultDirectory))) + { + try + { + var osu = loadOsu(host); + var storage = osu.Dependencies.Get(); + + string defaultStorageLocation = Path.Combine(Environment.CurrentDirectory, $"headless-{nameof(TestDefaultDirectory)}"); + + Assert.That(storage.GetFullPath("."), Is.EqualTo(defaultStorageLocation)); + } + finally + { + host.Exit(); + } + } + } + + [Test] + public void TestCustomDirectory() + { + using (var host = new HeadlessGameHost(nameof(TestCustomDirectory))) + { + string headlessPrefix = $"headless-{nameof(TestCustomDirectory)}"; + + // need access before the game has constructed its own storage yet. + Storage storage = new DesktopStorage(headlessPrefix, host); + // manual cleaning so we can prepare a config file. + storage.DeleteDirectory(string.Empty); + + using (var storageConfig = new StorageConfigManager(storage)) + storageConfig.Set(StorageConfig.FullPath, Path.Combine(Environment.CurrentDirectory, "custom-path")); + + try + { + var osu = loadOsu(host); + + // switch to DI'd storage + storage = osu.Dependencies.Get(); + + Assert.That(storage.GetFullPath("."), Is.EqualTo(Path.Combine(Environment.CurrentDirectory, "custom-path"))); + } + finally + { + host.Exit(); + } + } + } + + private OsuGameBase loadOsu(GameHost host) + { + var osu = new OsuGameBase(); + Task.Run(() => host.Run(osu)); + waitForOrAssert(() => osu.IsLoaded, @"osu! failed to start in a reasonable amount of time"); + return osu; + } + + private static void waitForOrAssert(Func result, string failureMessage, int timeout = 60000) + { + Task task = Task.Run(() => + { + while (!result()) Thread.Sleep(200); + }); + + Assert.IsTrue(task.Wait(timeout), failureMessage); + } + } +} diff --git a/osu.Game/Configuration/StorageConfigManager.cs b/osu.Game/Configuration/StorageConfigManager.cs new file mode 100644 index 0000000000..929f8f22ad --- /dev/null +++ b/osu.Game/Configuration/StorageConfigManager.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 osu.Framework.Configuration; +using osu.Framework.Platform; + +namespace osu.Game.Configuration +{ + public class StorageConfigManager : IniConfigManager + { + protected override string Filename => "storage.ini"; + + public StorageConfigManager(Storage storage) + : base(storage) + { + } + + protected override void InitialiseDefaults() + { + base.InitialiseDefaults(); + + Set(StorageConfig.FullPath, string.Empty); + } + } + + public enum StorageConfig + { + FullPath, + } +} diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 609b6ce98e..fe25197294 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -132,6 +132,8 @@ namespace osu.Game dependencies.Cache(contextFactory = new DatabaseContextFactory(Storage)); + dependencies.CacheAs(Storage); + var largeStore = new LargeTextureStore(Host.CreateTextureLoaderStore(new NamespacedResourceStore(Resources, @"Textures"))); largeStore.AddStore(Host.CreateTextureLoaderStore(new OnlineStore())); dependencies.Cache(largeStore); @@ -300,8 +302,13 @@ namespace osu.Game { base.SetHost(host); - if (Storage == null) - Storage = host.Storage; + var storageConfig = new StorageConfigManager(host.Storage); + + var customStoragePath = storageConfig.Get(StorageConfig.FullPath); + + Storage = !string.IsNullOrEmpty(customStoragePath) + ? new CustomStorage(customStoragePath, host) + : host.Storage; if (LocalConfig == null) LocalConfig = new OsuConfigManager(Storage); @@ -353,5 +360,17 @@ namespace osu.Game public override bool ChangeFocusOnClick => false; } } + + /// + /// A storage pointing to an absolute location specified by the user to store game data files. + /// + private class CustomStorage : NativeStorage + { + public CustomStorage(string fullPath, GameHost host) + : base(string.Empty, host) + { + BasePath = fullPath; + } + } } } From 62d433c9c57b2de866d579f4bfbad543eadc9649 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 4 May 2020 17:01:07 +0900 Subject: [PATCH 0588/1117] Adjust diffcalc test value --- 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 51fe0b035d..ee416e5a38 100644 --- a/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Catch.Tests { protected override string ResourceAssembly => "osu.Game.Rulesets.Catch"; - [TestCase(4.2058561036909863d, "diffcalc-test")] + [TestCase(4.050601681491468d, "diffcalc-test")] public void Test(double expected, string name) => base.Test(expected, name); From 5edabbdee25e38e3e0d1ab08296e5a4f6119e79b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 May 2020 17:35:35 +0900 Subject: [PATCH 0589/1117] Redirect log output to custom data directory --- osu.Game/OsuGameBase.cs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index fe25197294..f92db4e111 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -71,6 +71,8 @@ namespace osu.Game protected MenuCursorContainer MenuCursorContainer; + protected StorageConfigManager StorageConfig; + private Container content; protected override Container Content => content; @@ -302,13 +304,17 @@ namespace osu.Game { base.SetHost(host); - var storageConfig = new StorageConfigManager(host.Storage); + StorageConfig = new StorageConfigManager(host.Storage); - var customStoragePath = storageConfig.Get(StorageConfig.FullPath); + var customStoragePath = StorageConfig.Get(Configuration.StorageConfig.FullPath); - Storage = !string.IsNullOrEmpty(customStoragePath) - ? new CustomStorage(customStoragePath, host) - : host.Storage; + if (!string.IsNullOrEmpty(customStoragePath)) + { + Storage = new CustomStorage(customStoragePath, host); + Logger.Storage = Storage.GetStorageForDirectory("logs"); + } + else + Storage = host.Storage; if (LocalConfig == null) LocalConfig = new OsuConfigManager(Storage); From e2593ac3e77e82d14c776ef4cf6c19c60ac697b3 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 4 May 2020 08:56:18 +0000 Subject: [PATCH 0590/1117] Bump Microsoft.CodeAnalysis.FxCopAnalyzers from 2.9.8 to 3.0.0 Bumps [Microsoft.CodeAnalysis.FxCopAnalyzers](https://github.com/dotnet/roslyn-analyzers) from 2.9.8 to 3.0.0. - [Release notes](https://github.com/dotnet/roslyn-analyzers/releases) - [Changelog](https://github.com/dotnet/roslyn-analyzers/blob/master/PostReleaseActivities.md) - [Commits](https://github.com/dotnet/roslyn-analyzers/compare/v2.9.8...v3.0.0) Signed-off-by: dependabot-preview[bot] --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 21b8b402e0..fbe300458e 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -18,7 +18,7 @@ - + $(MSBuildThisFileDirectory)CodeAnalysis\osu.ruleset From 4ee2e6cd47add8c964d61445cca16a271b6236dc Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 4 May 2020 08:57:09 +0000 Subject: [PATCH 0591/1117] Bump Humanizer from 2.8.2 to 2.8.11 Bumps [Humanizer](https://github.com/Humanizr/Humanizer) from 2.8.2 to 2.8.11. - [Release notes](https://github.com/Humanizr/Humanizer/releases) - [Changelog](https://github.com/Humanizr/Humanizer/blob/master/release_notes.md) - [Commits](https://github.com/Humanizr/Humanizer/compare/v2.8.2...v2.8.11) Signed-off-by: dependabot-preview[bot] --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 91c89cbc20..9db5fe562c 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -20,7 +20,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 9ff7e3fc02..82253a0418 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -76,7 +76,7 @@ - + From fe31bac505bb59891325fc59afb4ba597df3e052 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 4 May 2020 18:20:20 +0900 Subject: [PATCH 0592/1117] Fix build error --- osu.Game/Overlays/SearchableList/DisplayStyleControl.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/SearchableList/DisplayStyleControl.cs b/osu.Game/Overlays/SearchableList/DisplayStyleControl.cs index a33f4eb30d..5ecb477a2f 100644 --- a/osu.Game/Overlays/SearchableList/DisplayStyleControl.cs +++ b/osu.Game/Overlays/SearchableList/DisplayStyleControl.cs @@ -91,6 +91,8 @@ namespace osu.Game.Overlays.SearchableList protected override void Dispose(bool isDisposing) { + base.Dispose(isDisposing); + bindable.ValueChanged -= Bindable_ValueChanged; } } From 969412a4265aa3c94f0a6112552b5ad9287f4908 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 4 May 2020 10:28:42 +0000 Subject: [PATCH 0593/1117] Bump Microsoft.CodeAnalysis.BannedApiAnalyzers from 2.9.8 to 3.0.0 Bumps [Microsoft.CodeAnalysis.BannedApiAnalyzers](https://github.com/dotnet/roslyn-analyzers) from 2.9.8 to 3.0.0. - [Release notes](https://github.com/dotnet/roslyn-analyzers/releases) - [Changelog](https://github.com/dotnet/roslyn-analyzers/blob/master/PostReleaseActivities.md) - [Commits](https://github.com/dotnet/roslyn-analyzers/compare/v2.9.8...v3.0.0) Signed-off-by: dependabot-preview[bot] --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 21b8b402e0..5d011dfdc5 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -16,7 +16,7 @@ - + From c987af988c98745a8039372563b87717a7691e46 Mon Sep 17 00:00:00 2001 From: alex Date: Mon, 4 May 2020 18:26:12 -0700 Subject: [PATCH 0594/1117] Fix typo --- osu.Game/Screens/OsuScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index 2124a66a75..35bb4fa34f 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -23,7 +23,7 @@ namespace osu.Game.Screens { /// /// The amount of negative padding that should be applied to game background content which touches both the left and right sides of the screen. - /// This allows for the game content to be pushed byt he options/notification overlays without causing black areas to appear. + /// This allows for the game content to be pushed by the options/notification overlays without causing black areas to appear. /// public const float HORIZONTAL_OVERFLOW_PADDING = 50; From 0e2ccac33b916abc63e9aa4e8cb474e6761fb22e Mon Sep 17 00:00:00 2001 From: alex Date: Mon, 4 May 2020 18:31:11 -0700 Subject: [PATCH 0595/1117] Add spaces to comments --- osu.Desktop/Updater/SquirrelUpdateManager.cs | 10 ++++----- osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs | 2 +- .../Replays/CatchAutoGenerator.cs | 6 ++--- .../Beatmaps/OsuBeatmapProcessor.cs | 18 +++++++-------- osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs | 2 +- .../Drawables/Pieces/MainCirclePiece.cs | 2 +- .../Beatmaps/IO/ImportBeatmapTest.cs | 22 +++++++++---------- .../NonVisual/ControlPointInfoTest.cs | 2 +- .../NonVisual/FramedReplayInputHandlerTest.cs | 6 ++--- osu.Game/Beatmaps/BeatmapManager.cs | 2 +- osu.Game/Graphics/Backgrounds/Triangles.cs | 4 ++-- .../Graphics/Containers/OsuScrollContainer.cs | 2 +- osu.Game/IPC/ArchiveImportIPCChannel.cs | 2 +- osu.Game/Online/API/APIAccess.cs | 8 +++---- osu.Game/Online/API/APIRequest.cs | 2 +- osu.Game/Online/Chat/Channel.cs | 2 +- osu.Game/Online/Chat/MessageFormatter.cs | 4 ++-- osu.Game/OsuGame.cs | 4 ++-- .../BeatmapListing/Panels/GridBeatmapPanel.cs | 2 +- osu.Game/Overlays/BeatmapSet/Header.cs | 2 +- osu.Game/Overlays/ChatOverlay.cs | 2 +- osu.Game/Overlays/DialogOverlay.cs | 2 +- osu.Game/Overlays/Music/PlaylistItem.cs | 2 +- osu.Game/Overlays/MusicController.cs | 2 +- osu.Game/Overlays/News/NewsArticleCover.cs | 2 +- .../Notifications/ProgressNotification.cs | 2 +- osu.Game/Overlays/NowPlayingOverlay.cs | 2 +- osu.Game/Overlays/OSD/Toast.cs | 2 +- .../Components/OverlinedInfoContainer.cs | 2 +- .../Profile/Header/MedalHeaderContainer.cs | 2 +- .../SearchableListFilterControl.cs | 2 +- osu.Game/Overlays/Toolbar/ToolbarButton.cs | 2 +- osu.Game/Overlays/VolumeOverlay.cs | 2 +- .../Replays/FramedReplayInputHandler.cs | 6 ++--- osu.Game/Rulesets/RulesetStore.cs | 6 ++--- osu.Game/Screens/BackgroundScreen.cs | 2 +- osu.Game/Screens/Menu/IntroScreen.cs | 2 +- osu.Game/Screens/Menu/LogoVisualisation.cs | 10 ++++----- osu.Game/Screens/Menu/MainMenu.cs | 2 +- osu.Game/Screens/Play/BreakOverlay.cs | 2 +- .../Screens/Play/HUD/PlayerSettingsOverlay.cs | 2 +- osu.Game/Screens/Play/KeyCounter.cs | 4 ++-- osu.Game/Screens/Play/PlayerLoader.cs | 6 ++--- osu.Game/Screens/Select/BeatmapCarousel.cs | 2 +- .../Select/BeatmapDetailAreaTabControl.cs | 2 +- osu.Game/Screens/Select/BeatmapDetails.cs | 4 ++-- .../Screens/Select/Details/AdvancedStats.cs | 2 +- osu.Game/Screens/Select/SongSelect.cs | 2 +- 48 files changed, 92 insertions(+), 92 deletions(-) diff --git a/osu.Desktop/Updater/SquirrelUpdateManager.cs b/osu.Desktop/Updater/SquirrelUpdateManager.cs index 60b47a8b3a..ade8460dd7 100644 --- a/osu.Desktop/Updater/SquirrelUpdateManager.cs +++ b/osu.Desktop/Updater/SquirrelUpdateManager.cs @@ -43,7 +43,7 @@ namespace osu.Desktop.Updater private async void checkForUpdateAsync(bool useDeltaPatching = true, UpdateProgressNotification notification = null) { - //should we schedule a retry on completion of this check? + // should we schedule a retry on completion of this check? bool scheduleRecheck = true; try @@ -52,7 +52,7 @@ namespace osu.Desktop.Updater var info = await updateManager.CheckForUpdate(!useDeltaPatching); if (info.ReleasesToApply.Count == 0) - //no updates available. bail and retry later. + // no updates available. bail and retry later. return; if (notification == null) @@ -81,8 +81,8 @@ namespace osu.Desktop.Updater { logger.Add(@"delta patching failed; will attempt full download!"); - //could fail if deltas are unavailable for full update path (https://github.com/Squirrel/Squirrel.Windows/issues/959) - //try again without deltas. + // could fail if deltas are unavailable for full update path (https://github.com/Squirrel/Squirrel.Windows/issues/959) + // try again without deltas. checkForUpdateAsync(false, notification); scheduleRecheck = false; } @@ -101,7 +101,7 @@ namespace osu.Desktop.Updater { if (scheduleRecheck) { - //check again in 30 minutes. + // check again in 30 minutes. Scheduler.AddDelayed(() => checkForUpdateAsync(), 60000 * 30); } } diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs b/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs index 16414261a5..c1d24395e4 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs @@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Catch.Mods RelativeSizeAxes = Axes.Both; } - //disable keyboard controls + // disable keyboard controls public bool OnPressed(CatchAction action) => true; public void OnReleased(CatchAction action) diff --git a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs index b90b5812a6..7a33cb0577 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs @@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Catch.Replays if (lastPosition - catcher_width_half < h.X && lastPosition + catcher_width_half > h.X) { - //we are already in the correct range. + // we are already in the correct range. lastTime = h.StartTime; addFrame(h.StartTime, lastPosition); return; @@ -72,14 +72,14 @@ namespace osu.Game.Rulesets.Catch.Replays } else if (dashRequired) { - //we do a movement in two parts - the dash part then the normal part... + // we do a movement in two parts - the dash part then the normal part... double timeAtNormalSpeed = positionChange / movement_speed; double timeWeNeedToSave = timeAtNormalSpeed - timeAvailable; double timeAtDashSpeed = timeWeNeedToSave / 2; float midPosition = (float)Interpolation.Lerp(lastPosition, h.X, (float)timeAtDashSpeed / timeAvailable); - //dash movement + // dash movement addFrame(h.StartTime - timeAvailable + 1, lastPosition, true); addFrame(h.StartTime - timeAvailable + timeAtDashSpeed, midPosition); addFrame(h.StartTime, h.X); diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs index 3a829f72fa..f51f04bf87 100644 --- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs @@ -66,7 +66,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps double stackThreshold = objectN.TimePreempt * beatmap.BeatmapInfo.StackLeniency; if (objectN.StartTime - endTime > stackThreshold) - //We are no longer within stacking range of the next object. + // We are no longer within stacking range of the next object. break; if (Vector2Extensions.Distance(stackBaseObject.Position, objectN.Position) < stack_distance @@ -88,7 +88,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps } } - //Reverse pass for stack calculation. + // Reverse pass for stack calculation. int extendedStartIndex = startIndex; for (int i = extendedEndIndex; i > startIndex; i--) @@ -124,7 +124,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps double endTime = objectN.GetEndTime(); if (objectI.StartTime - endTime > stackThreshold) - //We are no longer within stacking range of the previous object. + // We are no longer within stacking range of the previous object. break; // HitObjects before the specified update range haven't been reset yet @@ -145,20 +145,20 @@ namespace osu.Game.Rulesets.Osu.Beatmaps for (int j = n + 1; j <= i; j++) { - //For each object which was declared under this slider, we will offset it to appear *below* the slider end (rather than above). + // For each object which was declared under this slider, we will offset it to appear *below* the slider end (rather than above). OsuHitObject objectJ = beatmap.HitObjects[j]; if (Vector2Extensions.Distance(objectN.EndPosition, objectJ.Position) < stack_distance) objectJ.StackHeight -= offset; } - //We have hit a slider. We should restart calculation using this as the new base. - //Breaking here will mean that the slider still has StackCount of 0, so will be handled in the i-outer-loop. + // We have hit a slider. We should restart calculation using this as the new base. + // Breaking here will mean that the slider still has StackCount of 0, so will be handled in the i-outer-loop. break; } if (Vector2Extensions.Distance(objectN.Position, objectI.Position) < stack_distance) { - //Keep processing as if there are no sliders. If we come across a slider, this gets cancelled out. + // Keep processing as if there are no sliders. If we come across a slider, this gets cancelled out. //NOTE: Sliders with start positions stacking are a special case that is also handled here. objectN.StackHeight = objectI.StackHeight + 1; @@ -177,7 +177,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps if (objectN is Spinner) continue; if (objectI.StartTime - objectN.StartTime > stackThreshold) - //We are no longer within stacking range of the previous object. + // We are no longer within stacking range of the previous object. break; if (Vector2Extensions.Distance(objectN.EndPosition, objectI.Position) < stack_distance) @@ -221,7 +221,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps } else if (Vector2Extensions.Distance(beatmap.HitObjects[j].Position, position2) < stack_distance) { - //Case for sliders - bump notes down and right, rather than up and left. + // Case for sliders - bump notes down and right, rather than up and left. sliderStack++; beatmap.HitObjects[j].StackHeight -= sliderStack; startTime = beatmap.HitObjects[j].GetEndTime(); diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs index 44dba7715a..5e80d08667 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs @@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Osu.Mods Vector2 originalPosition = drawable.Position; Vector2 appearOffset = new Vector2(MathF.Cos(theta), MathF.Sin(theta)) * appearDistance; - //the - 1 and + 1 prevents the hit objects to appear in the wrong position. + // the - 1 and + 1 prevents the hit objects to appear in the wrong position. double appearTime = hitObject.StartTime - hitObject.TimePreempt - 1; double moveDuration = hitObject.TimePreempt + 1; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/MainCirclePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/MainCirclePiece.cs index e364c96426..cb3787a493 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/MainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/MainCirclePiece.cs @@ -81,7 +81,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces using (BeginDelayedSequence(flash_in, true)) { - //after the flash, we can hide some elements that were behind it + // after the flash, we can hide some elements that were behind it ring.FadeOut(); circle.FadeOut(); number.FadeOut(); diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index c6095ae404..ba6f5fc85c 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -31,7 +31,7 @@ namespace osu.Game.Tests.Beatmaps.IO [Test] public async Task TestImportWhenClosed() { - //unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. + // unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportWhenClosed))) { try @@ -48,7 +48,7 @@ namespace osu.Game.Tests.Beatmaps.IO [Test] public async Task TestImportThenDelete() { - //unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. + // unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportThenDelete))) { try @@ -69,7 +69,7 @@ namespace osu.Game.Tests.Beatmaps.IO [Test] public async Task TestImportThenImport() { - //unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. + // unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportThenImport))) { try @@ -96,7 +96,7 @@ namespace osu.Game.Tests.Beatmaps.IO [Test] public async Task TestImportCorruptThenImport() { - //unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. + // unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportCorruptThenImport))) { try @@ -138,7 +138,7 @@ namespace osu.Game.Tests.Beatmaps.IO [Test] public async Task TestRollbackOnFailure() { - //unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. + // unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestRollbackOnFailure))) { try @@ -215,7 +215,7 @@ namespace osu.Game.Tests.Beatmaps.IO [Test] public async Task TestImportThenImportDifferentHash() { - //unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. + // unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportThenImportDifferentHash))) { try @@ -246,7 +246,7 @@ namespace osu.Game.Tests.Beatmaps.IO [Test] public async Task TestImportThenDeleteThenImport() { - //unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. + // unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportThenDeleteThenImport))) { try @@ -274,7 +274,7 @@ namespace osu.Game.Tests.Beatmaps.IO [TestCase(false)] public async Task TestImportThenDeleteThenImportWithOnlineIDMismatch(bool set) { - //unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. + // unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. using (HeadlessGameHost host = new CleanRunHeadlessGameHost($"{nameof(TestImportThenDeleteThenImportWithOnlineIDMismatch)}-{set}")) { try @@ -308,7 +308,7 @@ namespace osu.Game.Tests.Beatmaps.IO [Test] public async Task TestImportWithDuplicateBeatmapIDs() { - //unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. + // unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportWithDuplicateBeatmapIDs))) { try @@ -695,12 +695,12 @@ namespace osu.Game.Tests.Beatmaps.IO waitForOrAssert(() => (resultSets = store.QueryBeatmapSets(s => s.OnlineBeatmapSetID == 241526)).Any(), @"BeatmapSet did not import to the database in allocated time.", timeout); - //ensure we were stored to beatmap database backing... + // ensure we were stored to beatmap database backing... Assert.IsTrue(resultSets.Count() == 1, $@"Incorrect result count found ({resultSets.Count()} but should be 1)."); IEnumerable queryBeatmaps() => store.QueryBeatmaps(s => s.BeatmapSet.OnlineBeatmapSetID == 241526 && s.BaseDifficultyID > 0); IEnumerable queryBeatmapSets() => store.QueryBeatmapSets(s => s.OnlineBeatmapSetID == 241526); - //if we don't re-check here, the set will be inserted but the beatmaps won't be present yet. + // if we don't re-check here, the set will be inserted but the beatmaps won't be present yet. waitForOrAssert(() => queryBeatmaps().Count() == 12, @"Beatmaps did not import to the database in allocated time", timeout); waitForOrAssert(() => queryBeatmapSets().Count() == 1, diff --git a/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs b/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs index 158954106d..830e4bc603 100644 --- a/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs +++ b/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs @@ -35,7 +35,7 @@ namespace osu.Game.Tests.NonVisual Assert.That(cpi.TimingPoints.Count, Is.EqualTo(2)); Assert.That(cpi.AllControlPoints.Count(), Is.EqualTo(2)); - cpi.Add(1000, new TimingControlPoint()); //is redundant + cpi.Add(1000, new TimingControlPoint()); // is redundant Assert.That(cpi.Groups.Count, Is.EqualTo(2)); Assert.That(cpi.TimingPoints.Count, Is.EqualTo(2)); diff --git a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs index 7df7df22ea..92a60663de 100644 --- a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs +++ b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs @@ -46,12 +46,12 @@ namespace osu.Game.Tests.NonVisual confirmCurrentFrame(0); confirmNextFrame(1); - //if we hit the first frame perfectly, time should progress to it. + // if we hit the first frame perfectly, time should progress to it. setTime(1000, 1000); confirmCurrentFrame(1); confirmNextFrame(2); - //in between non-important frames should progress based on input. + // in between non-important frames should progress based on input. setTime(1200, 1200); confirmCurrentFrame(1); @@ -144,7 +144,7 @@ namespace osu.Game.Tests.NonVisual confirmCurrentFrame(2); confirmNextFrame(1); - //ensure each frame plays out until start + // ensure each frame plays out until start setTime(-500, 1000); confirmCurrentFrame(1); confirmNextFrame(0); diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 19d1162d23..d7c30dc9ff 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -381,7 +381,7 @@ namespace osu.Game.Beatmaps foreach (var file in files.Where(f => f.Filename.EndsWith(".osu"))) { using (var raw = Files.Store.GetStream(file.FileInfo.StoragePath)) - using (var ms = new MemoryStream()) //we need a memory stream so we can seek + using (var ms = new MemoryStream()) // we need a memory stream so we can seek using (var sr = new LineBufferedReader(ms)) { raw.CopyTo(ms); diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index 590e4b2a5c..27027202ce 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -193,8 +193,8 @@ namespace osu.Game.Graphics.Backgrounds float u1 = 1 - RNG.NextSingle(); //uniform(0,1] random floats float u2 = 1 - RNG.NextSingle(); - float randStdNormal = (float)(Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2)); //random normal(0,1) - var scale = Math.Max(triangleScale * (mean + std_dev * randStdNormal), 0.1f); //random normal(mean,stdDev^2) + float randStdNormal = (float)(Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2)); // random normal(0,1) + var scale = Math.Max(triangleScale * (mean + std_dev * randStdNormal), 0.1f); // random normal(mean,stdDev^2) return new TriangleParticle { Scale = scale }; } diff --git a/osu.Game/Graphics/Containers/OsuScrollContainer.cs b/osu.Game/Graphics/Containers/OsuScrollContainer.cs index 1824fcd878..d504a11b22 100644 --- a/osu.Game/Graphics/Containers/OsuScrollContainer.cs +++ b/osu.Game/Graphics/Containers/OsuScrollContainer.cs @@ -158,7 +158,7 @@ namespace osu.Game.Graphics.Containers { if (!base.OnMouseDown(e)) return false; - //note that we are changing the colour of the box here as to not interfere with the hover effect. + // note that we are changing the colour of the box here as to not interfere with the hover effect. box.FadeColour(highlightColour, 100); return true; } diff --git a/osu.Game/IPC/ArchiveImportIPCChannel.cs b/osu.Game/IPC/ArchiveImportIPCChannel.cs index 484db932f8..029908ec9d 100644 --- a/osu.Game/IPC/ArchiveImportIPCChannel.cs +++ b/osu.Game/IPC/ArchiveImportIPCChannel.cs @@ -32,7 +32,7 @@ namespace osu.Game.IPC { if (importer == null) { - //we want to contact a remote osu! to handle the import. + // we want to contact a remote osu! to handle the import. await SendMessageAsync(new ArchiveImportMessage { Path = path }); return; } diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index adfef1d11f..4945f7f185 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -127,7 +127,7 @@ namespace osu.Game.Online.API case APIState.Offline: case APIState.Connecting: - //work to restore a connection... + // work to restore a connection... if (!HasLogin) { State = APIState.Offline; @@ -180,7 +180,7 @@ namespace osu.Game.Online.API break; } - //hard bail if we can't get a valid access token. + // hard bail if we can't get a valid access token. if (authentication.RequestAccessToken() == null) { Logout(); @@ -274,7 +274,7 @@ namespace osu.Game.Online.API { req.Perform(this); - //we could still be in initialisation, at which point we don't want to say we're Online yet. + // we could still be in initialisation, at which point we don't want to say we're Online yet. if (IsLoggedIn) State = APIState.Online; failureCount = 0; @@ -339,7 +339,7 @@ namespace osu.Game.Online.API log.Add($@"API failure count is now {failureCount}"); if (failureCount < 3) - //we might try again at an api level. + // we might try again at an api level. return false; if (State == APIState.Online) diff --git a/osu.Game/Online/API/APIRequest.cs b/osu.Game/Online/API/APIRequest.cs index 0bba04cac3..0f8acbb7af 100644 --- a/osu.Game/Online/API/APIRequest.cs +++ b/osu.Game/Online/API/APIRequest.cs @@ -98,7 +98,7 @@ namespace osu.Game.Online.API if (checkAndScheduleFailure()) return; - if (!WebRequest.Aborted) //could have been aborted by a Cancel() call + if (!WebRequest.Aborted) // could have been aborted by a Cancel() call { Logger.Log($@"Performing request {this}", LoggingTarget.Network); WebRequest.Perform(); diff --git a/osu.Game/Online/Chat/Channel.cs b/osu.Game/Online/Chat/Channel.cs index 6f67a95f53..dbb2da5c03 100644 --- a/osu.Game/Online/Chat/Channel.cs +++ b/osu.Game/Online/Chat/Channel.cs @@ -61,7 +61,7 @@ namespace osu.Game.Online.Chat /// public event Action MessageRemoved; - public bool ReadOnly => false; //todo not yet used. + public bool ReadOnly => false; // todo: not yet used. public override string ToString() => Name; diff --git a/osu.Game/Online/Chat/MessageFormatter.cs b/osu.Game/Online/Chat/MessageFormatter.cs index 717de18c14..6af2561c89 100644 --- a/osu.Game/Online/Chat/MessageFormatter.cs +++ b/osu.Game/Online/Chat/MessageFormatter.cs @@ -78,13 +78,13 @@ namespace osu.Game.Online.Chat { result.Text = result.Text.Remove(index, m.Length).Insert(index, displayText); - //since we just changed the line display text, offset any already processed links. + // since we just changed the line display text, offset any already processed links. result.Links.ForEach(l => l.Index -= l.Index > index ? m.Length - displayText.Length : 0); var details = GetLinkDetails(linkText); result.Links.Add(new Link(linkText, index, displayText.Length, linkActionOverride ?? details.Action, details.Argument)); - //adjust the offset for processing the current matches group. + // adjust the offset for processing the current matches group. captureOffset += m.Length - displayText.Length; } } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 8e62819c95..fdc8d94352 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -609,7 +609,7 @@ namespace osu.Game loadComponentSingleFile(screenshotManager, Add); - //overlay elements + // overlay elements loadComponentSingleFile(beatmapListing = new BeatmapListingOverlay(), overlayContent.Add, true); loadComponentSingleFile(dashboard = new DashboardOverlay(), overlayContent.Add, true); var rankingsOverlay = loadComponentSingleFile(new RankingsOverlay(), overlayContent.Add, true); @@ -781,7 +781,7 @@ namespace osu.Game { var previousLoadStream = asyncLoadStream; - //chain with existing load stream + // chain with existing load stream asyncLoadStream = Task.Run(async () => { if (previousLoadStream != null) diff --git a/osu.Game/Overlays/BeatmapListing/Panels/GridBeatmapPanel.cs b/osu.Game/Overlays/BeatmapListing/Panels/GridBeatmapPanel.cs index 84d35da096..28c36e6c56 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/GridBeatmapPanel.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/GridBeatmapPanel.cs @@ -35,7 +35,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels : base(beatmap) { Width = 380; - Height = 140 + vertical_padding; //full height of all the elements plus vertical padding (autosize uses the image) + Height = 140 + vertical_padding; // full height of all the elements plus vertical padding (autosize uses the image) } protected override void LoadComplete() diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs index 17fa689cd2..1ff08aab2c 100644 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ b/osu.Game/Overlays/BeatmapSet/Header.cs @@ -140,7 +140,7 @@ namespace osu.Game.Overlays.BeatmapSet { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, - Margin = new MarginPadding { Left = 3, Bottom = 4 }, //To better lineup with the font + Margin = new MarginPadding { Left = 3, Bottom = 4 }, // To better lineup with the font }, } }, diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 34afc3c431..5ba55f6d45 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -358,7 +358,7 @@ namespace osu.Game.Overlays protected override void OnFocus(FocusEvent e) { - //this is necessary as textbox is masked away and therefore can't get focus :( + // this is necessary as textbox is masked away and therefore can't get focus :( textbox.TakeFocus(); base.OnFocus(e); } diff --git a/osu.Game/Overlays/DialogOverlay.cs b/osu.Game/Overlays/DialogOverlay.cs index 59d748bc5d..9f9dbdbaf1 100644 --- a/osu.Game/Overlays/DialogOverlay.cs +++ b/osu.Game/Overlays/DialogOverlay.cs @@ -50,7 +50,7 @@ namespace osu.Game.Overlays { if (v != Visibility.Hidden) return; - //handle the dialog being dismissed. + // handle the dialog being dismissed. dialog.Delay(PopupDialog.EXIT_DURATION).Expire(); if (dialog == CurrentDialog) diff --git a/osu.Game/Overlays/Music/PlaylistItem.cs b/osu.Game/Overlays/Music/PlaylistItem.cs index de2f916946..840fa51b4f 100644 --- a/osu.Game/Overlays/Music/PlaylistItem.cs +++ b/osu.Game/Overlays/Music/PlaylistItem.cs @@ -78,7 +78,7 @@ namespace osu.Game.Overlays.Music { text.Clear(); - //space after the title to put a space between the title and artist + // space after the title to put a space between the title and artist titleSprites = text.AddText(title.Value + @" ", sprite => sprite.Font = OsuFont.GetFont(weight: FontWeight.Regular)).OfType(); text.AddText(artist.Value, sprite => diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index c872f82b32..ded641b262 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -250,7 +250,7 @@ namespace osu.Game.Overlays } else { - //figure out the best direction based on order in playlist. + // figure out the best direction based on order in playlist. var last = BeatmapSets.TakeWhile(b => b.ID != current.BeatmapSetInfo?.ID).Count(); var next = beatmap.NewValue == null ? -1 : BeatmapSets.TakeWhile(b => b.ID != beatmap.NewValue.BeatmapSetInfo?.ID).Count(); diff --git a/osu.Game/Overlays/News/NewsArticleCover.cs b/osu.Game/Overlays/News/NewsArticleCover.cs index cca0cfb4a0..e3f5a8cea3 100644 --- a/osu.Game/Overlays/News/NewsArticleCover.cs +++ b/osu.Game/Overlays/News/NewsArticleCover.cs @@ -162,7 +162,7 @@ namespace osu.Game.Overlays.News public string TooltipText => date.ToString("dddd dd MMMM yyyy hh:mm:ss UTCz").ToUpper(); } - //fake API data struct to use for now as a skeleton for data, as there is no API struct for news article info for now + // fake API data struct to use for now as a skeleton for data, as there is no API struct for news article info for now public class ArticleInfo { public string Title { get; set; } diff --git a/osu.Game/Overlays/Notifications/ProgressNotification.cs b/osu.Game/Overlays/Notifications/ProgressNotification.cs index 99836705c4..3105ecd742 100644 --- a/osu.Game/Overlays/Notifications/ProgressNotification.cs +++ b/osu.Game/Overlays/Notifications/ProgressNotification.cs @@ -39,7 +39,7 @@ namespace osu.Game.Overlays.Notifications { base.LoadComplete(); - //we may have received changes before we were displayed. + // we may have received changes before we were displayed. updateState(); } diff --git a/osu.Game/Overlays/NowPlayingOverlay.cs b/osu.Game/Overlays/NowPlayingOverlay.cs index 118cb037cb..ebb4a96d14 100644 --- a/osu.Game/Overlays/NowPlayingOverlay.cs +++ b/osu.Game/Overlays/NowPlayingOverlay.cs @@ -261,7 +261,7 @@ namespace osu.Game.Overlays // todo: this can likely be replaced with WorkingBeatmap.GetBeatmapAsync() Task.Run(() => { - if (beatmap?.Beatmap == null) //this is not needed if a placeholder exists + if (beatmap?.Beatmap == null) // this is not needed if a placeholder exists { title.Text = @"Nothing to play"; artist.Text = @"Nothing to play"; diff --git a/osu.Game/Overlays/OSD/Toast.cs b/osu.Game/Overlays/OSD/Toast.cs index 5d36cac20e..1497ca8fa8 100644 --- a/osu.Game/Overlays/OSD/Toast.cs +++ b/osu.Game/Overlays/OSD/Toast.cs @@ -31,7 +31,7 @@ namespace osu.Game.Overlays.OSD InternalChildren = new Drawable[] { - new Container //this container exists just to set a minimum width for the toast + new Container // this container exists just to set a minimum width for the toast { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Overlays/Profile/Header/Components/OverlinedInfoContainer.cs b/osu.Game/Overlays/Profile/Header/Components/OverlinedInfoContainer.cs index b11e41f90f..9f56a34aa6 100644 --- a/osu.Game/Overlays/Profile/Header/Components/OverlinedInfoContainer.cs +++ b/osu.Game/Overlays/Profile/Header/Components/OverlinedInfoContainer.cs @@ -54,7 +54,7 @@ namespace osu.Game.Overlays.Profile.Header.Components { Font = OsuFont.GetFont(size: big ? 40 : 18, weight: FontWeight.Light) }, - new Container //Add a minimum size to the FillFlowContainer + new Container // Add a minimum size to the FillFlowContainer { Width = minimumWidth, } diff --git a/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs index a5938a3fe7..e7df4eb5eb 100644 --- a/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs @@ -35,7 +35,7 @@ namespace osu.Game.Overlays.Profile.Header RelativeSizeAxes = Axes.Both, Colour = colourProvider.Background5, }, - new Container //artificial shadow + new Container // artificial shadow { RelativeSizeAxes = Axes.X, Height = 3, diff --git a/osu.Game/Overlays/SearchableList/SearchableListFilterControl.cs b/osu.Game/Overlays/SearchableList/SearchableListFilterControl.cs index 117f905de4..d31470e685 100644 --- a/osu.Game/Overlays/SearchableList/SearchableListFilterControl.cs +++ b/osu.Game/Overlays/SearchableList/SearchableListFilterControl.cs @@ -94,7 +94,7 @@ namespace osu.Game.Overlays.SearchableList RelativeSizeAxes = Axes.X, }, }, - new Box //keep the tab strip part of autosize, but don't put it in the flow container + new Box // keep the tab strip part of autosize, but don't put it in the flow container { RelativeSizeAxes = Axes.X, Height = 1, diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs index d6b810366d..3d66d3c28e 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs @@ -110,7 +110,7 @@ namespace osu.Game.Overlays.Toolbar tooltipContainer = new FillFlowContainer { Direction = FillDirection.Vertical, - RelativeSizeAxes = Axes.Both, //stops us being considered in parent's autosize + RelativeSizeAxes = Axes.Both, // stops us being considered in parent's autosize Anchor = TooltipAnchor.HasFlag(Anchor.x0) ? Anchor.BottomLeft : Anchor.BottomRight, Origin = TooltipAnchor, Position = new Vector2(TooltipAnchor.HasFlag(Anchor.x0) ? 5 : -5, 5), diff --git a/osu.Game/Overlays/VolumeOverlay.cs b/osu.Game/Overlays/VolumeOverlay.cs index b484921cce..676d2c941a 100644 --- a/osu.Game/Overlays/VolumeOverlay.cs +++ b/osu.Game/Overlays/VolumeOverlay.cs @@ -58,7 +58,7 @@ namespace osu.Game.Overlays { volumeMeterEffect = new VolumeMeter("EFFECTS", 125, colours.BlueDarker) { - Margin = new MarginPadding { Top = 100 + MuteButton.HEIGHT } //to counter the mute button and re-center the volume meters + Margin = new MarginPadding { Top = 100 + MuteButton.HEIGHT } // to counter the mute button and re-center the volume meters }, volumeMeterMaster = new VolumeMeter("MASTER", 150, colours.PinkDarker), volumeMeterMusic = new VolumeMeter("MUSIC", 125, colours.BlueDarker), diff --git a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs index 7e17396fde..55d82c4083 100644 --- a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs +++ b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs @@ -62,7 +62,7 @@ namespace osu.Game.Rulesets.Replays { int newFrame = nextFrameIndex; - //ensure we aren't at an extent. + // ensure we aren't at an extent. if (newFrame == currentFrameIndex) return false; currentFrameIndex = newFrame; @@ -99,8 +99,8 @@ namespace osu.Game.Rulesets.Replays if (frame == null) return false; - return IsImportant(frame) && //a button is in a pressed state - Math.Abs(CurrentTime - NextFrame?.Time ?? 0) <= AllowedImportantTimeSpan; //the next frame is within an allowable time span + return IsImportant(frame) && // a button is in a pressed state + Math.Abs(CurrentTime - NextFrame?.Time ?? 0) <= AllowedImportantTimeSpan; // the next frame is within an allowable time span } } diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index 543134cfb4..f302f8700f 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -81,7 +81,7 @@ namespace osu.Game.Rulesets var instances = loadedAssemblies.Values.Select(r => (Ruleset)Activator.CreateInstance(r)).ToList(); - //add all legacy rulesets first to ensure they have exclusive choice of primary key. + // add all legacy rulesets first to ensure they have exclusive choice of primary key. foreach (var r in instances.Where(r => r is ILegacyRuleset)) { if (context.RulesetInfo.SingleOrDefault(dbRuleset => dbRuleset.ID == r.RulesetInfo.ID) == null) @@ -90,7 +90,7 @@ namespace osu.Game.Rulesets context.SaveChanges(); - //add any other modes + // add any other modes foreach (var r in instances.Where(r => !(r is ILegacyRuleset))) { if (context.RulesetInfo.FirstOrDefault(ri => ri.InstantiationInfo == r.RulesetInfo.InstantiationInfo) == null) @@ -99,7 +99,7 @@ namespace osu.Game.Rulesets context.SaveChanges(); - //perform a consistency check + // perform a consistency check foreach (var r in context.RulesetInfo) { try diff --git a/osu.Game/Screens/BackgroundScreen.cs b/osu.Game/Screens/BackgroundScreen.cs index 5dfaceccf5..0f3615b7a9 100644 --- a/osu.Game/Screens/BackgroundScreen.cs +++ b/osu.Game/Screens/BackgroundScreen.cs @@ -30,7 +30,7 @@ namespace osu.Game.Screens protected override bool OnKeyDown(KeyDownEvent e) { - //we don't want to handle escape key. + // we don't want to handle escape key. return false; } diff --git a/osu.Game/Screens/Menu/IntroScreen.cs b/osu.Game/Screens/Menu/IntroScreen.cs index 736202ee52..0d5f3d1142 100644 --- a/osu.Game/Screens/Menu/IntroScreen.cs +++ b/osu.Game/Screens/Menu/IntroScreen.cs @@ -101,7 +101,7 @@ namespace osu.Game.Screens.Menu this.FadeIn(300); double fadeOutTime = exit_delay; - //we also handle the exit transition. + // we also handle the exit transition. if (MenuVoice.Value) seeya.Play(); else diff --git a/osu.Game/Screens/Menu/LogoVisualisation.cs b/osu.Game/Screens/Menu/LogoVisualisation.cs index 67537fa9df..0db7f2a2dc 100644 --- a/osu.Game/Screens/Menu/LogoVisualisation.cs +++ b/osu.Game/Screens/Menu/LogoVisualisation.cs @@ -162,7 +162,7 @@ namespace osu.Game.Screens.Menu private IShader shader; private Texture texture; - //Assuming the logo is a circle, we don't need a second dimension. + // Assuming the logo is a circle, we don't need a second dimension. private float size; private Color4 colour; @@ -209,13 +209,13 @@ namespace osu.Game.Screens.Menu float rotation = MathUtils.DegreesToRadians(i / (float)bars_per_visualiser * 360 + j * 360 / visualiser_rounds); float rotationCos = MathF.Cos(rotation); float rotationSin = MathF.Sin(rotation); - //taking the cos and sin to the 0..1 range + // taking the cos and sin to the 0..1 range var barPosition = new Vector2(rotationCos / 2 + 0.5f, rotationSin / 2 + 0.5f) * size; var barSize = new Vector2(size * MathF.Sqrt(2 * (1 - MathF.Cos(MathUtils.DegreesToRadians(360f / bars_per_visualiser)))) / 2f, bar_length * audioData[i]); - //The distance between the position and the sides of the bar. + // The distance between the position and the sides of the bar. var bottomOffset = new Vector2(-rotationSin * barSize.X / 2, rotationCos * barSize.X / 2); - //The distance between the bottom side of the bar and the top side. + // The distance between the bottom side of the bar and the top side. var amplitudeOffset = new Vector2(rotationCos * barSize.Y, rotationSin * barSize.Y); var rectangle = new Quad( @@ -231,7 +231,7 @@ namespace osu.Game.Screens.Menu colourInfo, null, vertexBatch.AddAction, - //barSize by itself will make it smooth more in the X axis than in the Y axis, this reverts that. + // barSize by itself will make it smooth more in the X axis than in the Y axis, this reverts that. Vector2.Divide(inflation, barSize.Yx)); } } diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 0589e4d12b..f0da2482d6 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -250,7 +250,7 @@ namespace osu.Game.Screens.Menu (Background as BackgroundScreenDefault)?.Next(); - //we may have consumed our preloaded instance, so let's make another. + // we may have consumed our preloaded instance, so let's make another. preloadSongSelect(); if (Beatmap.Value.Track != null && music?.IsUserPaused != true) diff --git a/osu.Game/Screens/Play/BreakOverlay.cs b/osu.Game/Screens/Play/BreakOverlay.cs index c978f4e96d..36f825b8f6 100644 --- a/osu.Game/Screens/Play/BreakOverlay.cs +++ b/osu.Game/Screens/Play/BreakOverlay.cs @@ -119,7 +119,7 @@ namespace osu.Game.Screens.Play FinishTransforms(true); Scheduler.CancelDelayedTasks(); - if (breaks == null) return; //we need breaks. + if (breaks == null) return; // we need breaks. foreach (var b in breaks) { diff --git a/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs b/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs index d201b5d30e..fc80983834 100644 --- a/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs +++ b/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs @@ -50,7 +50,7 @@ namespace osu.Game.Screens.Play.HUD protected override void PopIn() => this.FadeIn(fade_duration); protected override void PopOut() => this.FadeOut(fade_duration); - //We want to handle keyboard inputs all the time in order to trigger ToggleVisibility() when not visible + // We want to handle keyboard inputs all the time in order to trigger ToggleVisibility() when not visible public override bool PropagateNonPositionalInputSubTree => true; protected override bool OnKeyDown(KeyDownEvent e) diff --git a/osu.Game/Screens/Play/KeyCounter.cs b/osu.Game/Screens/Play/KeyCounter.cs index f4109a63d0..98df73a5e6 100644 --- a/osu.Game/Screens/Play/KeyCounter.cs +++ b/osu.Game/Screens/Play/KeyCounter.cs @@ -124,8 +124,8 @@ namespace osu.Game.Screens.Play } } }; - //Set this manually because an element with Alpha=0 won't take it size to AutoSizeContainer, - //so the size can be changing between buttonSprite and glowSprite. + // Set this manually because an element with Alpha=0 won't take it size to AutoSizeContainer, + // so the size can be changing between buttonSprite and glowSprite. Height = buttonSprite.DrawHeight; Width = buttonSprite.DrawWidth; } diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index c0d88feda2..93a734589c 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -314,8 +314,8 @@ namespace osu.Game.Screens.Play LoadTask = null; - //By default, we want to load the player and never be returned to. - //Note that this may change if the player we load requested a re-run. + // By default, we want to load the player and never be returned to. + // Note that this may change if the player we load requested a re-run. ValidForResume = false; if (player.LoadedBeatmapSuccessfully) @@ -360,7 +360,7 @@ namespace osu.Game.Screens.Play { if (!muteWarningShownOnce.Value) { - //Checks if the notification has not been shown yet and also if master volume is muted, track/music volume is muted or if the whole game is muted. + // Checks if the notification has not been shown yet and also if master volume is muted, track/music volume is muted or if the whole game is muted. if (volumeOverlay?.IsMuted.Value == true || audioManager.Volume.Value <= audioManager.Volume.MinValue || audioManager.VolumeTrack.Value <= audioManager.VolumeTrack.MinValue) { notificationOverlay?.Post(new MutedNotification()); diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 5a4a03662a..96b779cd20 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -208,7 +208,7 @@ namespace osu.Game.Screens.Select // without this, during a large beatmap import it is impossible to navigate the carousel. applyActiveCriteria(false, alwaysResetScrollPosition: false); - //check if we can/need to maintain our current selection. + // check if we can/need to maintain our current selection. if (previouslySelectedID != null) select((CarouselItem)newSet.Beatmaps.FirstOrDefault(b => b.Beatmap.ID == previouslySelectedID) ?? newSet); diff --git a/osu.Game/Screens/Select/BeatmapDetailAreaTabControl.cs b/osu.Game/Screens/Select/BeatmapDetailAreaTabControl.cs index f4bf1ab059..63711e3e50 100644 --- a/osu.Game/Screens/Select/BeatmapDetailAreaTabControl.cs +++ b/osu.Game/Screens/Select/BeatmapDetailAreaTabControl.cs @@ -26,7 +26,7 @@ namespace osu.Game.Screens.Select set => tabs.Current = value; } - public Action OnFilter; //passed the selected tab and if mods is checked + public Action OnFilter; // passed the selected tab and if mods is checked public IReadOnlyList TabItems { diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index aebb8e9d87..9669a1391c 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -201,7 +201,7 @@ namespace osu.Game.Screens.Select Schedule(() => { if (beatmap != requestedBeatmap) - //the beatmap has been changed since we started the lookup. + // the beatmap has been changed since we started the lookup. return; var b = res.ToBeatmap(rulesets); @@ -222,7 +222,7 @@ namespace osu.Game.Screens.Select Schedule(() => { if (beatmap != requestedBeatmap) - //the beatmap has been changed since we started the lookup. + // the beatmap has been changed since we started the lookup. return; updateMetrics(); diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index af0d36ea9a..02822ea608 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -52,7 +52,7 @@ namespace osu.Game.Screens.Select.Details AutoSizeAxes = Axes.Y, Children = new[] { - FirstValue = new StatisticRow(), //circle size/key amount + FirstValue = new StatisticRow(), // circle size/key amount HpDrain = new StatisticRow { Title = "HP Drain" }, Accuracy = new StatisticRow { Title = "Accuracy" }, ApproachRate = new StatisticRow { Title = "Approach Rate" }, diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index a7e27c27ba..5b7c9082f3 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -798,7 +798,7 @@ namespace osu.Game.Screens.Select Masking = true; Anchor = Anchor.Centre; Origin = Anchor.Centre; - Width = panel_overflow; //avoid horizontal masking so the panels don't clip when screen stack is pushed. + Width = panel_overflow; // avoid horizontal masking so the panels don't clip when screen stack is pushed. InternalChild = Content = new Container { RelativeSizeAxes = Axes.Both, From aff74db80da54a35a438a4e6443569107e5403a1 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Tue, 5 May 2020 10:40:10 +0200 Subject: [PATCH 0596/1117] Publicly expose HUDOverlay in Player. --- 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 ece4c6307e..af724d97a2 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -89,7 +89,7 @@ namespace osu.Game.Screens.Play protected DrawableRuleset DrawableRuleset { get; private set; } - protected HUDOverlay HUDOverlay { get; private set; } + public HUDOverlay HUDOverlay { get; private set; } public bool LoadedBeatmapSuccessfully => DrawableRuleset?.Objects.Any() == true; From 7781408643a682358c463077bf76a6d35a4bc1c7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 May 2020 18:27:10 +0900 Subject: [PATCH 0597/1117] Update in line with framework storage changes --- osu.Desktop/OsuGameDesktop.cs | 78 +++++++++++------------ osu.Game.Tournament/IPC/FileBasedIPC.cs | 84 +++++++++++-------------- osu.Game/IO/OsuStorage.cs | 26 ++++++++ osu.Game/IO/WrappedStorage.cs | 80 +++++++++++++++++++++++ osu.Game/OsuGameBase.cs | 26 +------- 5 files changed, 179 insertions(+), 115 deletions(-) create mode 100644 osu.Game/IO/OsuStorage.cs create mode 100644 osu.Game/IO/WrappedStorage.cs diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index f05ee48914..9351e17419 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -6,15 +6,14 @@ using System.IO; using System.Linq; using System.Reflection; using System.Threading.Tasks; +using Microsoft.Win32; using osu.Desktop.Overlays; using osu.Framework.Platform; using osu.Game; using osuTK.Input; -using Microsoft.Win32; using osu.Desktop.Updater; using osu.Framework; using osu.Framework.Logging; -using osu.Framework.Platform.Windows; using osu.Framework.Screens; using osu.Game.Screens.Menu; using osu.Game.Updater; @@ -37,7 +36,11 @@ namespace osu.Desktop try { if (Host is DesktopGameHost desktopHost) - return new StableStorage(desktopHost); + { + string stablePath = getStableInstallPath(); + if (!string.IsNullOrEmpty(stablePath)) + return new DesktopStorage(stablePath, desktopHost); + } } catch (Exception) { @@ -47,6 +50,35 @@ namespace osu.Desktop return null; } + private string getStableInstallPath() + { + static bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs")); + + string stableInstallPath; + + try + { + using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu")) + stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty).ToString().Split('"')[1].Replace("osu!.exe", ""); + + if (checkExists(stableInstallPath)) + return stableInstallPath; + } + catch + { + } + + stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!"); + if (checkExists(stableInstallPath)) + return stableInstallPath; + + stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".osu"); + if (checkExists(stableInstallPath)) + return stableInstallPath; + + return null; + } + protected override UpdateManager CreateUpdateManager() { switch (RuntimeInfo.OS) @@ -111,45 +143,5 @@ namespace osu.Desktop Task.Factory.StartNew(() => Import(filePaths), TaskCreationOptions.LongRunning); } - - /// - /// A method of accessing an osu-stable install in a controlled fashion. - /// - private class StableStorage : WindowsStorage - { - protected override string LocateBasePath() - { - static bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs")); - - string stableInstallPath; - - try - { - using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu")) - stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty).ToString().Split('"')[1].Replace("osu!.exe", ""); - - if (checkExists(stableInstallPath)) - return stableInstallPath; - } - catch - { - } - - stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!"); - if (checkExists(stableInstallPath)) - return stableInstallPath; - - stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".osu"); - if (checkExists(stableInstallPath)) - return stableInstallPath; - - return null; - } - - public StableStorage(DesktopGameHost host) - : base(string.Empty, host) - { - } - } } } diff --git a/osu.Game.Tournament/IPC/FileBasedIPC.cs b/osu.Game.Tournament/IPC/FileBasedIPC.cs index eefa9fcfe6..53ba597a7e 100644 --- a/osu.Game.Tournament/IPC/FileBasedIPC.cs +++ b/osu.Game.Tournament/IPC/FileBasedIPC.cs @@ -8,7 +8,6 @@ using Microsoft.Win32; using osu.Framework.Allocation; using osu.Framework.Logging; using osu.Framework.Platform; -using osu.Framework.Platform.Windows; using osu.Framework.Threading; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Legacy; @@ -52,7 +51,12 @@ namespace osu.Game.Tournament.IPC try { - Storage = new StableStorage(host as DesktopGameHost); + var path = findStablePath(); + + if (string.IsNullOrEmpty(path)) + return null; + + Storage = new DesktopStorage(path, host as DesktopGameHost); const string file_ipc_filename = "ipc.txt"; const string file_ipc_state_filename = "ipc-state.txt"; @@ -145,64 +149,50 @@ namespace osu.Game.Tournament.IPC return Storage; } - /// - /// A method of accessing an osu-stable install in a controlled fashion. - /// - private class StableStorage : WindowsStorage + private string findStablePath() { - protected override string LocateBasePath() - { - static bool checkExists(string p) - { - return File.Exists(Path.Combine(p, "ipc.txt")); - } + static bool checkExists(string p) => File.Exists(Path.Combine(p, "ipc.txt")); - string stableInstallPath = string.Empty; + string stableInstallPath = string.Empty; + + try + { + try + { + stableInstallPath = Environment.GetEnvironmentVariable("OSU_STABLE_PATH"); + + if (checkExists(stableInstallPath)) + return stableInstallPath; + } + catch + { + } try { - try - { - stableInstallPath = Environment.GetEnvironmentVariable("OSU_STABLE_PATH"); + using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu")) + stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty).ToString().Split('"')[1].Replace("osu!.exe", ""); - if (checkExists(stableInstallPath)) - return stableInstallPath; - } - catch - { - } - - try - { - using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu")) - stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty).ToString().Split('"')[1].Replace("osu!.exe", ""); - - if (checkExists(stableInstallPath)) - return stableInstallPath; - } - catch - { - } - - stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!"); if (checkExists(stableInstallPath)) return stableInstallPath; - - stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".osu"); - if (checkExists(stableInstallPath)) - return stableInstallPath; - - return null; } - finally + catch { - Logger.Log($"Stable path for tourney usage: {stableInstallPath}"); } - } - public StableStorage(DesktopGameHost host) - : base(string.Empty, host) + stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!"); + if (checkExists(stableInstallPath)) + return stableInstallPath; + + stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".osu"); + if (checkExists(stableInstallPath)) + return stableInstallPath; + + return null; + } + finally { + Logger.Log($"Stable path for tourney usage: {stableInstallPath}"); } } } diff --git a/osu.Game/IO/OsuStorage.cs b/osu.Game/IO/OsuStorage.cs new file mode 100644 index 0000000000..ee42c491d1 --- /dev/null +++ b/osu.Game/IO/OsuStorage.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.Logging; +using osu.Framework.Platform; +using osu.Game.Configuration; + +namespace osu.Game.IO +{ + public class OsuStorage : WrappedStorage + { + public OsuStorage(GameHost host) + : base(host.Storage, string.Empty) + { + var storageConfig = new StorageConfigManager(host.Storage); + + var customStoragePath = storageConfig.Get(StorageConfig.FullPath); + + if (!string.IsNullOrEmpty(customStoragePath)) + { + ChangeTargetStorage(host.GetStorage(customStoragePath)); + Logger.Storage = UnderlyingStorage.GetStorageForDirectory("logs"); + } + } + } +} diff --git a/osu.Game/IO/WrappedStorage.cs b/osu.Game/IO/WrappedStorage.cs new file mode 100644 index 0000000000..705bbf6840 --- /dev/null +++ b/osu.Game/IO/WrappedStorage.cs @@ -0,0 +1,80 @@ +// 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 osu.Framework.Platform; + +namespace osu.Game.IO +{ + /// + /// A storage which wraps another storage and delegates implementation, potentially mutating the lookup path. + /// + public class WrappedStorage : Storage + { + protected Storage UnderlyingStorage { get; private set; } + + private readonly string subPath; + + public WrappedStorage(Storage underlyingStorage, string subPath = null) + : base(string.Empty) + { + ChangeTargetStorage(underlyingStorage); + + this.subPath = subPath; + } + + protected virtual string MutatePath(string path) => !string.IsNullOrEmpty(subPath) ? Path.Combine(subPath, path) : path; + + protected void ChangeTargetStorage(Storage newStorage) + { + UnderlyingStorage = newStorage; + } + + public override string GetFullPath(string path, bool createIfNotExisting = false) => + UnderlyingStorage.GetFullPath(MutatePath(path), createIfNotExisting); + + public override bool Exists(string path) => + UnderlyingStorage.Exists(MutatePath(path)); + + public override bool ExistsDirectory(string path) => + UnderlyingStorage.ExistsDirectory(MutatePath(path)); + + public override void DeleteDirectory(string path) => + UnderlyingStorage.DeleteDirectory(MutatePath(path)); + + public override void Delete(string path) => + UnderlyingStorage.Delete(MutatePath(path)); + + public override IEnumerable GetDirectories(string path) => + UnderlyingStorage.GetDirectories(MutatePath(path)); + + public override IEnumerable GetFiles(string path, string pattern = "*") => + UnderlyingStorage.GetFiles(MutatePath(path), pattern); + + public override Stream GetStream(string path, FileAccess access = FileAccess.Read, FileMode mode = FileMode.OpenOrCreate) => + UnderlyingStorage.GetStream(MutatePath(path), access, mode); + + public override string GetDatabaseConnectionString(string name) => + UnderlyingStorage.GetDatabaseConnectionString(MutatePath(name)); + + public override void DeleteDatabase(string name) => UnderlyingStorage.DeleteDatabase(MutatePath(name)); + + public override void OpenInNativeExplorer() => UnderlyingStorage.OpenInNativeExplorer(); + + public override Storage GetStorageForDirectory(string path) + { + if (string.IsNullOrEmpty(path)) + throw new ArgumentException("Must be non-null and not empty string", nameof(path)); + + if (!path.EndsWith(Path.DirectorySeparatorChar)) + path += Path.DirectorySeparatorChar; + + // create non-existing path. + GetFullPath(path, true); + + return new WrappedStorage(this, path); + } + } +} diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index f92db4e111..d9f9e2de42 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -71,8 +71,6 @@ namespace osu.Game protected MenuCursorContainer MenuCursorContainer; - protected StorageConfigManager StorageConfig; - private Container content; protected override Container Content => content; @@ -304,17 +302,7 @@ namespace osu.Game { base.SetHost(host); - StorageConfig = new StorageConfigManager(host.Storage); - - var customStoragePath = StorageConfig.Get(Configuration.StorageConfig.FullPath); - - if (!string.IsNullOrEmpty(customStoragePath)) - { - Storage = new CustomStorage(customStoragePath, host); - Logger.Storage = Storage.GetStorageForDirectory("logs"); - } - else - Storage = host.Storage; + Storage = new OsuStorage(host); if (LocalConfig == null) LocalConfig = new OsuConfigManager(Storage); @@ -366,17 +354,5 @@ namespace osu.Game public override bool ChangeFocusOnClick => false; } } - - /// - /// A storage pointing to an absolute location specified by the user to store game data files. - /// - private class CustomStorage : NativeStorage - { - public CustomStorage(string fullPath, GameHost host) - : base(string.Empty, host) - { - BasePath = fullPath; - } - } } } From ed83ac188e20c2b9e6e00de7083b90e1974dc9ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 6 May 2020 23:25:25 +0200 Subject: [PATCH 0598/1117] Remove special case for moving catcher sprite --- .../TestSceneHyperDashColouring.cs | 12 +----------- osu.Game.Rulesets.Catch/UI/Catcher.cs | 17 +---------------- 2 files changed, 2 insertions(+), 27 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs index 589bafe400..1e708cce4b 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs @@ -128,17 +128,7 @@ namespace osu.Game.Rulesets.Catch.Tests catcherArea.MovableCatcher.SetHyperDashState(2); }); - AddUntilStep("catcher colour is correct", () => - { - var expected = expectedCatcherColour; - - if (expected == Catcher.DEFAULT_HYPER_DASH_COLOUR) - // The expected colour for Catcher.Colour is another colour - // for the default skin, assert with that instead. - expected = Catcher.DEFAULT_CATCHER_HYPER_DASH_COLOUR; - - return catcherArea.MovableCatcher.Colour == expected; - }); + AddUntilStep("catcher colour is correct", () => catcherArea.MovableCatcher.Colour == expectedCatcherColour); AddAssert("catcher trails colours are correct", () => trails.HyperDashTrailsColour == expectedCatcherColour); AddAssert("catcher end-glow colours are correct", () => trails.EndGlowSpritesColour == (expectedEndGlowColour ?? expectedCatcherColour)); diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 2022cffb40..520cfeee70 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -28,15 +28,6 @@ namespace osu.Game.Rulesets.Catch.UI ///
  • public static readonly Color4 DEFAULT_HYPER_DASH_COLOUR = Color4.Red; - /// - /// The default hyper-dash colour used directly for this - /// 's . - /// - /// - /// This colour is only used when no skin overrides . - /// - public static readonly Color4 DEFAULT_CATCHER_HYPER_DASH_COLOUR = Color4.OrangeRed; - /// /// The duration between transitioning to hyper-dash state. /// @@ -288,13 +279,7 @@ namespace osu.Game.Rulesets.Catch.UI { if (hyperDashing) { - // special behaviour for catcher colour if no skin overrides. - var catcherColour = - hyperDashColour == DEFAULT_HYPER_DASH_COLOUR - ? DEFAULT_CATCHER_HYPER_DASH_COLOUR - : hyperDashColour; - - this.FadeColour(catcherColour, HYPER_DASH_TRANSITION_DURATION, Easing.OutQuint); + this.FadeColour(hyperDashColour, HYPER_DASH_TRANSITION_DURATION, Easing.OutQuint); this.FadeTo(0.2f, HYPER_DASH_TRANSITION_DURATION, Easing.OutQuint); } else From 52d1e2b5f889dd193aad2c10b0f3b40f87e24f8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 6 May 2020 23:27:01 +0200 Subject: [PATCH 0599/1117] Improve xmldoc --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 520cfeee70..40c7d6a9b5 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -24,7 +24,8 @@ namespace osu.Game.Rulesets.Catch.UI public class Catcher : SkinReloadableDrawable, IKeyBindingHandler { /// - /// The default colour used for all hyper-dashing components. (catcher drawables and fruit) + /// The default colour used to tint hyper-dash fruit, along with the moving catcher, its trail + /// and end glow/after-image during a hyper-dash. /// public static readonly Color4 DEFAULT_HYPER_DASH_COLOUR = Color4.Red; From 25f73c0b9f30d2c3dc129d02f0bd8a61e738374c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 6 May 2020 23:40:36 +0200 Subject: [PATCH 0600/1117] Add [NotNull] annotation --- osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs index afbfac9a51..64fb4b2196 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using JetBrains.Annotations; using osu.Framework.Graphics; using osu.Framework.Graphics.Animations; using osu.Framework.Graphics.Containers; @@ -73,7 +74,7 @@ namespace osu.Game.Rulesets.Catch.UI } } - public CatcherTrailDisplay(Catcher catcher) + public CatcherTrailDisplay([NotNull] Catcher catcher) { this.catcher = catcher ?? throw new ArgumentNullException(nameof(catcher)); From b44a70ef9ab7240d3a9a285654996889e8e67910 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 7 May 2020 01:46:37 +0300 Subject: [PATCH 0601/1117] Let the catcher be responsible for stopping the trails --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 2 +- osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 40c7d6a9b5..558555af96 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -74,7 +74,7 @@ namespace osu.Game.Rulesets.Catch.UI dashing = value; - trails.DisplayTrail |= dashing; + trails.DisplayTrail = value || HyperDashing; } } diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs index 64fb4b2196..bab3cb748b 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs @@ -103,11 +103,8 @@ namespace osu.Game.Rulesets.Catch.UI private void displayTrail() { - if (!catcher.Dashing && !catcher.HyperDashing) - { - DisplayTrail = false; + if (!DisplayTrail) return; - } var sprite = createTrailSprite(catcher.HyperDashing ? hyperDashTrails : dashTrails); From 5186da8412ded39b4216cab60c0858fe05b42f71 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 7 May 2020 11:37:04 +0900 Subject: [PATCH 0602/1117] Fix potential song select nullref --- osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs index 6d760df065..1e4f6aeda1 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs @@ -61,7 +61,7 @@ namespace osu.Game.Screens.Select.Carousel terms.Add(Beatmap.Version); foreach (var criteriaTerm in criteria.SearchTerms) - match &= terms.Any(term => term.IndexOf(criteriaTerm, StringComparison.InvariantCultureIgnoreCase) >= 0); + match &= terms.Any(term => term?.IndexOf(criteriaTerm, StringComparison.InvariantCultureIgnoreCase) >= 0); } Filtered.Value = !match; From e91e4a73af89e719e05c1f349836433079a2523e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 7 May 2020 12:22:07 +0900 Subject: [PATCH 0603/1117] Fix catch crashing when finishing maps --- .../Scoring/Legacy/ScoreInfoExtensions.cs | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/osu.Game/Scoring/Legacy/ScoreInfoExtensions.cs b/osu.Game/Scoring/Legacy/ScoreInfoExtensions.cs index 9745d1abef..6f73a284a2 100644 --- a/osu.Game/Scoring/Legacy/ScoreInfoExtensions.cs +++ b/osu.Game/Scoring/Legacy/ScoreInfoExtensions.cs @@ -12,7 +12,7 @@ namespace osu.Game.Scoring.Legacy switch (scoreInfo.Ruleset?.ID ?? scoreInfo.RulesetID) { case 3: - return scoreInfo.Statistics[HitResult.Perfect]; + return getCount(scoreInfo, HitResult.Perfect); } return null; @@ -35,10 +35,10 @@ namespace osu.Game.Scoring.Legacy case 0: case 1: case 3: - return scoreInfo.Statistics[HitResult.Great]; + return getCount(scoreInfo, HitResult.Great); case 2: - return scoreInfo.Statistics[HitResult.Perfect]; + return getCount(scoreInfo, HitResult.Perfect); } return null; @@ -65,10 +65,10 @@ namespace osu.Game.Scoring.Legacy switch (scoreInfo.Ruleset?.ID ?? scoreInfo.RulesetID) { case 3: - return scoreInfo.Statistics[HitResult.Good]; + return getCount(scoreInfo, HitResult.Good); case 2: - return scoreInfo.Statistics[HitResult.SmallTickMiss]; + return getCount(scoreInfo, HitResult.SmallTickMiss); } return null; @@ -94,13 +94,13 @@ namespace osu.Game.Scoring.Legacy { case 0: case 1: - return scoreInfo.Statistics[HitResult.Good]; + return getCount(scoreInfo, HitResult.Good); case 3: - return scoreInfo.Statistics[HitResult.Ok]; + return getCount(scoreInfo, HitResult.Ok); case 2: - return scoreInfo.Statistics[HitResult.LargeTickHit]; + return getCount(scoreInfo, HitResult.LargeTickHit); } return null; @@ -131,10 +131,10 @@ namespace osu.Game.Scoring.Legacy { case 0: case 3: - return scoreInfo.Statistics[HitResult.Meh]; + return getCount(scoreInfo, HitResult.Meh); case 2: - return scoreInfo.Statistics[HitResult.SmallTickHit]; + return getCount(scoreInfo, HitResult.SmallTickHit); } return null; @@ -156,9 +156,17 @@ namespace osu.Game.Scoring.Legacy } public static int? GetCountMiss(this ScoreInfo scoreInfo) => - scoreInfo.Statistics[HitResult.Miss]; + getCount(scoreInfo, HitResult.Miss); public static void SetCountMiss(this ScoreInfo scoreInfo, int value) => scoreInfo.Statistics[HitResult.Miss] = value; + + private static int? getCount(ScoreInfo scoreInfo, HitResult result) + { + if (scoreInfo.Statistics.TryGetValue(result, out var existing)) + return existing; + + return null; + } } } From 401c516503239f341c73396e5539e7b78f2b1078 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 7 May 2020 13:04:08 +0900 Subject: [PATCH 0604/1117] Expose searchable terms from beatmap info instead --- osu.Game/Beatmaps/BeatmapInfo.cs | 5 +++++ osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs | 8 ++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 90c100db05..3860f12baa 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -149,6 +149,11 @@ namespace osu.Game.Beatmaps } } + public string[] SearchableTerms => new[] + { + Version + }.Concat(Metadata?.SearchableTerms ?? Enumerable.Empty()).Where(s => !string.IsNullOrEmpty(s)).ToArray(); + public override string ToString() { string version = string.IsNullOrEmpty(Version) ? string.Empty : $"[{Version}]"; diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs index 1e4f6aeda1..ed54c158db 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.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.Screens.Select.Filter; @@ -55,13 +54,10 @@ namespace osu.Game.Screens.Select.Carousel if (match) { - var terms = new List(); - - terms.AddRange(Beatmap.Metadata.SearchableTerms); - terms.Add(Beatmap.Version); + var terms = Beatmap.SearchableTerms; foreach (var criteriaTerm in criteria.SearchTerms) - match &= terms.Any(term => term?.IndexOf(criteriaTerm, StringComparison.InvariantCultureIgnoreCase) >= 0); + match &= terms.Any(term => term.IndexOf(criteriaTerm, StringComparison.InvariantCultureIgnoreCase) >= 0); } Filtered.Value = !match; From 259ef688110bfbc6e9eed2f7b8779af8b65bbb3e Mon Sep 17 00:00:00 2001 From: Joehu Date: Wed, 6 May 2020 22:29:37 -0700 Subject: [PATCH 0605/1117] Fix date tooltip not showing in 24-hour format --- osu.Game/Graphics/DrawableDate.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/DrawableDate.cs b/osu.Game/Graphics/DrawableDate.cs index 8c520f4e10..8b6df4a834 100644 --- a/osu.Game/Graphics/DrawableDate.cs +++ b/osu.Game/Graphics/DrawableDate.cs @@ -139,7 +139,7 @@ namespace osu.Game.Graphics return false; dateText.Text = $"{date:d MMMM yyyy} "; - timeText.Text = $"{date:hh:mm:ss \"UTC\"z}"; + timeText.Text = $"{date:HH:mm:ss \"UTC\"z}"; return true; } From 09759565faee8f9f96fbc48aacaa7a8ad7842eb1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 May 2020 14:49:58 +0900 Subject: [PATCH 0606/1117] Add support for 3v3 tournament chroma key layout --- osu.Game.Tournament/Models/LadderInfo.cs | 6 ++ .../Screens/Gameplay/GameplayScreen.cs | 83 +++++++++++++++++-- 2 files changed, 82 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tournament/Models/LadderInfo.cs b/osu.Game.Tournament/Models/LadderInfo.cs index c2e6da9ca5..7794019437 100644 --- a/osu.Game.Tournament/Models/LadderInfo.cs +++ b/osu.Game.Tournament/Models/LadderInfo.cs @@ -32,5 +32,11 @@ namespace osu.Game.Tournament.Models MinValue = 640, MaxValue = 1366, }; + + public Bindable PlayersPerTeam = new BindableInt(4) + { + MinValue = 3, + MaxValue = 4, + }; } } diff --git a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs index 64a5cd6dec..e4e3842369 100644 --- a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs +++ b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs @@ -36,7 +36,7 @@ namespace osu.Game.Tournament.Screens.Gameplay [Resolved] private TournamentMatchChatDisplay chat { get; set; } - private Box chroma; + private Drawable chroma; [BackgroundDependencyLoader] private void load(LadderInfo ladder, MatchIPCInfo ipc, Storage storage) @@ -61,16 +61,30 @@ namespace osu.Game.Tournament.Screens.Gameplay Y = 110, Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Children = new Drawable[] + Children = new[] { - chroma = new Box + chroma = new Container { - // chroma key area for stable gameplay - Name = "chroma", Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Height = 512, - Colour = new Color4(0, 255, 0, 255), + Children = new Drawable[] + { + new ChromaArea + { + Name = "Left chroma", + RelativeSizeAxes = Axes.Both, + Width = 0.5f, + }, + new ChromaArea + { + Name = "Right chroma", + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Width = 0.5f, + } + } }, } }, @@ -98,9 +112,15 @@ namespace osu.Game.Tournament.Screens.Gameplay }, new SettingsSlider { - LabelText = "Chroma Width", + LabelText = "Chroma width", Bindable = LadderInfo.ChromaKeyWidth, KeyboardStep = 1, + }, + new SettingsSlider + { + LabelText = "Players per team", + Bindable = LadderInfo.PlayersPerTeam, + KeyboardStep = 1, } } } @@ -201,5 +221,54 @@ namespace osu.Game.Tournament.Screens.Gameplay lastState = state.NewValue; } } + + private class ChromaArea : CompositeDrawable + { + [Resolved] + private LadderInfo ladder { get; set; } + + [BackgroundDependencyLoader] + private void load() + { + // chroma key area for stable gameplay + Colour = new Color4(0, 255, 0, 255); + + ladder.PlayersPerTeam.BindValueChanged(performLayout, true); + } + + private void performLayout(ValueChangedEvent playerCount) + { + switch (playerCount.NewValue) + { + case 3: + InternalChildren = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Width = 0.5f, + Height = 0.5f, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + }, + new Box + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Height = 0.5f, + }, + }; + break; + + default: + InternalChild = new Box + { + RelativeSizeAxes = Axes.Both, + }; + break; + } + } + } } } From 83be5455d3529707cbac2192e538984fc351eea4 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Thu, 7 May 2020 08:52:36 +0200 Subject: [PATCH 0607/1117] Disable the display of HUD through DisplayHud property. --- osu.Game/Rulesets/UI/DrawableRuleset.cs | 9 +++++++++ osu.Game/Screens/Play/Player.cs | 9 ++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index 5062c92afe..ff1f67783e 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -487,6 +487,15 @@ namespace osu.Game.Rulesets.UI protected virtual ResumeOverlay CreateResumeOverlay() => null; + /// + /// Whether to display the HUD with this ruleset. + /// Override to false to completely disable the display of the HUD with this ruleset. + /// + /// + /// HUD refers here to in player as well as . + /// + public virtual bool DisplayHud => true; + /// /// Sets a replay to be used, overriding local input. /// diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index af724d97a2..375976ea6c 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -89,7 +89,7 @@ namespace osu.Game.Screens.Play protected DrawableRuleset DrawableRuleset { get; private set; } - public HUDOverlay HUDOverlay { get; private set; } + protected HUDOverlay HUDOverlay { get; private set; } public bool LoadedBeatmapSuccessfully => DrawableRuleset?.Objects.Any() == true; @@ -184,6 +184,13 @@ namespace osu.Game.Screens.Play addGameplayComponents(GameplayClockContainer, Beatmap.Value, playableBeatmap); addOverlayComponents(GameplayClockContainer, Beatmap.Value); + if (!DrawableRuleset.DisplayHud) + { + HUDOverlay.ShowHud.Value = false; + HUDOverlay.ShowHud.Disabled = true; + BreakOverlay.Hide(); + } + DrawableRuleset.HasReplayLoaded.BindValueChanged(_ => updatePauseOnFocusLostState(), true); // bind clock into components that require it From 83998d5ba5a661a284bb19b54d711f8bc583f6ad Mon Sep 17 00:00:00 2001 From: Lucas A Date: Thu, 7 May 2020 09:39:14 +0200 Subject: [PATCH 0608/1117] Trim whitespace. --- osu.Game/Rulesets/UI/DrawableRuleset.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index ff1f67783e..57fbb7f1a5 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -489,7 +489,7 @@ namespace osu.Game.Rulesets.UI /// /// Whether to display the HUD with this ruleset. - /// Override to false to completely disable the display of the HUD with this ruleset. + /// Override to false to completely disable the display of the HUD with this ruleset. /// /// /// HUD refers here to in player as well as . From 90e17853a80af4b970a1cae6481c287c124d6fef Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 May 2020 18:12:11 +0900 Subject: [PATCH 0609/1117] Update tests in line with framework changes --- osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs index 2d5f1f238f..b82339281e 100644 --- a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs +++ b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs @@ -25,7 +25,7 @@ namespace osu.Game.Tests.NonVisual var osu = loadOsu(host); var storage = osu.Dependencies.Get(); - string defaultStorageLocation = Path.Combine(Environment.CurrentDirectory, $"headless-{nameof(TestDefaultDirectory)}"); + string defaultStorageLocation = Path.Combine(Environment.CurrentDirectory, $"headless", nameof(TestDefaultDirectory)); Assert.That(storage.GetFullPath("."), Is.EqualTo(defaultStorageLocation)); } @@ -41,7 +41,7 @@ namespace osu.Game.Tests.NonVisual { using (var host = new HeadlessGameHost(nameof(TestCustomDirectory))) { - string headlessPrefix = $"headless-{nameof(TestCustomDirectory)}"; + string headlessPrefix = Path.Combine("headless", nameof(TestCustomDirectory)); // need access before the game has constructed its own storage yet. Storage storage = new DesktopStorage(headlessPrefix, host); From d21c42a222ffe2f569b66fa7a5deb177374e247a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 May 2020 20:59:29 +0900 Subject: [PATCH 0610/1117] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 8214fa2f2c..af699af1ba 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 91c89cbc20..397d48f7b8 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -24,7 +24,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 9ff7e3fc02..036f87541f 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -80,7 +80,7 @@ - + From 4ac5ed71f4f82b1f9a4b47677e30e00ac3e230d7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 May 2020 21:48:57 +0900 Subject: [PATCH 0611/1117] Remove redundant string interpolation --- osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs index b82339281e..d741bc5de1 100644 --- a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs +++ b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs @@ -25,7 +25,7 @@ namespace osu.Game.Tests.NonVisual var osu = loadOsu(host); var storage = osu.Dependencies.Get(); - string defaultStorageLocation = Path.Combine(Environment.CurrentDirectory, $"headless", nameof(TestDefaultDirectory)); + string defaultStorageLocation = Path.Combine(Environment.CurrentDirectory, "headless", nameof(TestDefaultDirectory)); Assert.That(storage.GetFullPath("."), Is.EqualTo(defaultStorageLocation)); } From 754afb9c0bbd5d049352c455ec1122de69a9a9fa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 May 2020 18:31:17 +0900 Subject: [PATCH 0612/1117] Expose ContextFactory to allow for connection flushing --- osu.Game/OsuGameBase.cs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index d9f9e2de42..d0c06df8ea 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -121,7 +121,7 @@ namespace osu.Game protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); - private DatabaseContextFactory contextFactory; + protected DatabaseContextFactory ContextFactory; protected override UserInputManager CreateUserInputManager() => new OsuUserInputManager(); @@ -130,7 +130,7 @@ namespace osu.Game { Resources.AddStore(new DllResourceStore(OsuResources.ResourceAssembly)); - dependencies.Cache(contextFactory = new DatabaseContextFactory(Storage)); + dependencies.Cache(ContextFactory = new DatabaseContextFactory(Storage)); dependencies.CacheAs(Storage); @@ -161,7 +161,7 @@ namespace osu.Game runMigrations(); - dependencies.Cache(SkinManager = new SkinManager(Storage, contextFactory, Host, Audio, new NamespacedResourceStore(Resources, "Skins/Legacy"))); + dependencies.Cache(SkinManager = new SkinManager(Storage, ContextFactory, Host, Audio, new NamespacedResourceStore(Resources, "Skins/Legacy"))); dependencies.CacheAs(SkinManager); if (API == null) API = new APIAccess(LocalConfig); @@ -170,12 +170,12 @@ namespace osu.Game var defaultBeatmap = new DummyWorkingBeatmap(Audio, Textures); - dependencies.Cache(RulesetStore = new RulesetStore(contextFactory, Storage)); - dependencies.Cache(FileStore = new FileStore(contextFactory, Storage)); + dependencies.Cache(RulesetStore = new RulesetStore(ContextFactory, Storage)); + dependencies.Cache(FileStore = new FileStore(ContextFactory, Storage)); // ordering is important here to ensure foreign keys rules are not broken in ModelStore.Cleanup() - dependencies.Cache(ScoreManager = new ScoreManager(RulesetStore, () => BeatmapManager, Storage, API, contextFactory, Host)); - dependencies.Cache(BeatmapManager = new BeatmapManager(Storage, contextFactory, RulesetStore, API, Audio, Host, defaultBeatmap)); + dependencies.Cache(ScoreManager = new ScoreManager(RulesetStore, () => BeatmapManager, Storage, API, ContextFactory, Host)); + dependencies.Cache(BeatmapManager = new BeatmapManager(Storage, ContextFactory, RulesetStore, API, Audio, Host, defaultBeatmap)); // this should likely be moved to ArchiveModelManager when another case appers where it is necessary // to have inter-dependent model managers. this could be obtained with an IHasForeign interface to @@ -189,8 +189,8 @@ namespace osu.Game BeatmapManager.ItemRemoved += i => ScoreManager.Delete(getBeatmapScores(i), true); BeatmapManager.ItemAdded += i => ScoreManager.Undelete(getBeatmapScores(i), true); - dependencies.Cache(KeyBindingStore = new KeyBindingStore(contextFactory, RulesetStore)); - dependencies.Cache(SettingsStore = new SettingsStore(contextFactory)); + dependencies.Cache(KeyBindingStore = new KeyBindingStore(ContextFactory, RulesetStore)); + dependencies.Cache(SettingsStore = new SettingsStore(ContextFactory)); dependencies.Cache(RulesetConfigCache = new RulesetConfigCache(SettingsStore)); dependencies.Cache(new SessionStatics()); dependencies.Cache(new OsuColour()); @@ -279,7 +279,7 @@ namespace osu.Game { try { - using (var db = contextFactory.GetForWrite(false)) + using (var db = ContextFactory.GetForWrite(false)) db.Context.Migrate(); } catch (Exception e) @@ -288,12 +288,12 @@ namespace osu.Game // if we failed, let's delete the database and start fresh. // todo: we probably want a better (non-destructive) migrations/recovery process at a later point than this. - contextFactory.ResetDatabase(); + ContextFactory.ResetDatabase(); Logger.Log("Database purged successfully.", LoggingTarget.Database); // only run once more, then hard bail. - using (var db = contextFactory.GetForWrite(false)) + using (var db = ContextFactory.GetForWrite(false)) db.Context.Migrate(); } } From 49a03f1c06ca6824fc8e131ddd1e8f845c4ea1f2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 May 2020 18:31:36 +0900 Subject: [PATCH 0613/1117] Add basic blocking migration, move not copy --- osu.Game/IO/OsuStorage.cs | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/osu.Game/IO/OsuStorage.cs b/osu.Game/IO/OsuStorage.cs index ee42c491d1..f6cac2f4f1 100644 --- a/osu.Game/IO/OsuStorage.cs +++ b/osu.Game/IO/OsuStorage.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.IO; using osu.Framework.Logging; using osu.Framework.Platform; using osu.Game.Configuration; @@ -9,10 +11,15 @@ namespace osu.Game.IO { public class OsuStorage : WrappedStorage { + private readonly GameHost host; + private readonly StorageConfigManager storageConfig; + public OsuStorage(GameHost host) : base(host.Storage, string.Empty) { - var storageConfig = new StorageConfigManager(host.Storage); + this.host = host; + + storageConfig = new StorageConfigManager(host.Storage); var customStoragePath = storageConfig.Get(StorageConfig.FullPath); @@ -22,5 +29,34 @@ namespace osu.Game.IO Logger.Storage = UnderlyingStorage.GetStorageForDirectory("logs"); } } + + public void Migrate(string newLocation) + { + string oldLocation = GetFullPath("."); + + // ensure the new location has no files present, else hard abort + if (Directory.Exists(newLocation)) + { + if (Directory.GetFiles(newLocation).Length > 0) + throw new InvalidOperationException("Migration destination already has files present"); + + Directory.Delete(newLocation, true); + } + + Directory.Move(oldLocation, newLocation); + + Directory.CreateDirectory(newLocation); + // temporary + Directory.CreateDirectory(oldLocation); + + // move back exceptions for now + Directory.Move(Path.Combine(newLocation, "cache"), Path.Combine(oldLocation, "cache")); + File.Move(Path.Combine(newLocation, "framework.ini"), Path.Combine(oldLocation, "framework.ini")); + + ChangeTargetStorage(host.GetStorage(newLocation)); + + storageConfig.Set(StorageConfig.FullPath, newLocation); + storageConfig.Save(); + } } } From 7a2020fd4561e461ef7700a5178d7b05a8c23b6b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 May 2020 19:00:59 +0900 Subject: [PATCH 0614/1117] User copy operation instead of move --- osu.Game/IO/OsuStorage.cs | 63 ++++++++++++++++++++++++++++++++++----- 1 file changed, 55 insertions(+), 8 deletions(-) diff --git a/osu.Game/IO/OsuStorage.cs b/osu.Game/IO/OsuStorage.cs index f6cac2f4f1..955aae7b68 100644 --- a/osu.Game/IO/OsuStorage.cs +++ b/osu.Game/IO/OsuStorage.cs @@ -3,6 +3,7 @@ using System; using System.IO; +using System.Linq; using osu.Framework.Logging; using osu.Framework.Platform; using osu.Game.Configuration; @@ -14,6 +15,14 @@ namespace osu.Game.IO private readonly GameHost host; private readonly StorageConfigManager storageConfig; + internal static readonly string[] IGNORE_DIRECTORIES = { "cache" }; + + internal static readonly string[] IGNORE_FILES = + { + "framework.ini", + "storage.ini" + }; + public OsuStorage(GameHost host) : base(host.Storage, string.Empty) { @@ -43,20 +52,58 @@ namespace osu.Game.IO Directory.Delete(newLocation, true); } - Directory.Move(oldLocation, newLocation); + var source = new DirectoryInfo(oldLocation); + var destination = new DirectoryInfo(newLocation); - Directory.CreateDirectory(newLocation); - // temporary - Directory.CreateDirectory(oldLocation); - - // move back exceptions for now - Directory.Move(Path.Combine(newLocation, "cache"), Path.Combine(oldLocation, "cache")); - File.Move(Path.Combine(newLocation, "framework.ini"), Path.Combine(oldLocation, "framework.ini")); + copyRecursive(source, destination); ChangeTargetStorage(host.GetStorage(newLocation)); storageConfig.Set(StorageConfig.FullPath, newLocation); storageConfig.Save(); + + deleteRecursive(source); + } + + private static void deleteRecursive(DirectoryInfo target, bool topLevelExcludes = true) + { + foreach (System.IO.FileInfo fi in target.GetFiles()) + { + if (IGNORE_FILES.Contains(fi.Name)) + continue; + + fi.Delete(); + } + + foreach (DirectoryInfo dir in target.GetDirectories()) + { + if (IGNORE_DIRECTORIES.Contains(dir.Name)) + continue; + + dir.Delete(true); + } + } + + private static void copyRecursive(DirectoryInfo source, DirectoryInfo destination, bool topLevelExcludes = true) + { + // based off example code https://docs.microsoft.com/en-us/dotnet/api/system.io.directoryinfo + Directory.CreateDirectory(destination.FullName); + + foreach (System.IO.FileInfo fi in source.GetFiles()) + { + if (IGNORE_FILES.Contains(fi.Name)) + continue; + + fi.CopyTo(Path.Combine(destination.FullName, fi.Name), true); + } + + foreach (DirectoryInfo dir in source.GetDirectories()) + { + if (IGNORE_DIRECTORIES.Contains(dir.Name)) + continue; + + copyRecursive(dir, destination.CreateSubdirectory(dir.Name), false); + } } } } From 5e65bda8d8ba49b5213fcf163861bdee4ebddb6c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 May 2020 19:01:19 +0900 Subject: [PATCH 0615/1117] Add test coverage --- .../NonVisual/CustomDataDirectoryTest.cs | 60 ++++++++++++++++++- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs index d741bc5de1..7f08fad5be 100644 --- a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs +++ b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs @@ -7,14 +7,23 @@ using System.Threading; using System.Threading.Tasks; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Framework.Platform; using osu.Game.Configuration; +using osu.Game.IO; namespace osu.Game.Tests.NonVisual { [TestFixture] public class CustomDataDirectoryTest { + [SetUp] + public void SetUp() + { + if (Directory.Exists(customPath)) + Directory.Delete(customPath, true); + } + [Test] public void TestDefaultDirectory() { @@ -36,6 +45,8 @@ namespace osu.Game.Tests.NonVisual } } + private string customPath => Path.Combine(Environment.CurrentDirectory, "custom-path"); + [Test] public void TestCustomDirectory() { @@ -49,7 +60,7 @@ namespace osu.Game.Tests.NonVisual storage.DeleteDirectory(string.Empty); using (var storageConfig = new StorageConfigManager(storage)) - storageConfig.Set(StorageConfig.FullPath, Path.Combine(Environment.CurrentDirectory, "custom-path")); + storageConfig.Set(StorageConfig.FullPath, customPath); try { @@ -58,7 +69,52 @@ namespace osu.Game.Tests.NonVisual // switch to DI'd storage storage = osu.Dependencies.Get(); - Assert.That(storage.GetFullPath("."), Is.EqualTo(Path.Combine(Environment.CurrentDirectory, "custom-path"))); + Assert.That(storage.GetFullPath("."), Is.EqualTo(customPath)); + } + finally + { + host.Exit(); + } + } + } + + [Test] + public void TestMigration() + { + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestMigration))) + { + try + { + var osu = loadOsu(host); + var storage = osu.Dependencies.Get(); + + // ensure we perform a save + host.Dependencies.Get().Save(); + + // ensure we "use" cache + host.Storage.GetStorageForDirectory("cache"); + + string defaultStorageLocation = Path.Combine(Environment.CurrentDirectory, "headless", nameof(TestMigration)); + + Assert.That(storage.GetFullPath("."), Is.EqualTo(defaultStorageLocation)); + + (storage as OsuStorage)?.Migrate(customPath); + + Assert.That(storage.GetFullPath("."), Is.EqualTo(customPath)); + + foreach (var file in OsuStorage.IGNORE_FILES) + { + Assert.That(host.Storage.Exists(file), Is.True); + Assert.That(storage.Exists(file), Is.False); + } + + foreach (var dir in OsuStorage.IGNORE_DIRECTORIES) + { + Assert.That(host.Storage.ExistsDirectory(dir), Is.True); + Assert.That(storage.ExistsDirectory(dir), Is.False); + } + + Assert.That(new StreamReader(host.Storage.GetStream("storage.ini")).ReadToEnd().Contains($"FullPath = {customPath}")); } finally { From b6f232e39428ce2e056c821cf229c15717c1d134 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 May 2020 10:27:48 +0900 Subject: [PATCH 0616/1117] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 8214fa2f2c..af699af1ba 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 9db5fe562c..47804ed06e 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -24,7 +24,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 82253a0418..86cffa9fba 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -80,7 +80,7 @@ - + From d6840d880a0df25e310c7ee32f7427f0d4baf675 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 May 2020 10:38:31 +0900 Subject: [PATCH 0617/1117] Update StableStorage implementation in line with framework changes --- osu.Desktop/OsuGameDesktop.cs | 78 +++++++++++------------ osu.Game.Tournament/IPC/FileBasedIPC.cs | 84 +++++++++++-------------- 2 files changed, 72 insertions(+), 90 deletions(-) diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index f05ee48914..9351e17419 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -6,15 +6,14 @@ using System.IO; using System.Linq; using System.Reflection; using System.Threading.Tasks; +using Microsoft.Win32; using osu.Desktop.Overlays; using osu.Framework.Platform; using osu.Game; using osuTK.Input; -using Microsoft.Win32; using osu.Desktop.Updater; using osu.Framework; using osu.Framework.Logging; -using osu.Framework.Platform.Windows; using osu.Framework.Screens; using osu.Game.Screens.Menu; using osu.Game.Updater; @@ -37,7 +36,11 @@ namespace osu.Desktop try { if (Host is DesktopGameHost desktopHost) - return new StableStorage(desktopHost); + { + string stablePath = getStableInstallPath(); + if (!string.IsNullOrEmpty(stablePath)) + return new DesktopStorage(stablePath, desktopHost); + } } catch (Exception) { @@ -47,6 +50,35 @@ namespace osu.Desktop return null; } + private string getStableInstallPath() + { + static bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs")); + + string stableInstallPath; + + try + { + using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu")) + stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty).ToString().Split('"')[1].Replace("osu!.exe", ""); + + if (checkExists(stableInstallPath)) + return stableInstallPath; + } + catch + { + } + + stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!"); + if (checkExists(stableInstallPath)) + return stableInstallPath; + + stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".osu"); + if (checkExists(stableInstallPath)) + return stableInstallPath; + + return null; + } + protected override UpdateManager CreateUpdateManager() { switch (RuntimeInfo.OS) @@ -111,45 +143,5 @@ namespace osu.Desktop Task.Factory.StartNew(() => Import(filePaths), TaskCreationOptions.LongRunning); } - - /// - /// A method of accessing an osu-stable install in a controlled fashion. - /// - private class StableStorage : WindowsStorage - { - protected override string LocateBasePath() - { - static bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs")); - - string stableInstallPath; - - try - { - using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu")) - stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty).ToString().Split('"')[1].Replace("osu!.exe", ""); - - if (checkExists(stableInstallPath)) - return stableInstallPath; - } - catch - { - } - - stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!"); - if (checkExists(stableInstallPath)) - return stableInstallPath; - - stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".osu"); - if (checkExists(stableInstallPath)) - return stableInstallPath; - - return null; - } - - public StableStorage(DesktopGameHost host) - : base(string.Empty, host) - { - } - } } } diff --git a/osu.Game.Tournament/IPC/FileBasedIPC.cs b/osu.Game.Tournament/IPC/FileBasedIPC.cs index eefa9fcfe6..53ba597a7e 100644 --- a/osu.Game.Tournament/IPC/FileBasedIPC.cs +++ b/osu.Game.Tournament/IPC/FileBasedIPC.cs @@ -8,7 +8,6 @@ using Microsoft.Win32; using osu.Framework.Allocation; using osu.Framework.Logging; using osu.Framework.Platform; -using osu.Framework.Platform.Windows; using osu.Framework.Threading; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Legacy; @@ -52,7 +51,12 @@ namespace osu.Game.Tournament.IPC try { - Storage = new StableStorage(host as DesktopGameHost); + var path = findStablePath(); + + if (string.IsNullOrEmpty(path)) + return null; + + Storage = new DesktopStorage(path, host as DesktopGameHost); const string file_ipc_filename = "ipc.txt"; const string file_ipc_state_filename = "ipc-state.txt"; @@ -145,64 +149,50 @@ namespace osu.Game.Tournament.IPC return Storage; } - /// - /// A method of accessing an osu-stable install in a controlled fashion. - /// - private class StableStorage : WindowsStorage + private string findStablePath() { - protected override string LocateBasePath() - { - static bool checkExists(string p) - { - return File.Exists(Path.Combine(p, "ipc.txt")); - } + static bool checkExists(string p) => File.Exists(Path.Combine(p, "ipc.txt")); - string stableInstallPath = string.Empty; + string stableInstallPath = string.Empty; + + try + { + try + { + stableInstallPath = Environment.GetEnvironmentVariable("OSU_STABLE_PATH"); + + if (checkExists(stableInstallPath)) + return stableInstallPath; + } + catch + { + } try { - try - { - stableInstallPath = Environment.GetEnvironmentVariable("OSU_STABLE_PATH"); + using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu")) + stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty).ToString().Split('"')[1].Replace("osu!.exe", ""); - if (checkExists(stableInstallPath)) - return stableInstallPath; - } - catch - { - } - - try - { - using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu")) - stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty).ToString().Split('"')[1].Replace("osu!.exe", ""); - - if (checkExists(stableInstallPath)) - return stableInstallPath; - } - catch - { - } - - stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!"); if (checkExists(stableInstallPath)) return stableInstallPath; - - stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".osu"); - if (checkExists(stableInstallPath)) - return stableInstallPath; - - return null; } - finally + catch { - Logger.Log($"Stable path for tourney usage: {stableInstallPath}"); } - } - public StableStorage(DesktopGameHost host) - : base(string.Empty, host) + stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!"); + if (checkExists(stableInstallPath)) + return stableInstallPath; + + stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".osu"); + if (checkExists(stableInstallPath)) + return stableInstallPath; + + return null; + } + finally { + Logger.Log($"Stable path for tourney usage: {stableInstallPath}"); } } } From af5e1f8298ad47fdbd783e3dedcb5ba0038aa898 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 May 2020 14:22:07 +0900 Subject: [PATCH 0618/1117] Commit autogenerated rider VCS settings update --- .idea/.idea.osu.Desktop/.idea/projectSettingsUpdater.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.idea/.idea.osu.Desktop/.idea/projectSettingsUpdater.xml b/.idea/.idea.osu.Desktop/.idea/projectSettingsUpdater.xml index 4bb9f4d2a0..7515e76054 100644 --- a/.idea/.idea.osu.Desktop/.idea/projectSettingsUpdater.xml +++ b/.idea/.idea.osu.Desktop/.idea/projectSettingsUpdater.xml @@ -1,6 +1,6 @@ - \ No newline at end of file From 9f64882f371aeae723e646871cbdfe5bb250f168 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 May 2020 14:48:38 +0900 Subject: [PATCH 0619/1117] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index af699af1ba..a406cdf08a 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 47804ed06e..5ccfaaac9e 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -24,7 +24,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 86cffa9fba..dc83d937f7 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -80,7 +80,7 @@ - + From 8b5de7403f317cd87be9315976632d08873b021a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 May 2020 15:12:29 +0900 Subject: [PATCH 0620/1117] Fix android usage of obsoleted VersionCode --- osu.Android/OsuGameAndroid.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Android/OsuGameAndroid.cs b/osu.Android/OsuGameAndroid.cs index 84f215f930..19ed7ffcf5 100644 --- a/osu.Android/OsuGameAndroid.cs +++ b/osu.Android/OsuGameAndroid.cs @@ -18,7 +18,8 @@ namespace osu.Android try { - string versionName = packageInfo.VersionCode.ToString(); + // todo: needs checking before play store redeploy. + string versionName = packageInfo.VersionName; // undo play store version garbling return new Version(int.Parse(versionName.Substring(0, 4)), int.Parse(versionName.Substring(4, 4)), int.Parse(versionName.Substring(8, 1))); } From c5589d278b17f30196272e26ac506dbfb2a3e46d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 May 2020 15:35:25 +0900 Subject: [PATCH 0621/1117] Revert "Commit autogenerated rider VCS settings update" This reverts commit af5e1f8298ad47fdbd783e3dedcb5ba0038aa898. --- .idea/.idea.osu.Desktop/.idea/projectSettingsUpdater.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.idea/.idea.osu.Desktop/.idea/projectSettingsUpdater.xml b/.idea/.idea.osu.Desktop/.idea/projectSettingsUpdater.xml index 7515e76054..4bb9f4d2a0 100644 --- a/.idea/.idea.osu.Desktop/.idea/projectSettingsUpdater.xml +++ b/.idea/.idea.osu.Desktop/.idea/projectSettingsUpdater.xml @@ -1,6 +1,6 @@ - \ No newline at end of file From 30dd158c33ef1c3968dd7432ffa3fc78f2aff460 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 8 May 2020 09:37:50 +0200 Subject: [PATCH 0622/1117] Rename property to AllowGameplayOverlays and update XMLDoc accordingly. --- osu.Game/Rulesets/UI/DrawableRuleset.cs | 8 ++++---- osu.Game/Screens/Play/Player.cs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index 57fbb7f1a5..e1c21209c2 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -488,13 +488,13 @@ namespace osu.Game.Rulesets.UI protected virtual ResumeOverlay CreateResumeOverlay() => null; /// - /// Whether to display the HUD with this ruleset. - /// Override to false to completely disable the display of the HUD with this ruleset. + /// Whether to display gameplay overlays with this ruleset. + /// Override to false to completely disable the display of gameplay overlays. /// /// - /// HUD refers here to in player as well as . + /// Gameplay overlays refer here to in player as well as . /// - public virtual bool DisplayHud => true; + public virtual bool AllowGameplayOverlays => true; /// public class DimmableStoryboard : UserDimContainer { - public Container OverlayLayerContainer; + public Container OverlayLayerContainer { get; private set; } private readonly Storyboard storyboard; private DrawableStoryboard drawableStoryboard; From c2697d39070d73ffbb2b7894a57088a85df2e127 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 May 2020 20:49:01 +0900 Subject: [PATCH 0808/1117] Use DrawableSample in SkinnableSound class --- .../Objects/Drawables/DrawableHitObject.cs | 11 ++- osu.Game/Skinning/SkinnableSound.cs | 72 +++++++------------ 2 files changed, 32 insertions(+), 51 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 33ea02c22f..c32d4e441e 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -11,7 +11,6 @@ using osu.Framework.Extensions.TypeExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Primitives; using osu.Framework.Threading; -using osu.Framework.Audio; using osu.Game.Audio; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects.Types; @@ -96,8 +95,6 @@ namespace osu.Game.Rulesets.Objects.Drawables /// protected virtual float SamplePlaybackPosition => 0.5f; - private readonly BindableDouble balanceAdjust = new BindableDouble(); - private BindableList samplesBindable; private Bindable startTimeBindable; private Bindable userPositionalHitSounds; @@ -173,7 +170,6 @@ namespace osu.Game.Rulesets.Objects.Drawables } Samples = new SkinnableSound(samples.Select(s => HitObject.SampleControlPoint.ApplyTo(s))); - Samples.AddAdjustment(AdjustableProperty.Balance, balanceAdjust); AddInternal(Samples); } @@ -360,8 +356,11 @@ namespace osu.Game.Rulesets.Objects.Drawables { const float balance_adjust_amount = 0.4f; - balanceAdjust.Value = balance_adjust_amount * (userPositionalHitSounds.Value ? SamplePlaybackPosition - 0.5f : 0); - Samples?.Play(); + if (Samples != null) + { + Samples.Balance.Value = balance_adjust_amount * (userPositionalHitSounds.Value ? SamplePlaybackPosition - 0.5f : 0); + Samples.Play(); + } } protected override void Update() diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs index a78c04ecd4..30320c89a6 100644 --- a/osu.Game/Skinning/SkinnableSound.cs +++ b/osu.Game/Skinning/SkinnableSound.cs @@ -4,11 +4,11 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Audio; -using osu.Framework.Audio.Sample; using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Graphics.Audio; +using osu.Framework.Graphics.Containers; using osu.Game.Audio; namespace osu.Game.Skinning @@ -17,25 +17,32 @@ namespace osu.Game.Skinning { private readonly ISampleInfo[] hitSamples; - private List<(AdjustableProperty property, BindableDouble bindable)> adjustments; - - private SampleChannel[] channels; - [Resolved] private ISampleStore samples { get; set; } + public SkinnableSound(ISampleInfo hitSamples) + : this(new[] { hitSamples }) + { + } + public SkinnableSound(IEnumerable hitSamples) { this.hitSamples = hitSamples.ToArray(); - } - - public SkinnableSound(ISampleInfo hitSamples) - { - this.hitSamples = new[] { hitSamples }; + InternalChild = samplesContainer = new AudioContainer(); } private bool looping; + private readonly AudioContainer samplesContainer; + + public BindableNumber Volume => samplesContainer.Volume; + + public BindableNumber Balance => samplesContainer.Balance; + + public BindableNumber Frequency => samplesContainer.Frequency; + + public BindableNumber Tempo => samplesContainer.Tempo; + public bool Looping { get => looping; @@ -45,33 +52,23 @@ namespace osu.Game.Skinning looping = value; - channels?.ForEach(c => c.Looping = looping); + samplesContainer.ForEach(c => c.Looping = looping); } } - public void Play() => channels?.ForEach(c => c.Play()); - - public void Stop() => channels?.ForEach(c => c.Stop()); - - public void AddAdjustment(AdjustableProperty type, BindableDouble adjustBindable) + public void Play() => samplesContainer.ForEach(c => { - if (adjustments == null) adjustments = new List<(AdjustableProperty, BindableDouble)>(); + if (c.AggregateVolume.Value > 0) + c.Play(); + }); - adjustments.Add((type, adjustBindable)); - channels?.ForEach(c => c.AddAdjustment(type, adjustBindable)); - } - - public void RemoveAdjustment(AdjustableProperty type, BindableDouble adjustBindable) - { - adjustments?.Remove((type, adjustBindable)); - channels?.ForEach(c => c.RemoveAdjustment(type, adjustBindable)); - } + public void Stop() => samplesContainer.ForEach(c => c.Stop()); public override bool IsPresent => Scheduler.HasPendingTasks; protected override void SkinChanged(ISkinSource skin, bool allowFallback) { - channels = hitSamples.Select(s => + var channels = hitSamples.Select(s => { var ch = skin.GetSample(s); @@ -88,27 +85,12 @@ namespace osu.Game.Skinning { ch.Looping = looping; ch.Volume.Value = s.Volume / 100.0; - - if (adjustments != null) - { - foreach (var (property, bindable) in adjustments) - ch.AddAdjustment(property, bindable); - } } return ch; - }).Where(c => c != null).ToArray(); - } + }).Where(c => c != null); - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - if (channels != null) - { - foreach (var c in channels) - c.Dispose(); - } + samplesContainer.ChildrenEnumerable = channels.Select(c => new DrawableSample(c)); } } } From 3354d48a38c90314caa376481b06a1d0e9f77fc3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 May 2020 17:48:43 +0900 Subject: [PATCH 0809/1117] Change snapping to be screen space coordinate based --- .../Blueprints/HoldNoteSelectionBlueprint.cs | 2 +- .../Edit/ManiaHitObjectComposer.cs | 16 +++++++++++----- .../TestSceneOsuDistanceSnapGrid.cs | 8 +++++--- .../Sliders/Components/PathControlPointPiece.cs | 4 ++-- .../Sliders/SliderSelectionBlueprint.cs | 2 +- .../Visual/Editing/TestSceneDistanceSnapGrid.cs | 8 +++++--- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 17 +++++++++++------ ...SnapProvider.cs => IPositionSnapProvider.cs} | 11 +++++++++-- .../Rulesets/Edit/OverlaySelectionBlueprint.cs | 2 +- osu.Game/Rulesets/Edit/PlacementBlueprint.cs | 3 +++ osu.Game/Rulesets/Edit/SelectionBlueprint.cs | 2 +- .../Compose/Components/BlueprintContainer.cs | 8 ++++---- .../Components/ComposeBlueprintContainer.cs | 5 ++--- .../Edit/Compose/Components/DistanceSnapGrid.cs | 2 +- .../Compose/Components/Timeline/Timeline.cs | 12 ++++++------ .../Timeline/TimelineHitObjectBlueprint.cs | 4 ++-- 16 files changed, 65 insertions(+), 41 deletions(-) rename osu.Game/Rulesets/Edit/{IDistanceSnapProvider.cs => IPositionSnapProvider.cs} (84%) diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNoteSelectionBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNoteSelectionBlueprint.cs index 43d43ef252..1737c4d2e5 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNoteSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNoteSelectionBlueprint.cs @@ -77,6 +77,6 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints public override Quad SelectionQuad => ScreenSpaceDrawQuad; - public override Vector2 SelectionPoint => DrawableObject.Head.ScreenSpaceDrawQuad.Centre; + public override Vector2 ScreenSpaceSelectionPoint => DrawableObject.Head.ScreenSpaceDrawQuad.Centre; } } diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index dfa933baad..9ba2cdeaec 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.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.Beatmaps; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; @@ -44,26 +45,31 @@ namespace osu.Game.Rulesets.Mania.Edit public int TotalColumns => Playfield.TotalColumns; - public override (Vector2 position, double time) GetSnappedPosition(Vector2 position, double time) + public override (Vector2 position, double time) SnapPositionToValidTime(Vector2 position) + { + throw new NotImplementedException(); + } + + public override (Vector2 position, double time) SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) { var hoc = Playfield.GetColumn(0).HitObjectContainer; - float targetPosition = hoc.ToLocalSpace(ToScreenSpace(position)).Y; + Vector2 targetPosition = hoc.ToLocalSpace(screenSpacePosition); if (drawableRuleset.ScrollingInfo.Direction.Value == ScrollingDirection.Down) { // We're dealing with screen coordinates in which the position decreases towards the centre of the screen resulting in an increase in start time. // The scrolling algorithm instead assumes a top anchor meaning an increase in time corresponds to an increase in position, // so when scrolling downwards the coordinates need to be flipped. - targetPosition = hoc.DrawHeight - targetPosition; + targetPosition.Y = hoc.DrawHeight - targetPosition.Y; } - double targetTime = drawableRuleset.ScrollingInfo.Algorithm.TimeAt(targetPosition, + double targetTime = drawableRuleset.ScrollingInfo.Algorithm.TimeAt(targetPosition.Y, EditorClock.CurrentTime, drawableRuleset.ScrollingInfo.TimeRange.Value, hoc.DrawHeight); - return base.GetSnappedPosition(position, targetTime); + return (targetPosition, targetTime); } protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuDistanceSnapGrid.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuDistanceSnapGrid.cs index c182aa5d63..f95f76b405 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuDistanceSnapGrid.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuDistanceSnapGrid.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Osu.Tests [Cached] private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor(); - [Cached(typeof(IDistanceSnapProvider))] + [Cached(typeof(IPositionSnapProvider))] private readonly SnapProvider snapProvider = new SnapProvider(); private TestOsuDistanceSnapGrid grid; @@ -172,9 +172,11 @@ namespace osu.Game.Rulesets.Osu.Tests } } - private class SnapProvider : IDistanceSnapProvider + private class SnapProvider : IPositionSnapProvider { - public (Vector2 position, double time) GetSnappedPosition(Vector2 position, double time) => (position, time); + public (Vector2 position, double time) SnapPositionToValidTime(Vector2 position) => (position, 0); + + public (Vector2 position, double time) SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) => (screenSpacePosition, 0); public float GetBeatSnapDistanceAt(double referenceTime) => (float)beat_length; 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 d0c1eb5317..abbef0772f 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components private IEditorChangeHandler changeHandler { get; set; } [Resolved(CanBeNull = true)] - private IDistanceSnapProvider snapProvider { get; set; } + private IPositionSnapProvider snapProvider { get; set; } [Resolved] private OsuColour colours { get; set; } @@ -162,7 +162,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components if (ControlPoint == slider.Path.ControlPoints[0]) { // Special handling for the head control point - the position of the slider changes which means the snapped position and time have to be taken into account - (Vector2 snappedPosition, double snappedTime) = snapProvider?.GetSnappedPosition(e.MousePosition, slider.StartTime) ?? (e.MousePosition, slider.StartTime); + (Vector2 snappedPosition, double snappedTime) = snapProvider?.SnapScreenSpacePositionToValidTime(e.MousePosition) ?? (e.MousePosition, slider.StartTime); Vector2 movementDelta = snappedPosition - slider.Position; slider.Position += movementDelta; diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs index b7074b7ee5..6633136673 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs @@ -190,7 +190,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders new OsuMenuItem("Add control point", MenuItemType.Standard, () => addControlPoint(rightClickPosition)), }; - public override Vector2 SelectionPoint => ((DrawableSlider)DrawableObject).HeadCircle.ScreenSpaceDrawQuad.Centre; + public override Vector2 ScreenSpaceSelectionPoint => ((DrawableSlider)DrawableObject).HeadCircle.ScreenSpaceDrawQuad.Centre; public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => BodyPiece.ReceivePositionalInputAt(screenSpacePos); diff --git a/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs b/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs index 417d16fdb0..0e5e88c47a 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs @@ -23,7 +23,7 @@ namespace osu.Game.Tests.Visual.Editing [Cached(typeof(EditorBeatmap))] private readonly EditorBeatmap editorBeatmap; - [Cached(typeof(IDistanceSnapProvider))] + [Cached(typeof(IPositionSnapProvider))] private readonly SnapProvider snapProvider = new SnapProvider(); public TestSceneDistanceSnapGrid() @@ -151,9 +151,11 @@ namespace osu.Game.Tests.Visual.Editing => (Vector2.Zero, 0); } - private class SnapProvider : IDistanceSnapProvider + private class SnapProvider : IPositionSnapProvider { - public (Vector2 position, double time) GetSnappedPosition(Vector2 position, double time) => (position, time); + public (Vector2 position, double time) SnapPositionToValidTime(Vector2 position) => (position, 0); + + public (Vector2 position, double time) SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) => (screenSpacePosition, 0); public float GetBeatSnapDistanceAt(double referenceTime) => 10; diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 883288d6d7..82e8fc8b10 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -245,8 +245,7 @@ namespace osu.Game.Rulesets.Edit { EditorBeatmap.PlacementObject.Value = hitObject; - if (distanceSnapGrid != null) - hitObject.StartTime = GetSnappedPosition(distanceSnapGrid.ToLocalSpace(inputManager.CurrentState.Mouse.Position), hitObject.StartTime).time; + hitObject.StartTime = SnapScreenSpacePositionToValidTime(inputManager.CurrentState.Mouse.Position).time; } public void EndPlacement(HitObject hitObject, bool commit) @@ -265,7 +264,11 @@ namespace osu.Game.Rulesets.Edit public void Delete(HitObject hitObject) => EditorBeatmap.Remove(hitObject); - public override (Vector2 position, double time) GetSnappedPosition(Vector2 position, double time) => distanceSnapGrid?.GetSnappedPosition(position) ?? (position, time); + public override (Vector2 position, double time) SnapPositionToValidTime(Vector2 position) => + distanceSnapGrid?.GetSnappedPosition(position) ?? (position, 0); + + public override (Vector2 position, double time) SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) + => SnapPositionToValidTime(drawableRulesetWrapper.Playfield.ToLocalSpace(screenSpacePosition)); public override float GetBeatSnapDistanceAt(double referenceTime) { @@ -297,8 +300,8 @@ namespace osu.Game.Rulesets.Edit } [Cached(typeof(HitObjectComposer))] - [Cached(typeof(IDistanceSnapProvider))] - public abstract class HitObjectComposer : CompositeDrawable, IDistanceSnapProvider + [Cached(typeof(IPositionSnapProvider))] + public abstract class HitObjectComposer : CompositeDrawable, IPositionSnapProvider { internal HitObjectComposer() { @@ -323,7 +326,9 @@ namespace osu.Game.Rulesets.Edit [CanBeNull] protected virtual DistanceSnapGrid CreateDistanceSnapGrid([NotNull] IEnumerable selectedHitObjects) => null; - public abstract (Vector2 position, double time) GetSnappedPosition(Vector2 position, double time); + public abstract (Vector2 position, double time) SnapPositionToValidTime(Vector2 position); + + public abstract (Vector2 position, double time) SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition); public abstract float GetBeatSnapDistanceAt(double referenceTime); diff --git a/osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs b/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs similarity index 84% rename from osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs rename to osu.Game/Rulesets/Edit/IPositionSnapProvider.cs index c6e61f68da..93cb605132 100644 --- a/osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs +++ b/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs @@ -5,9 +5,16 @@ using osuTK; namespace osu.Game.Rulesets.Edit { - public interface IDistanceSnapProvider + public interface IPositionSnapProvider { - (Vector2 position, double time) GetSnappedPosition(Vector2 position, double time); + /// + /// Given a position (local to the provider), find a valid time snap + /// + /// The local position to be snapped. + /// The time and position post-snapping. + (Vector2 position, double time) SnapPositionToValidTime(Vector2 position); + + (Vector2 position, double time) SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition); /// /// Retrieves the distance between two points within a timing point that are one beat length apart. diff --git a/osu.Game/Rulesets/Edit/OverlaySelectionBlueprint.cs b/osu.Game/Rulesets/Edit/OverlaySelectionBlueprint.cs index b4ae3f3fba..8202d3a1d1 100644 --- a/osu.Game/Rulesets/Edit/OverlaySelectionBlueprint.cs +++ b/osu.Game/Rulesets/Edit/OverlaySelectionBlueprint.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Edit public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => DrawableObject.ReceivePositionalInputAt(screenSpacePos); - public override Vector2 SelectionPoint => DrawableObject.ScreenSpaceDrawQuad.Centre; + public override Vector2 ScreenSpaceSelectionPoint => DrawableObject.ScreenSpaceDrawQuad.Centre; public override Quad SelectionQuad => DrawableObject.ScreenSpaceDrawQuad; diff --git a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs index fb1eb7adbf..c06e50950c 100644 --- a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs +++ b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs @@ -66,7 +66,10 @@ namespace osu.Game.Rulesets.Edit protected void BeginPlacement(double? startTime = null, bool commitStart = false) { HitObject.StartTime = startTime ?? EditorClock.CurrentTime; + + // applies snapping to above time placementHandler.BeginPlacement(HitObject); + PlacementActive |= commitStart; } diff --git a/osu.Game/Rulesets/Edit/SelectionBlueprint.cs b/osu.Game/Rulesets/Edit/SelectionBlueprint.cs index e6a63eae4f..71256093d5 100644 --- a/osu.Game/Rulesets/Edit/SelectionBlueprint.cs +++ b/osu.Game/Rulesets/Edit/SelectionBlueprint.cs @@ -116,7 +116,7 @@ namespace osu.Game.Rulesets.Edit /// /// The screen-space point that causes this to be selected. /// - public virtual Vector2 SelectionPoint => ScreenSpaceDrawQuad.Centre; + public virtual Vector2 ScreenSpaceSelectionPoint => ScreenSpaceDrawQuad.Centre; /// /// The screen-space quad that outlines this for selections. diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 8910684463..d1cae6b3cd 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -49,7 +49,7 @@ namespace osu.Game.Screens.Edit.Compose.Components private readonly BindableList selectedHitObjects = new BindableList(); [Resolved(canBeNull: true)] - private IDistanceSnapProvider snapProvider { get; set; } + private IPositionSnapProvider snapProvider { get; set; } protected BlueprintContainer() { @@ -326,7 +326,7 @@ namespace osu.Game.Screens.Edit.Compose.Components { foreach (var blueprint in SelectionBlueprints) { - if (blueprint.IsAlive && blueprint.IsPresent && rect.Contains(blueprint.SelectionPoint)) + if (blueprint.IsAlive && blueprint.IsPresent && rect.Contains(blueprint.ScreenSpaceSelectionPoint)) blueprint.Select(); else blueprint.Deselect(); @@ -384,7 +384,7 @@ namespace osu.Game.Screens.Edit.Compose.Components // Movement is tracked from the blueprint of the earliest hitobject, since it only makes sense to distance snap from that hitobject movementBlueprint = selectionHandler.SelectedBlueprints.OrderBy(b => b.HitObject.StartTime).First(); - movementBlueprintOriginalPosition = movementBlueprint.SelectionPoint; // todo: unsure if correct + movementBlueprintOriginalPosition = movementBlueprint.ScreenSpaceSelectionPoint; // todo: unsure if correct } /// @@ -405,7 +405,7 @@ namespace osu.Game.Screens.Edit.Compose.Components Vector2 movePosition = movementBlueprintOriginalPosition.Value + e.ScreenSpaceMousePosition - e.ScreenSpaceMouseDownPosition; // Retrieve a snapped position. - (Vector2 snappedPosition, double snappedTime) = snapProvider.GetSnappedPosition(ToLocalSpace(movePosition), draggedObject.StartTime); + (Vector2 snappedPosition, double snappedTime) = snapProvider.SnapScreenSpacePositionToValidTime(movePosition); // Move the hitobjects. if (!selectionHandler.HandleMovement(new MoveSelectionEvent(movementBlueprint, ToScreenSpace(snappedPosition)))) diff --git a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs index 0eb77a8561..bb6094ebe8 100644 --- a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs @@ -67,10 +67,9 @@ namespace osu.Game.Screens.Edit.Compose.Components private void updatePlacementPosition(Vector2 screenSpacePosition) { - Vector2 snappedGridPosition = composer.GetSnappedPosition(ToLocalSpace(screenSpacePosition), 0).position; - Vector2 snappedScreenSpacePosition = ToScreenSpace(snappedGridPosition); + Vector2 snappedPlayfieldPosition = composer.SnapScreenSpacePositionToValidTime(screenSpacePosition).position; - currentPlacement.UpdatePosition(snappedScreenSpacePosition); + currentPlacement.UpdatePosition(ToScreenSpace(snappedPlayfieldPosition)); } #endregion diff --git a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs index 3a42938fc1..8a92a2011d 100644 --- a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs +++ b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs @@ -43,7 +43,7 @@ namespace osu.Game.Screens.Edit.Compose.Components protected OsuColour Colours { get; private set; } [Resolved] - protected IDistanceSnapProvider SnapProvider { get; private set; } + protected IPositionSnapProvider SnapProvider { get; private set; } [Resolved] private EditorBeatmap beatmap { get; set; } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index 25f3cfc285..1006da28df 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -17,9 +17,9 @@ using osuTK; namespace osu.Game.Screens.Edit.Compose.Components.Timeline { - [Cached(typeof(IDistanceSnapProvider))] + [Cached(typeof(IPositionSnapProvider))] [Cached] - public class Timeline : ZoomableScrollContainer, IDistanceSnapProvider + public class Timeline : ZoomableScrollContainer, IPositionSnapProvider { public readonly Bindable WaveformVisible = new Bindable(); public readonly IBindable Beatmap = new Bindable(); @@ -181,12 +181,12 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline [Resolved] private IBeatSnapProvider beatSnapProvider { get; set; } - public double GetTimeFromScreenSpacePosition(Vector2 position) - => getTimeFromPosition(Content.ToLocalSpace(position)); - - public (Vector2 position, double time) GetSnappedPosition(Vector2 position, double time) => + public (Vector2 position, double time) SnapPositionToValidTime(Vector2 position) => (position, beatSnapProvider.SnapTime(getTimeFromPosition(position))); + public (Vector2 position, double time) SnapScreenSpacePositionToValidTime(Vector2 position) => + (position, beatSnapProvider.SnapTime(getTimeFromPosition(Content.ToLocalSpace(position)))); + private double getTimeFromPosition(Vector2 localPosition) => (localPosition.X / Content.DrawWidth) * track.Length; diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index 16ba3ba89a..b5eae26f98 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -186,7 +186,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline } } - public override Vector2 SelectionPoint => ScreenSpaceDrawQuad.TopLeft; + public override Vector2 ScreenSpaceSelectionPoint => ScreenSpaceDrawQuad.TopLeft; public class DragBar : Container { @@ -275,7 +275,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline OnDragHandled?.Invoke(e); - var time = timeline.GetTimeFromScreenSpacePosition(e.ScreenSpaceMousePosition); + var time = timeline.SnapScreenSpacePositionToValidTime(e.ScreenSpaceMousePosition).time; switch (hitObject) { From c46bfc2532d139838ad6a94e11571f2c81430421 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 May 2020 18:19:21 +0900 Subject: [PATCH 0810/1117] Create SnapResult class to hold various snapping results --- .../Edit/ManiaHitObjectComposer.cs | 10 +---- .../TestSceneOsuDistanceSnapGrid.cs | 4 +- .../Components/PathControlPointPiece.cs | 6 +-- .../Editing/TestSceneDistanceSnapGrid.cs | 4 +- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 17 ++++---- .../Rulesets/Edit/IPositionSnapProvider.cs | 27 ++++++++++--- .../Compose/Components/BlueprintContainer.cs | 15 ++++--- .../Components/ComposeBlueprintContainer.cs | 2 +- .../Compose/Components/Timeline/Timeline.cs | 7 +--- .../Timeline/TimelineHitObjectBlueprint.cs | 39 ++++++++++--------- 10 files changed, 70 insertions(+), 61 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index 9ba2cdeaec..f7951fcc5d 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.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.Beatmaps; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; @@ -45,12 +44,7 @@ namespace osu.Game.Rulesets.Mania.Edit public int TotalColumns => Playfield.TotalColumns; - public override (Vector2 position, double time) SnapPositionToValidTime(Vector2 position) - { - throw new NotImplementedException(); - } - - public override (Vector2 position, double time) SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) + public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) { var hoc = Playfield.GetColumn(0).HitObjectContainer; @@ -69,7 +63,7 @@ namespace osu.Game.Rulesets.Mania.Edit drawableRuleset.ScrollingInfo.TimeRange.Value, hoc.DrawHeight); - return (targetPosition, targetTime); + return new SnapResult(targetPosition, targetTime); } protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuDistanceSnapGrid.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuDistanceSnapGrid.cs index f95f76b405..0d0be2953b 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuDistanceSnapGrid.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuDistanceSnapGrid.cs @@ -174,9 +174,7 @@ namespace osu.Game.Rulesets.Osu.Tests private class SnapProvider : IPositionSnapProvider { - public (Vector2 position, double time) SnapPositionToValidTime(Vector2 position) => (position, 0); - - public (Vector2 position, double time) SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) => (screenSpacePosition, 0); + public SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, 0); public float GetBeatSnapDistanceAt(double referenceTime) => (float)beat_length; 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 abbef0772f..834bf1892f 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -162,11 +162,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components if (ControlPoint == slider.Path.ControlPoints[0]) { // Special handling for the head control point - the position of the slider changes which means the snapped position and time have to be taken into account - (Vector2 snappedPosition, double snappedTime) = snapProvider?.SnapScreenSpacePositionToValidTime(e.MousePosition) ?? (e.MousePosition, slider.StartTime); - Vector2 movementDelta = snappedPosition - slider.Position; + var result = snapProvider?.SnapScreenSpacePositionToValidTime(e.MousePosition); + Vector2 movementDelta = (result?.ScreenSpacePosition ?? e.MousePosition) - slider.Position; slider.Position += movementDelta; - slider.StartTime = snappedTime; + slider.StartTime = result?.Time ?? slider.StartTime; // Since control points are relative to the position of the slider, they all need to be offset backwards by the delta for (int i = 1; i < slider.Path.ControlPoints.Count; i++) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs b/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs index 0e5e88c47a..8190cf5f89 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs @@ -153,9 +153,7 @@ namespace osu.Game.Tests.Visual.Editing private class SnapProvider : IPositionSnapProvider { - public (Vector2 position, double time) SnapPositionToValidTime(Vector2 position) => (position, 0); - - public (Vector2 position, double time) SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) => (screenSpacePosition, 0); + public SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, 0); public float GetBeatSnapDistanceAt(double referenceTime) => 10; diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 82e8fc8b10..7e9bb850af 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -245,7 +245,8 @@ namespace osu.Game.Rulesets.Edit { EditorBeatmap.PlacementObject.Value = hitObject; - hitObject.StartTime = SnapScreenSpacePositionToValidTime(inputManager.CurrentState.Mouse.Position).time; + if (SnapScreenSpacePositionToValidTime(inputManager.CurrentState.Mouse.Position).Time is double time) + hitObject.StartTime = time; } public void EndPlacement(HitObject hitObject, bool commit) @@ -264,11 +265,13 @@ namespace osu.Game.Rulesets.Edit public void Delete(HitObject hitObject) => EditorBeatmap.Remove(hitObject); - public override (Vector2 position, double time) SnapPositionToValidTime(Vector2 position) => - distanceSnapGrid?.GetSnappedPosition(position) ?? (position, 0); + public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) + { + if (distanceSnapGrid == null) return new SnapResult(screenSpacePosition, null); - public override (Vector2 position, double time) SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) - => SnapPositionToValidTime(drawableRulesetWrapper.Playfield.ToLocalSpace(screenSpacePosition)); + (Vector2 pos, double time) = distanceSnapGrid.GetSnappedPosition(distanceSnapGrid.ToLocalSpace(screenSpacePosition)); + return new SnapResult(distanceSnapGrid.ToScreenSpace(pos), time); + } public override float GetBeatSnapDistanceAt(double referenceTime) { @@ -326,9 +329,7 @@ namespace osu.Game.Rulesets.Edit [CanBeNull] protected virtual DistanceSnapGrid CreateDistanceSnapGrid([NotNull] IEnumerable selectedHitObjects) => null; - public abstract (Vector2 position, double time) SnapPositionToValidTime(Vector2 position); - - public abstract (Vector2 position, double time) SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition); + public abstract SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition); public abstract float GetBeatSnapDistanceAt(double referenceTime); diff --git a/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs b/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs index 93cb605132..d95800f403 100644 --- a/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs +++ b/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs @@ -8,13 +8,11 @@ namespace osu.Game.Rulesets.Edit public interface IPositionSnapProvider { /// - /// Given a position (local to the provider), find a valid time snap + /// Given a position, find a valid time snap. /// - /// The local position to be snapped. + /// The screen-space position to be snapped. /// The time and position post-snapping. - (Vector2 position, double time) SnapPositionToValidTime(Vector2 position); - - (Vector2 position, double time) SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition); + SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition); /// /// Retrieves the distance between two points within a timing point that are one beat length apart. @@ -55,4 +53,23 @@ namespace osu.Game.Rulesets.Edit /// A value that represents snapped to the closest beat of the timing point. float GetSnappedDistanceFromDistance(double referenceTime, float distance); } + + public class SnapResult + { + /// + /// The screen space position, potentially altered for snapping. + /// + public Vector2 ScreenSpacePosition; + + /// + /// The resultant time for snapping, if a value could be attained. + /// + public double? Time; + + public SnapResult(Vector2 screenSpacePosition, double? time) + { + ScreenSpacePosition = screenSpacePosition; + Time = time; + } + } } diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index d1cae6b3cd..e38df3d812 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -405,16 +405,19 @@ namespace osu.Game.Screens.Edit.Compose.Components Vector2 movePosition = movementBlueprintOriginalPosition.Value + e.ScreenSpaceMousePosition - e.ScreenSpaceMouseDownPosition; // Retrieve a snapped position. - (Vector2 snappedPosition, double snappedTime) = snapProvider.SnapScreenSpacePositionToValidTime(movePosition); + var result = snapProvider.SnapScreenSpacePositionToValidTime(movePosition); // Move the hitobjects. - if (!selectionHandler.HandleMovement(new MoveSelectionEvent(movementBlueprint, ToScreenSpace(snappedPosition)))) + if (!selectionHandler.HandleMovement(new MoveSelectionEvent(movementBlueprint, result.ScreenSpacePosition))) return true; - // Apply the start time at the newly snapped-to position - double offset = snappedTime - draggedObject.StartTime; - foreach (HitObject obj in selectionHandler.SelectedHitObjects) - obj.StartTime += offset; + if (result.Time.HasValue) + { + // Apply the start time at the newly snapped-to position + double offset = result.Time.Value - draggedObject.StartTime; + foreach (HitObject obj in selectionHandler.SelectedHitObjects) + obj.StartTime += offset; + } return true; } diff --git a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs index bb6094ebe8..e1a4bca1d6 100644 --- a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs @@ -67,7 +67,7 @@ namespace osu.Game.Screens.Edit.Compose.Components private void updatePlacementPosition(Vector2 screenSpacePosition) { - Vector2 snappedPlayfieldPosition = composer.SnapScreenSpacePositionToValidTime(screenSpacePosition).position; + Vector2 snappedPlayfieldPosition = composer.SnapScreenSpacePositionToValidTime(screenSpacePosition).ScreenSpacePosition; currentPlacement.UpdatePosition(ToScreenSpace(snappedPlayfieldPosition)); } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index 1006da28df..ec2b11c0cf 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -181,11 +181,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline [Resolved] private IBeatSnapProvider beatSnapProvider { get; set; } - public (Vector2 position, double time) SnapPositionToValidTime(Vector2 position) => - (position, beatSnapProvider.SnapTime(getTimeFromPosition(position))); - - public (Vector2 position, double time) SnapScreenSpacePositionToValidTime(Vector2 position) => - (position, beatSnapProvider.SnapTime(getTimeFromPosition(Content.ToLocalSpace(position)))); + public SnapResult SnapScreenSpacePositionToValidTime(Vector2 position) => + new SnapResult(position, beatSnapProvider.SnapTime(getTimeFromPosition(Content.ToLocalSpace(position)))); private double getTimeFromPosition(Vector2 localPosition) => (localPosition.X / Content.DrawWidth) * track.Length; diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index b5eae26f98..03e05b75c5 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -275,32 +275,33 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline OnDragHandled?.Invoke(e); - var time = timeline.SnapScreenSpacePositionToValidTime(e.ScreenSpaceMousePosition).time; - - switch (hitObject) + if (timeline.SnapScreenSpacePositionToValidTime(e.ScreenSpaceMousePosition).Time is double time) { - case IHasRepeats repeatHitObject: - // find the number of repeats which can fit in the requested time. - var lengthOfOneRepeat = repeatHitObject.Duration / (repeatHitObject.RepeatCount + 1); - var proposedCount = Math.Max(0, (int)((time - hitObject.StartTime) / lengthOfOneRepeat) - 1); + switch (hitObject) + { + case IHasRepeats repeatHitObject: + // find the number of repeats which can fit in the requested time. + var lengthOfOneRepeat = repeatHitObject.Duration / (repeatHitObject.RepeatCount + 1); + var proposedCount = Math.Max(0, (int)((time - hitObject.StartTime) / lengthOfOneRepeat) - 1); - if (proposedCount == repeatHitObject.RepeatCount) - return; + if (proposedCount == repeatHitObject.RepeatCount) + return; - repeatHitObject.RepeatCount = proposedCount; - break; + repeatHitObject.RepeatCount = proposedCount; + break; - case IHasEndTime endTimeHitObject: - var snappedTime = Math.Max(hitObject.StartTime, beatSnapProvider.SnapTime(time)); + case IHasEndTime endTimeHitObject: + var snappedTime = Math.Max(hitObject.StartTime, beatSnapProvider.SnapTime(time)); - if (endTimeHitObject.EndTime == snappedTime) - return; + if (endTimeHitObject.EndTime == snappedTime) + return; - endTimeHitObject.EndTime = snappedTime; - break; + endTimeHitObject.EndTime = snappedTime; + break; + } + + beatmap.UpdateHitObject(hitObject); } - - beatmap.UpdateHitObject(hitObject); } protected override void OnDragEnd(DragEndEvent e) From ffb8d48fc30874ff81a7c4c6be3dad7fe70fc83b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 May 2020 18:32:36 +0900 Subject: [PATCH 0811/1117] Fix osu!mania editor placement regressions --- osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs | 2 +- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 2 ++ .../Edit/Compose/Components/ComposeBlueprintContainer.cs | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index f7951fcc5d..3e1d4f2f3a 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -63,7 +63,7 @@ namespace osu.Game.Rulesets.Mania.Edit drawableRuleset.ScrollingInfo.TimeRange.Value, hoc.DrawHeight); - return new SnapResult(targetPosition, targetTime); + return new SnapResult(screenSpacePosition, targetTime); } protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 7e9bb850af..5018b7bcb6 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -269,7 +269,9 @@ namespace osu.Game.Rulesets.Edit { if (distanceSnapGrid == null) return new SnapResult(screenSpacePosition, null); + // TODO: move distance snap grid to OsuHitObjectComposer. (Vector2 pos, double time) = distanceSnapGrid.GetSnappedPosition(distanceSnapGrid.ToLocalSpace(screenSpacePosition)); + return new SnapResult(distanceSnapGrid.ToScreenSpace(pos), time); } diff --git a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs index e1a4bca1d6..95e2f41f1f 100644 --- a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs @@ -67,9 +67,9 @@ namespace osu.Game.Screens.Edit.Compose.Components private void updatePlacementPosition(Vector2 screenSpacePosition) { - Vector2 snappedPlayfieldPosition = composer.SnapScreenSpacePositionToValidTime(screenSpacePosition).ScreenSpacePosition; + var snapResult = composer.SnapScreenSpacePositionToValidTime(screenSpacePosition); - currentPlacement.UpdatePosition(ToScreenSpace(snappedPlayfieldPosition)); + currentPlacement.UpdatePosition(snapResult.ScreenSpacePosition); } #endregion From 23bf0d000e1013594129a57e24020d4c0e5312b4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 May 2020 18:40:55 +0900 Subject: [PATCH 0812/1117] Implement mania beat snapping support --- .../Edit/ManiaHitObjectComposer.cs | 9 ++++++++- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 12 ++++++------ .../Compose/Components/ComposeBlueprintContainer.cs | 9 ++++----- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index 3e1d4f2f3a..053dcd0832 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -63,7 +63,14 @@ namespace osu.Game.Rulesets.Mania.Edit drawableRuleset.ScrollingInfo.TimeRange.Value, hoc.DrawHeight); - return new SnapResult(screenSpacePosition, targetTime); + targetTime = BeatSnapProvider.SnapTime(targetTime); + + screenSpacePosition.Y = hoc.ToScreenSpace( + new Vector2(0, drawableRuleset.ScrollingInfo.Algorithm.PositionAt(targetTime, EditorClock.CurrentTime, drawableRuleset.ScrollingInfo.TimeRange.Value, + hoc.DrawHeight)) + ).Y; + + return new SnapResult(screenSpacePosition, BeatSnapProvider.SnapTime(targetTime)); } protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 5018b7bcb6..b45cdea751 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Edit private IAdjustableClock adjustableClock { get; set; } [Resolved] - private IBeatSnapProvider beatSnapProvider { get; set; } + protected IBeatSnapProvider BeatSnapProvider { get; private set; } protected ComposeBlueprintContainer BlueprintContainer { get; private set; } @@ -278,27 +278,27 @@ namespace osu.Game.Rulesets.Edit public override float GetBeatSnapDistanceAt(double referenceTime) { DifficultyControlPoint difficultyPoint = EditorBeatmap.ControlPointInfo.DifficultyPointAt(referenceTime); - return (float)(100 * EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier / beatSnapProvider.BeatDivisor); + return (float)(100 * EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier / BeatSnapProvider.BeatDivisor); } public override float DurationToDistance(double referenceTime, double duration) { - double beatLength = beatSnapProvider.GetBeatLengthAtTime(referenceTime); + double beatLength = BeatSnapProvider.GetBeatLengthAtTime(referenceTime); return (float)(duration / beatLength * GetBeatSnapDistanceAt(referenceTime)); } public override double DistanceToDuration(double referenceTime, float distance) { - double beatLength = beatSnapProvider.GetBeatLengthAtTime(referenceTime); + double beatLength = BeatSnapProvider.GetBeatLengthAtTime(referenceTime); return distance / GetBeatSnapDistanceAt(referenceTime) * beatLength; } public override double GetSnappedDurationFromDistance(double referenceTime, float distance) - => beatSnapProvider.SnapTime(referenceTime + DistanceToDuration(referenceTime, distance), referenceTime) - referenceTime; + => BeatSnapProvider.SnapTime(referenceTime + DistanceToDuration(referenceTime, distance), referenceTime) - referenceTime; public override float GetSnappedDistanceFromDistance(double referenceTime, float distance) { - var snappedEndTime = beatSnapProvider.SnapTime(referenceTime + DistanceToDuration(referenceTime, distance), referenceTime); + var snappedEndTime = BeatSnapProvider.SnapTime(referenceTime + DistanceToDuration(referenceTime, distance), referenceTime); return DurationToDistance(referenceTime, snappedEndTime - referenceTime); } diff --git a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs index 95e2f41f1f..7982cba4e3 100644 --- a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs @@ -11,7 +11,6 @@ using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; -using osuTK; namespace osu.Game.Screens.Edit.Compose.Components { @@ -65,9 +64,9 @@ namespace osu.Game.Screens.Edit.Compose.Components createPlacement(); } - private void updatePlacementPosition(Vector2 screenSpacePosition) + private void updatePlacementPosition() { - var snapResult = composer.SnapScreenSpacePositionToValidTime(screenSpacePosition); + var snapResult = composer.SnapScreenSpacePositionToValidTime(inputManager.CurrentState.Mouse.Position); currentPlacement.UpdatePosition(snapResult.ScreenSpacePosition); } @@ -84,7 +83,7 @@ namespace osu.Game.Screens.Edit.Compose.Components removePlacement(); if (currentPlacement != null) - updatePlacementPosition(inputManager.CurrentState.Mouse.Position); + updatePlacementPosition(); } protected sealed override SelectionBlueprint CreateBlueprintFor(HitObject hitObject) @@ -116,7 +115,7 @@ namespace osu.Game.Screens.Edit.Compose.Components placementBlueprintContainer.Child = currentPlacement = blueprint; // Fixes a 1-frame position discrepancy due to the first mouse move event happening in the next frame - updatePlacementPosition(inputManager.CurrentState.Mouse.Position); + updatePlacementPosition(); } } From 970bd86d2e1df8b109046ff0265d01b850b9f435 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 May 2020 18:46:15 +0900 Subject: [PATCH 0813/1117] Remove local TimeAt usage in mania placement --- .../Edit/Blueprints/ManiaPlacementBlueprint.cs | 2 +- .../Edit/ManiaHitObjectComposer.cs | 11 +++++++---- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 3 +-- osu.Game/Rulesets/Edit/PlacementBlueprint.cs | 5 +---- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs index 3fb03d642f..4ebc4dae1a 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs @@ -54,7 +54,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints return base.OnMouseDown(e); HitObject.Column = Column.Index; - BeginPlacement(TimeAt(e.ScreenSpaceMousePosition), true); + BeginPlacement(true); return true; } diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index 053dcd0832..724786a1c0 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -46,19 +46,22 @@ namespace osu.Game.Rulesets.Mania.Edit public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) { - var hoc = Playfield.GetColumn(0).HitObjectContainer; + var hoc = ColumnAt(screenSpacePosition)?.HitObjectContainer; - Vector2 targetPosition = hoc.ToLocalSpace(screenSpacePosition); + if (hoc == null) + return new SnapResult(screenSpacePosition, null); + + Vector2 localPosition = hoc.ToLocalSpace(screenSpacePosition); if (drawableRuleset.ScrollingInfo.Direction.Value == ScrollingDirection.Down) { // We're dealing with screen coordinates in which the position decreases towards the centre of the screen resulting in an increase in start time. // The scrolling algorithm instead assumes a top anchor meaning an increase in time corresponds to an increase in position, // so when scrolling downwards the coordinates need to be flipped. - targetPosition.Y = hoc.DrawHeight - targetPosition.Y; + localPosition.Y = hoc.DrawHeight - localPosition.Y; } - double targetTime = drawableRuleset.ScrollingInfo.Algorithm.TimeAt(targetPosition.Y, + double targetTime = drawableRuleset.ScrollingInfo.Algorithm.TimeAt(localPosition.Y, EditorClock.CurrentTime, drawableRuleset.ScrollingInfo.TimeRange.Value, hoc.DrawHeight); diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index b45cdea751..1e328e6b6b 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -245,8 +245,7 @@ namespace osu.Game.Rulesets.Edit { EditorBeatmap.PlacementObject.Value = hitObject; - if (SnapScreenSpacePositionToValidTime(inputManager.CurrentState.Mouse.Position).Time is double time) - hitObject.StartTime = time; + hitObject.StartTime = SnapScreenSpacePositionToValidTime(inputManager.CurrentState.Mouse.Position).Time ?? EditorClock.CurrentTime; } public void EndPlacement(HitObject hitObject, bool commit) diff --git a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs index c06e50950c..5c506926b8 100644 --- a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs +++ b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs @@ -61,12 +61,9 @@ namespace osu.Game.Rulesets.Edit /// /// Signals that the placement of has started. /// - /// The start time of at the placement point. If null, the current clock time is used. /// Whether this call is committing a value for HitObject.StartTime and continuing with further adjustments. - protected void BeginPlacement(double? startTime = null, bool commitStart = false) + protected void BeginPlacement(bool commitStart = false) { - HitObject.StartTime = startTime ?? EditorClock.CurrentTime; - // applies snapping to above time placementHandler.BeginPlacement(HitObject); From 82d6549161052871355aab9327e7a676e728081c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 May 2020 19:05:03 +0900 Subject: [PATCH 0814/1117] Pass down snap result and remove local TimeAt usage --- .../Blueprints/HoldNotePlacementBlueprint.cs | 17 +++++++------ .../Blueprints/ManiaPlacementBlueprint.cs | 25 +++---------------- .../HitCircles/HitCirclePlacementBlueprint.cs | 3 +-- .../Sliders/SliderPlacementBlueprint.cs | 4 +-- .../Spinners/SpinnerPlacementBlueprint.cs | 3 +-- osu.Game/Rulesets/Edit/PlacementBlueprint.cs | 4 +-- .../Components/ComposeBlueprintContainer.cs | 2 +- .../Visual/PlacementBlueprintTestScene.cs | 4 +-- 8 files changed, 22 insertions(+), 40 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs index c63e30e98a..5dbd84e370 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs @@ -4,6 +4,7 @@ using System; using osu.Framework.Graphics; using osu.Framework.Input.Events; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Mania.Edit.Blueprints.Components; using osu.Game.Rulesets.Mania.Objects; using osuTK; @@ -59,23 +60,25 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints private double originalStartTime; - public override void UpdatePosition(Vector2 screenSpacePosition) + public override void UpdatePosition(SnapResult result) { - base.UpdatePosition(screenSpacePosition); + base.UpdatePosition(result); if (PlacementActive) { - var endTime = TimeAt(screenSpacePosition); - - HitObject.StartTime = endTime < originalStartTime ? endTime : originalStartTime; - HitObject.Duration = Math.Abs(endTime - originalStartTime); + if (result.Time is double endTime) + { + HitObject.StartTime = endTime < originalStartTime ? endTime : originalStartTime; + HitObject.Duration = Math.Abs(endTime - originalStartTime); + } } else { headPiece.Width = tailPiece.Width = SnappedWidth; headPiece.X = tailPiece.X = SnappedMousePosition.X; - originalStartTime = HitObject.StartTime = TimeAt(screenSpacePosition); + if (result.Time is double startTime) + originalStartTime = HitObject.StartTime = startTime; } } } diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs index 4ebc4dae1a..4d10afd289 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs @@ -58,10 +58,10 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints return true; } - public override void UpdatePosition(Vector2 screenSpacePosition) + public override void UpdatePosition(SnapResult result) { if (!PlacementActive) - Column = ColumnAt(screenSpacePosition); + Column = ColumnAt(result.ScreenSpacePosition); if (Column == null) return; @@ -69,26 +69,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints // Snap to the column var parentPos = Parent.ToLocalSpace(Column.ToScreenSpace(new Vector2(Column.DrawWidth / 2, 0))); - SnappedMousePosition = new Vector2(parentPos.X, Parent.ToLocalSpace(screenSpacePosition).Y); - } - - protected double TimeAt(Vector2 screenSpacePosition) - { - if (Column == null) - return 0; - - var hitObjectContainer = Column.HitObjectContainer; - - // If we're scrolling downwards, a position of 0 is actually further away from the hit target - // so we need to flip the vertical coordinate in the hitobject container's space - var hitObjectPos = mouseToHitObjectPosition(Column.HitObjectContainer.ToLocalSpace(screenSpacePosition)).Y; - if (scrollingInfo.Direction.Value == ScrollingDirection.Down) - hitObjectPos = hitObjectContainer.DrawHeight - hitObjectPos; - - return scrollingInfo.Algorithm.TimeAt(hitObjectPos, - EditorClock.CurrentTime, - scrollingInfo.TimeRange.Value, - hitObjectContainer.DrawHeight); + SnappedMousePosition = new Vector2(parentPos.X, Parent.ToLocalSpace(result.ScreenSpacePosition).Y); } protected float PositionAt(double time) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/HitCirclePlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/HitCirclePlacementBlueprint.cs index dad199715e..e12dec2668 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/HitCirclePlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/HitCirclePlacementBlueprint.cs @@ -5,7 +5,6 @@ using osu.Framework.Input.Events; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components; using osu.Game.Rulesets.Osu.Objects; -using osuTK; using osuTK.Input; namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles @@ -40,6 +39,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles return base.OnMouseDown(e); } - public override void UpdatePosition(Vector2 screenSpacePosition) => HitObject.Position = ToLocalSpace(screenSpacePosition); + public override void UpdatePosition(SnapResult result) => HitObject.Position = ToLocalSpace(result.ScreenSpacePosition); } } diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index ac30f5a762..59ec92c79e 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -67,13 +67,13 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders inputManager = GetContainingInputManager(); } - public override void UpdatePosition(Vector2 screenSpacePosition) + public override void UpdatePosition(SnapResult result) { switch (state) { case PlacementState.Initial: BeginPlacement(); - HitObject.Position = ToLocalSpace(screenSpacePosition); + HitObject.Position = ToLocalSpace(result.ScreenSpacePosition); break; case PlacementState.Body: diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/SpinnerPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/SpinnerPlacementBlueprint.cs index 74b563d922..546f0e5981 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/SpinnerPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/SpinnerPlacementBlueprint.cs @@ -8,7 +8,6 @@ using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners.Components; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.UI; -using osuTK; using osuTK.Input; namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners @@ -61,7 +60,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners return true; } - public override void UpdatePosition(Vector2 screenSpacePosition) + public override void UpdatePosition(SnapResult result) { } } diff --git a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs index 5c506926b8..bab9bf71ef 100644 --- a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs +++ b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs @@ -86,8 +86,8 @@ namespace osu.Game.Rulesets.Edit /// /// Updates the position of this to a new screen-space position. /// - /// The screen-space position. - public abstract void UpdatePosition(Vector2 screenSpacePosition); + /// The snap result information. + public abstract void UpdatePosition(SnapResult snapResult); /// /// Invokes , diff --git a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs index 7982cba4e3..0b5d8262fd 100644 --- a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs @@ -68,7 +68,7 @@ namespace osu.Game.Screens.Edit.Compose.Components { var snapResult = composer.SnapScreenSpacePositionToValidTime(inputManager.CurrentState.Mouse.Position); - currentPlacement.UpdatePosition(snapResult.ScreenSpacePosition); + currentPlacement.UpdatePosition(snapResult); } #endregion diff --git a/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs b/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs index dc67d28f63..a4e629b6f5 100644 --- a/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs +++ b/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs @@ -71,7 +71,7 @@ namespace osu.Game.Tests.Visual { base.Update(); - currentBlueprint.UpdatePosition(InputManager.CurrentState.Mouse.Position); + currentBlueprint.UpdatePosition(new SnapResult(InputManager.CurrentState.Mouse.Position, null)); } public override void Add(Drawable drawable) @@ -81,7 +81,7 @@ namespace osu.Game.Tests.Visual if (drawable is PlacementBlueprint blueprint) { blueprint.Show(); - blueprint.UpdatePosition(InputManager.CurrentState.Mouse.Position); + blueprint.UpdatePosition(new SnapResult(InputManager.CurrentState.Mouse.Position, null)); } } From 62092e3f5b4c60b18505a502a311b5db3dc1aa6a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 May 2020 19:09:04 +0900 Subject: [PATCH 0815/1117] Propagate mania column in SnapResult --- .../Blueprints/ManiaPlacementBlueprint.cs | 8 +------- .../Edit/ManiaHitObjectComposer.cs | 19 ++++++++++++++++--- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs index 4d10afd289..2f1b38d564 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs @@ -33,9 +33,6 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints /// protected float SnappedWidth { get; private set; } - [Resolved] - private IManiaHitObjectComposer composer { get; set; } - [Resolved] private IScrollingInfo scrollingInfo { get; set; } @@ -61,7 +58,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints public override void UpdatePosition(SnapResult result) { if (!PlacementActive) - Column = ColumnAt(result.ScreenSpacePosition); + Column = (result as ManiaSnapResult)?.Column; if (Column == null) return; @@ -85,9 +82,6 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints return hitObjectToMousePosition(Column.HitObjectContainer.ToSpaceOfOtherDrawable(new Vector2(0, pos), Parent)).Y; } - protected Column ColumnAt(Vector2 screenSpacePosition) - => composer.ColumnAt(screenSpacePosition); - /// /// Converts a mouse position to a hitobject position. /// diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index 724786a1c0..89ccf0019a 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -46,11 +46,13 @@ namespace osu.Game.Rulesets.Mania.Edit public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) { - var hoc = ColumnAt(screenSpacePosition)?.HitObjectContainer; + var column = ColumnAt(screenSpacePosition); - if (hoc == null) + if (column == null) return new SnapResult(screenSpacePosition, null); + var hoc = column.HitObjectContainer; + Vector2 localPosition = hoc.ToLocalSpace(screenSpacePosition); if (drawableRuleset.ScrollingInfo.Direction.Value == ScrollingDirection.Down) @@ -73,7 +75,7 @@ namespace osu.Game.Rulesets.Mania.Edit hoc.DrawHeight)) ).Y; - return new SnapResult(screenSpacePosition, BeatSnapProvider.SnapTime(targetTime)); + return new ManiaSnapResult(screenSpacePosition, BeatSnapProvider.SnapTime(targetTime), column); } protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) @@ -94,4 +96,15 @@ namespace osu.Game.Rulesets.Mania.Edit new HoldNoteCompositionTool() }; } + + public class ManiaSnapResult : SnapResult + { + public readonly Column Column; + + public ManiaSnapResult(Vector2 screenSpacePosition, double time, Column column) + : base(screenSpacePosition, time) + { + Column = column; + } + } } From 2f78866dfb452c091cca286ec1d7744a3c4deab3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 May 2020 19:23:17 +0900 Subject: [PATCH 0816/1117] Move positioning out of mania blueprints --- .../Blueprints/HoldNotePlacementBlueprint.cs | 7 +++- .../Blueprints/ManiaPlacementBlueprint.cs | 42 ------------------- .../Edit/Blueprints/NotePlacementBlueprint.cs | 20 +++++---- .../Edit/ManiaHitObjectComposer.cs | 17 ++++---- 4 files changed, 26 insertions(+), 60 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs index 5dbd84e370..055b92b39d 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs @@ -74,8 +74,11 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints } else { - headPiece.Width = tailPiece.Width = SnappedWidth; - headPiece.X = tailPiece.X = SnappedMousePosition.X; + if (result is ManiaSnapResult maniaResult) + { + headPiece.Width = tailPiece.Width = maniaResult.Column.DrawWidth; + headPiece.X = tailPiece.X = ToLocalSpace(result.ScreenSpacePosition).X; + } if (result.Time is double startTime) originalStartTime = HitObject.StartTime = startTime; diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs index 2f1b38d564..e8c7aea814 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs @@ -23,16 +23,6 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints protected Column Column; - /// - /// The current mouse position, snapped to the closest column. - /// - protected Vector2 SnappedMousePosition { get; private set; } - - /// - /// The width of the closest column to the current mouse position. - /// - protected float SnappedWidth { get; private set; } - [Resolved] private IScrollingInfo scrollingInfo { get; set; } @@ -59,14 +49,6 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints { if (!PlacementActive) Column = (result as ManiaSnapResult)?.Column; - - if (Column == null) return; - - SnappedWidth = Column.DrawWidth; - - // Snap to the column - var parentPos = Parent.ToLocalSpace(Column.ToScreenSpace(new Vector2(Column.DrawWidth / 2, 0))); - SnappedMousePosition = new Vector2(parentPos.X, Parent.ToLocalSpace(result.ScreenSpacePosition).Y); } protected float PositionAt(double time) @@ -82,30 +64,6 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints return hitObjectToMousePosition(Column.HitObjectContainer.ToSpaceOfOtherDrawable(new Vector2(0, pos), Parent)).Y; } - /// - /// Converts a mouse position to a hitobject position. - /// - /// - /// Blueprints are centred on the mouse position, such that the hitobject position is anchored at the top or bottom of the blueprint depending on the scroll direction. - /// - /// The mouse position. - /// The resulting hitobject position, acnhored at the top or bottom of the blueprint depending on the scroll direction. - private Vector2 mouseToHitObjectPosition(Vector2 mousePosition) - { - switch (scrollingInfo.Direction.Value) - { - case ScrollingDirection.Up: - mousePosition.Y -= DefaultNotePiece.NOTE_HEIGHT / 2; - break; - - case ScrollingDirection.Down: - mousePosition.Y += DefaultNotePiece.NOTE_HEIGHT / 2; - break; - } - - return mousePosition; - } - /// /// Converts a hitobject position to a mouse position. /// diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/NotePlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/NotePlacementBlueprint.cs index a4c0791253..5f6db2e6dd 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/NotePlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/NotePlacementBlueprint.cs @@ -3,6 +3,7 @@ using osu.Framework.Graphics; using osu.Framework.Input.Events; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Mania.Edit.Blueprints.Components; using osu.Game.Rulesets.Mania.Objects; using osuTK.Input; @@ -11,22 +12,25 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints { public class NotePlacementBlueprint : ManiaPlacementBlueprint { + private readonly EditNotePiece piece; + public NotePlacementBlueprint() : base(new Note()) { - Origin = Anchor.Centre; + RelativeSizeAxes = Axes.Both; - AutoSizeAxes = Axes.Y; - - InternalChild = new EditNotePiece { RelativeSizeAxes = Axes.X }; + InternalChild = piece = new EditNotePiece { Origin = Anchor.Centre }; } - protected override void Update() + public override void UpdatePosition(SnapResult result) { - base.Update(); + base.UpdatePosition(result); - Width = SnappedWidth; - Position = SnappedMousePosition; + if (result is ManiaSnapResult maniaResult) + { + piece.Width = maniaResult.Column.DrawWidth; + piece.Position = ToLocalSpace(result.ScreenSpacePosition); + } } protected override bool OnMouseDown(MouseDownEvent e) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index 89ccf0019a..c38952fd29 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -55,7 +55,9 @@ namespace osu.Game.Rulesets.Mania.Edit Vector2 localPosition = hoc.ToLocalSpace(screenSpacePosition); - if (drawableRuleset.ScrollingInfo.Direction.Value == ScrollingDirection.Down) + var scrollInfo = drawableRuleset.ScrollingInfo; + + if (scrollInfo.Direction.Value == ScrollingDirection.Down) { // We're dealing with screen coordinates in which the position decreases towards the centre of the screen resulting in an increase in start time. // The scrolling algorithm instead assumes a top anchor meaning an increase in time corresponds to an increase in position, @@ -63,19 +65,18 @@ namespace osu.Game.Rulesets.Mania.Edit localPosition.Y = hoc.DrawHeight - localPosition.Y; } - double targetTime = drawableRuleset.ScrollingInfo.Algorithm.TimeAt(localPosition.Y, + double targetTime = scrollInfo.Algorithm.TimeAt(localPosition.Y, EditorClock.CurrentTime, - drawableRuleset.ScrollingInfo.TimeRange.Value, + scrollInfo.TimeRange.Value, hoc.DrawHeight); targetTime = BeatSnapProvider.SnapTime(targetTime); - screenSpacePosition.Y = hoc.ToScreenSpace( - new Vector2(0, drawableRuleset.ScrollingInfo.Algorithm.PositionAt(targetTime, EditorClock.CurrentTime, drawableRuleset.ScrollingInfo.TimeRange.Value, - hoc.DrawHeight)) - ).Y; + var localPos = new Vector2( + hoc.DrawWidth / 2, + scrollInfo.Algorithm.PositionAt(targetTime, EditorClock.CurrentTime, scrollInfo.TimeRange.Value, hoc.DrawHeight)); - return new ManiaSnapResult(screenSpacePosition, BeatSnapProvider.SnapTime(targetTime), column); + return new ManiaSnapResult(hoc.ToScreenSpace(localPos), BeatSnapProvider.SnapTime(targetTime), column); } protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) From 26fb779f4d7b6929d90ecc434070d88401046387 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 May 2020 19:52:57 +0900 Subject: [PATCH 0817/1117] Move remaining positioning logic local to hold note blueprint --- .../Blueprints/HoldNotePlacementBlueprint.cs | 22 +++++++++- .../Blueprints/ManiaPlacementBlueprint.cs | 41 ------------------- 2 files changed, 20 insertions(+), 43 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs index 055b92b39d..38a12ed2ed 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs @@ -2,11 +2,13 @@ // 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.Input.Events; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Mania.Edit.Blueprints.Components; using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.UI.Scrolling; using osuTK; using osuTK.Input; @@ -37,8 +39,8 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints if (Column != null) { - headPiece.Y = PositionAt(HitObject.StartTime); - tailPiece.Y = PositionAt(HitObject.EndTime); + headPiece.Y = positionAt(HitObject.StartTime); + tailPiece.Y = positionAt(HitObject.EndTime); } var topPosition = new Vector2(headPiece.DrawPosition.X, Math.Min(headPiece.DrawPosition.Y, tailPiece.DrawPosition.Y)); @@ -84,5 +86,21 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints originalStartTime = HitObject.StartTime = startTime; } } + + [Resolved] + private IScrollingInfo scrollingInfo { get; set; } + + private float positionAt(double time) + { + var pos = scrollingInfo.Algorithm.PositionAt(time, + EditorClock.CurrentTime, + scrollingInfo.TimeRange.Value, + Column.HitObjectContainer.DrawHeight); + + if (scrollingInfo.Direction.Value == ScrollingDirection.Down) + pos = Column.HitObjectContainer.DrawHeight - pos; + + return Column.HitObjectContainer.ToSpaceOfOtherDrawable(new Vector2(0, pos), Parent).Y; + } } } diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs index e8c7aea814..e83495cec4 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs @@ -1,16 +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.Allocation; using osu.Framework.Graphics; using osu.Framework.Input; using osu.Framework.Input.Events; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Mania.Objects; -using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; using osu.Game.Rulesets.Mania.UI; -using osu.Game.Rulesets.UI.Scrolling; -using osuTK; using osuTK.Input; namespace osu.Game.Rulesets.Mania.Edit.Blueprints @@ -23,9 +19,6 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints protected Column Column; - [Resolved] - private IScrollingInfo scrollingInfo { get; set; } - protected ManiaPlacementBlueprint(T hitObject) : base(hitObject) { @@ -50,39 +43,5 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints if (!PlacementActive) Column = (result as ManiaSnapResult)?.Column; } - - protected float PositionAt(double time) - { - var pos = scrollingInfo.Algorithm.PositionAt(time, - EditorClock.CurrentTime, - scrollingInfo.TimeRange.Value, - Column.HitObjectContainer.DrawHeight); - - if (scrollingInfo.Direction.Value == ScrollingDirection.Down) - pos = Column.HitObjectContainer.DrawHeight - pos; - - return hitObjectToMousePosition(Column.HitObjectContainer.ToSpaceOfOtherDrawable(new Vector2(0, pos), Parent)).Y; - } - - /// - /// Converts a hitobject position to a mouse position. - /// - /// The hitobject position. - /// The resulting mouse position, anchored at the centre of the hitobject. - private Vector2 hitObjectToMousePosition(Vector2 hitObjectPosition) - { - switch (scrollingInfo.Direction.Value) - { - case ScrollingDirection.Up: - hitObjectPosition.Y += DefaultNotePiece.NOTE_HEIGHT / 2; - break; - - case ScrollingDirection.Down: - hitObjectPosition.Y -= DefaultNotePiece.NOTE_HEIGHT / 2; - break; - } - - return hitObjectPosition; - } } } From 19e2da9c73e03b674ad2525bc59bf8b6d8e16571 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 May 2020 19:59:36 +0900 Subject: [PATCH 0818/1117] Fix down scrolling giving incorrect positioning data --- osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index c38952fd29..7ad2ce4699 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -72,11 +72,17 @@ namespace osu.Game.Rulesets.Mania.Edit targetTime = BeatSnapProvider.SnapTime(targetTime); - var localPos = new Vector2( + localPosition = new Vector2( hoc.DrawWidth / 2, scrollInfo.Algorithm.PositionAt(targetTime, EditorClock.CurrentTime, scrollInfo.TimeRange.Value, hoc.DrawHeight)); - return new ManiaSnapResult(hoc.ToScreenSpace(localPos), BeatSnapProvider.SnapTime(targetTime), column); + if (scrollInfo.Direction.Value == ScrollingDirection.Down) + { + // reapply the above. + localPosition.Y = hoc.DrawHeight - localPosition.Y; + } + + return new ManiaSnapResult(hoc.ToScreenSpace(localPosition), BeatSnapProvider.SnapTime(targetTime), column); } protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) From 7c9fbb6fcfb6a6fdfe6706bb1b320080a76ffc1b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 May 2020 21:03:03 +0900 Subject: [PATCH 0819/1117] Split out classes --- .../Edit/ManiaHitObjectComposer.cs | 11 ------- .../Edit/ManiaSnapResult.cs | 20 +++++++++++++ .../Rulesets/Edit/IPositionSnapProvider.cs | 19 ------------ osu.Game/Rulesets/Edit/SnapResult.cs | 29 +++++++++++++++++++ 4 files changed, 49 insertions(+), 30 deletions(-) create mode 100644 osu.Game.Rulesets.Mania/Edit/ManiaSnapResult.cs create mode 100644 osu.Game/Rulesets/Edit/SnapResult.cs diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index 7ad2ce4699..3287c10531 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -103,15 +103,4 @@ namespace osu.Game.Rulesets.Mania.Edit new HoldNoteCompositionTool() }; } - - public class ManiaSnapResult : SnapResult - { - public readonly Column Column; - - public ManiaSnapResult(Vector2 screenSpacePosition, double time, Column column) - : base(screenSpacePosition, time) - { - Column = column; - } - } } diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaSnapResult.cs b/osu.Game.Rulesets.Mania/Edit/ManiaSnapResult.cs new file mode 100644 index 0000000000..b94f5e51c4 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Edit/ManiaSnapResult.cs @@ -0,0 +1,20 @@ +// 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.Edit; +using osu.Game.Rulesets.Mania.UI; +using osuTK; + +namespace osu.Game.Rulesets.Mania.Edit +{ + public class ManiaSnapResult : SnapResult + { + public readonly Column Column; + + public ManiaSnapResult(Vector2 screenSpacePosition, double time, Column column) + : base(screenSpacePosition, time) + { + Column = column; + } + } +} diff --git a/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs b/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs index d95800f403..c854c06031 100644 --- a/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs +++ b/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs @@ -53,23 +53,4 @@ namespace osu.Game.Rulesets.Edit /// A value that represents snapped to the closest beat of the timing point. float GetSnappedDistanceFromDistance(double referenceTime, float distance); } - - public class SnapResult - { - /// - /// The screen space position, potentially altered for snapping. - /// - public Vector2 ScreenSpacePosition; - - /// - /// The resultant time for snapping, if a value could be attained. - /// - public double? Time; - - public SnapResult(Vector2 screenSpacePosition, double? time) - { - ScreenSpacePosition = screenSpacePosition; - Time = time; - } - } } diff --git a/osu.Game/Rulesets/Edit/SnapResult.cs b/osu.Game/Rulesets/Edit/SnapResult.cs new file mode 100644 index 0000000000..5d07d7b233 --- /dev/null +++ b/osu.Game/Rulesets/Edit/SnapResult.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 osuTK; + +namespace osu.Game.Rulesets.Edit +{ + /// + /// The result of a position/time snapping process. + /// + public class SnapResult + { + /// + /// The screen space position, potentially altered for snapping. + /// + public Vector2 ScreenSpacePosition; + + /// + /// The resultant time for snapping, if a value could be attained. + /// + public double? Time; + + public SnapResult(Vector2 screenSpacePosition, double? time) + { + ScreenSpacePosition = screenSpacePosition; + Time = time; + } + } +} From e3cec9cf6c7251c16d075bf0b27a8546f7ca0692 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 May 2020 21:13:08 +0900 Subject: [PATCH 0820/1117] Simplify column assignment --- .../Edit/Blueprints/ManiaPlacementBlueprint.cs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs index e83495cec4..af57b4fa07 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs @@ -17,7 +17,20 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints { protected new T HitObject => (T)base.HitObject; - protected Column Column; + private Column column; + + public Column Column + { + get => column; + set + { + if (value == column) + return; + + column = value; + HitObject.Column = column.Index; + } + } protected ManiaPlacementBlueprint(T hitObject) : base(hitObject) @@ -31,9 +44,8 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints return false; if (Column == null) - return base.OnMouseDown(e); + return false; - HitObject.Column = Column.Index; BeginPlacement(true); return true; } From 63b5f1a376c1f6d3dce6c6040653297839acf04c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 May 2020 21:14:20 +0900 Subject: [PATCH 0821/1117] Remove unnecessary IRequireHighFrequencyMousePosition --- .../Edit/Blueprints/ManiaPlacementBlueprint.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs index af57b4fa07..8d3b3ea583 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; -using osu.Framework.Input; using osu.Framework.Input.Events; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Mania.Objects; @@ -11,8 +10,7 @@ using osuTK.Input; namespace osu.Game.Rulesets.Mania.Edit.Blueprints { - public abstract class ManiaPlacementBlueprint : PlacementBlueprint, - IRequireHighFrequencyMousePosition // the playfield could be moving behind us + public abstract class ManiaPlacementBlueprint : PlacementBlueprint where T : ManiaHitObject { protected new T HitObject => (T)base.HitObject; From 69db62b78a0d996843a07afc152b8f2aeea1c7e4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 May 2020 21:42:21 +0900 Subject: [PATCH 0822/1117] Combine implementation of time-to-position lookup --- .../ManiaPlacementBlueprintTestScene.cs | 2 ++ .../ManiaSelectionBlueprintTestScene.cs | 2 ++ .../Blueprints/HoldNotePlacementBlueprint.cs | 23 ++++----------- .../Edit/IManiaHitObjectComposer.cs | 2 ++ .../Edit/ManiaHitObjectComposer.cs | 28 ++++++++++++------- 5 files changed, 29 insertions(+), 28 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestScene.cs b/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestScene.cs index aac77c9c1c..be3e205f36 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestScene.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestScene.cs @@ -49,6 +49,8 @@ namespace osu.Game.Rulesets.Mania.Tests public Column ColumnAt(Vector2 screenSpacePosition) => column; + public Vector2 ScreenSpacePositionAtTime(double time, Column column = null) => Vector2.Zero; + public int TotalColumns => 1; } } diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaSelectionBlueprintTestScene.cs b/osu.Game.Rulesets.Mania.Tests/ManiaSelectionBlueprintTestScene.cs index b598893e8c..3d654466ed 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaSelectionBlueprintTestScene.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaSelectionBlueprintTestScene.cs @@ -33,6 +33,8 @@ namespace osu.Game.Rulesets.Mania.Tests public Column ColumnAt(Vector2 screenSpacePosition) => column; + public Vector2 ScreenSpacePositionAtTime(double time, Column column = null) => Vector2.Zero; + public int TotalColumns => 1; } } diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs index 38a12ed2ed..31bf76edd0 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs @@ -20,6 +20,9 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints private readonly EditNotePiece headPiece; private readonly EditNotePiece tailPiece; + [Resolved] + private IManiaHitObjectComposer composer { get; set; } + public HoldNotePlacementBlueprint() : base(new HoldNote()) { @@ -39,8 +42,8 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints if (Column != null) { - headPiece.Y = positionAt(HitObject.StartTime); - tailPiece.Y = positionAt(HitObject.EndTime); + headPiece.Y = Parent.ToLocalSpace(composer.ScreenSpacePositionAtTime(HitObject.StartTime, Column)).Y; + tailPiece.Y = Parent.ToLocalSpace(composer.ScreenSpacePositionAtTime(HitObject.EndTime, Column)).Y; } var topPosition = new Vector2(headPiece.DrawPosition.X, Math.Min(headPiece.DrawPosition.Y, tailPiece.DrawPosition.Y)); @@ -86,21 +89,5 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints originalStartTime = HitObject.StartTime = startTime; } } - - [Resolved] - private IScrollingInfo scrollingInfo { get; set; } - - private float positionAt(double time) - { - var pos = scrollingInfo.Algorithm.PositionAt(time, - EditorClock.CurrentTime, - scrollingInfo.TimeRange.Value, - Column.HitObjectContainer.DrawHeight); - - if (scrollingInfo.Direction.Value == ScrollingDirection.Down) - pos = Column.HitObjectContainer.DrawHeight - pos; - - return Column.HitObjectContainer.ToSpaceOfOtherDrawable(new Vector2(0, pos), Parent).Y; - } } } diff --git a/osu.Game.Rulesets.Mania/Edit/IManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/IManiaHitObjectComposer.cs index f64bab1fae..f1915cd85a 100644 --- a/osu.Game.Rulesets.Mania/Edit/IManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/IManiaHitObjectComposer.cs @@ -10,6 +10,8 @@ namespace osu.Game.Rulesets.Mania.Edit { Column ColumnAt(Vector2 screenSpacePosition); + Vector2 ScreenSpacePositionAtTime(double time, Column column = null); + int TotalColumns { get; } } } diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index 3287c10531..0cae26b51c 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -53,6 +53,7 @@ namespace osu.Game.Rulesets.Mania.Edit var hoc = column.HitObjectContainer; + // convert to local space of column so we can snap and fetch correct location. Vector2 localPosition = hoc.ToLocalSpace(screenSpacePosition); var scrollInfo = drawableRuleset.ScrollingInfo; @@ -65,24 +66,31 @@ namespace osu.Game.Rulesets.Mania.Edit localPosition.Y = hoc.DrawHeight - localPosition.Y; } - double targetTime = scrollInfo.Algorithm.TimeAt(localPosition.Y, - EditorClock.CurrentTime, - scrollInfo.TimeRange.Value, - hoc.DrawHeight); + double targetTime = scrollInfo.Algorithm.TimeAt(localPosition.Y, EditorClock.CurrentTime, scrollInfo.TimeRange.Value, hoc.DrawHeight); + // apply beat snapping targetTime = BeatSnapProvider.SnapTime(targetTime); - localPosition = new Vector2( - hoc.DrawWidth / 2, - scrollInfo.Algorithm.PositionAt(targetTime, EditorClock.CurrentTime, scrollInfo.TimeRange.Value, hoc.DrawHeight)); + // convert back to screen space + screenSpacePosition = ScreenSpacePositionAtTime(targetTime, column); + + return new ManiaSnapResult(screenSpacePosition, targetTime, column); + } + + public Vector2 ScreenSpacePositionAtTime(double time, Column column = null) + { + var hoc = (column ?? Playfield.GetColumn(0)).HitObjectContainer; + var scrollInfo = drawableRuleset.ScrollingInfo; + + var pos = scrollInfo.Algorithm.PositionAt(time, EditorClock.CurrentTime, scrollInfo.TimeRange.Value, hoc.DrawHeight); if (scrollInfo.Direction.Value == ScrollingDirection.Down) { - // reapply the above. - localPosition.Y = hoc.DrawHeight - localPosition.Y; + // as explained above + pos = hoc.DrawHeight - pos; } - return new ManiaSnapResult(hoc.ToScreenSpace(localPosition), BeatSnapProvider.SnapTime(targetTime), column); + return hoc.ToScreenSpace(new Vector2(hoc.DrawWidth / 2, pos)); } protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) From b5a7023312d72670a155b6435354cccd817aa748 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 May 2020 21:46:52 +0900 Subject: [PATCH 0823/1117] Seek to start time after placement, not end --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 883288d6d7..10dffc6aa8 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -257,7 +257,7 @@ namespace osu.Game.Rulesets.Edit { EditorBeatmap.Add(hitObject); - adjustableClock.Seek(hitObject.GetEndTime()); + adjustableClock.Seek(hitObject.StartTime); } showGridFor(Enumerable.Empty()); From e09a1bf546d7bdd12cdd5670c2d0b2398c8242a8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 May 2020 21:50:52 +0900 Subject: [PATCH 0824/1117] Only seek forwards if not already beyond the placed object --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 10dffc6aa8..67216b019d 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -257,7 +257,8 @@ namespace osu.Game.Rulesets.Edit { EditorBeatmap.Add(hitObject); - adjustableClock.Seek(hitObject.StartTime); + if (adjustableClock.CurrentTime < hitObject.StartTime) + adjustableClock.Seek(hitObject.StartTime); } showGridFor(Enumerable.Empty()); From 1b8d657eade8b02ace6f42d5d97d45aa55e320c1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 20 May 2020 23:46:47 +0900 Subject: [PATCH 0825/1117] Implement score panel list --- .../Visual/Ranking/TestSceneScorePanelList.cs | 69 ++++++++++++++++ osu.Game/Screens/Ranking/ScorePanel.cs | 20 +++-- osu.Game/Screens/Ranking/ScorePanelList.cs | 80 +++++++++++++++++++ 3 files changed, 163 insertions(+), 6 deletions(-) create mode 100644 osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs create mode 100644 osu.Game/Screens/Ranking/ScorePanelList.cs diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs new file mode 100644 index 0000000000..4964af8784 --- /dev/null +++ b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs @@ -0,0 +1,69 @@ +// 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; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Rulesets.Scoring; +using osu.Game.Scoring; +using osu.Game.Screens.Ranking; +using osu.Game.Tests.Beatmaps; +using osu.Game.Users; + +namespace osu.Game.Tests.Visual.Ranking +{ + public class TestSceneScorePanelList : OsuTestScene + { + public TestSceneScorePanelList() + { + var list = new ScorePanelList + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }; + + Add(list); + + list.AddScore(createScore()); + list.AddScore(createScore()); + list.AddScore(createScore()); + list.AddScore(createScore()); + list.AddScore(createScore()); + list.AddScore(createScore()); + list.AddScore(createScore()); + list.AddScore(createScore()); + list.AddScore(createScore()); + list.AddScore(createScore()); + list.AddScore(createScore()); + list.AddScore(createScore()); + list.AddScore(createScore()); + list.AddScore(createScore()); + } + + private ScoreInfo createScore() => new ScoreInfo + { + User = new User + { + Id = 2, + Username = "peppy", + CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", + }, + Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo, + Mods = new Mod[] { new OsuModHardRock(), new OsuModDoubleTime() }, + TotalScore = 2845370, + Accuracy = 0.95, + MaxCombo = 999, + Rank = ScoreRank.S, + Date = DateTimeOffset.Now, + Statistics = + { + { HitResult.Miss, 1 }, + { HitResult.Meh, 50 }, + { HitResult.Good, 100 }, + { HitResult.Great, 300 }, + } + }; + } +} diff --git a/osu.Game/Screens/Ranking/ScorePanel.cs b/osu.Game/Screens/Ranking/ScorePanel.cs index bf57cb4dd9..baca2fd9e1 100644 --- a/osu.Game/Screens/Ranking/ScorePanel.cs +++ b/osu.Game/Screens/Ranking/ScorePanel.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Events; using osu.Game.Scoring; using osu.Game.Screens.Ranking.Contracted; using osu.Game.Screens.Ranking.Expanded; @@ -75,8 +76,7 @@ namespace osu.Game.Screens.Ranking private static readonly Color4 contracted_middle_layer_colour = Color4Extensions.FromHex("#353535"); public event Action StateChanged; - - private readonly ScoreInfo score; + public readonly ScoreInfo Score; private Container topLayerContainer; private Drawable topLayerBackground; @@ -90,7 +90,7 @@ namespace osu.Game.Screens.Ranking public ScorePanel(ScoreInfo score) { - this.score = score; + Score = score; } [BackgroundDependencyLoader] @@ -189,8 +189,8 @@ namespace osu.Game.Screens.Ranking topLayerBackground.FadeColour(expanded_top_layer_colour, resize_duration, Easing.OutQuint); middleLayerBackground.FadeColour(expanded_middle_layer_colour, resize_duration, Easing.OutQuint); - topLayerContentContainer.Add(middleLayerContent = new ExpandedPanelTopContent(score.User).With(d => d.Alpha = 0)); - middleLayerContentContainer.Add(topLayerContent = new ExpandedPanelMiddleContent(score).With(d => d.Alpha = 0)); + topLayerContentContainer.Add(middleLayerContent = new ExpandedPanelTopContent(Score.User).With(d => d.Alpha = 0)); + middleLayerContentContainer.Add(topLayerContent = new ExpandedPanelMiddleContent(Score).With(d => d.Alpha = 0)); break; case PanelState.Contracted: @@ -199,7 +199,7 @@ namespace osu.Game.Screens.Ranking topLayerBackground.FadeColour(contracted_top_layer_colour, resize_duration, Easing.OutQuint); middleLayerBackground.FadeColour(contracted_middle_layer_colour, resize_duration, Easing.OutQuint); - middleLayerContentContainer.Add(topLayerContent = new ContractedPanelMiddleContent(score).With(d => d.Alpha = 0)); + middleLayerContentContainer.Add(topLayerContent = new ContractedPanelMiddleContent(Score).With(d => d.Alpha = 0)); break; } @@ -222,5 +222,13 @@ namespace osu.Game.Screens.Ranking middleLayerContent?.FadeIn(content_fade_duration); } } + + protected override bool OnClick(ClickEvent e) + { + if (State == PanelState.Contracted) + State = PanelState.Expanded; + + return true; + } } } diff --git a/osu.Game/Screens/Ranking/ScorePanelList.cs b/osu.Game/Screens/Ranking/ScorePanelList.cs new file mode 100644 index 0000000000..cc6842b2dd --- /dev/null +++ b/osu.Game/Screens/Ranking/ScorePanelList.cs @@ -0,0 +1,80 @@ +// 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.Graphics.Containers; +using osu.Framework.Utils; +using osu.Game.Scoring; +using osuTK; + +namespace osu.Game.Screens.Ranking +{ + public class ScorePanelList : CompositeDrawable + { + private readonly Flow panels; + private ScorePanel expandedPanel; + + public ScorePanelList() + { + RelativeSizeAxes = Axes.Both; + + InternalChild = panels = new Flow + { + Anchor = Anchor.Centre, + Origin = Anchor.Custom, + Direction = FillDirection.Horizontal, + AutoSizeAxes = Axes.Both, + }; + } + + public void AddScore(ScoreInfo score) + { + var panel = new ScorePanel(score) + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + }; + + panel.StateChanged += s => onPanelStateChanged(panel, s); + + // Todo: Temporary + panel.State = expandedPanel == null ? PanelState.Expanded : PanelState.Contracted; + + panels.Add(panel); + } + + public void RemoveScore(ScoreInfo score) => panels.RemoveAll(p => p.Score == score); + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + if (expandedPanel != null) + { + var firstPanel = panels.FlowingChildren.First(); + var target = expandedPanel.DrawPosition.X - firstPanel.DrawPosition.X + expandedPanel.DrawSize.X / 2; + + panels.OriginPosition = new Vector2((float)Interpolation.Lerp(panels.OriginPosition.X, target, Math.Clamp(Math.Abs(Time.Elapsed) / 80, 0, 1)), panels.DrawHeight / 2); + } + } + + private void onPanelStateChanged(ScorePanel panel, PanelState state) + { + if (state == PanelState.Contracted) + return; + + if (expandedPanel != null) + expandedPanel.State = PanelState.Contracted; + expandedPanel = panel; + } + + private class Flow : FillFlowContainer + { + // Todo: Order is wrong. + public override IEnumerable FlowingChildren => AliveInternalChildren.OfType().OrderBy(s => s.Score.TotalScore); + } + } +} From acba1f3ad667fdeae03a1708329e4c49f2ce4006 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 20 May 2020 23:46:54 +0900 Subject: [PATCH 0826/1117] Integrate score panel list into results screen --- osu.Game/Screens/Ranking/ResultsScreen.cs | 38 +++++++---------------- 1 file changed, 11 insertions(+), 27 deletions(-) diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index f2458d9f1f..652d158fbb 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -46,7 +46,7 @@ namespace osu.Game.Screens.Ranking private readonly bool allowRetry; private Drawable bottomPanel; - private Container contractedPanels; + private ScorePanelList panels; public ResultsScreen(ScoreInfo score, bool allowRetry = true) { @@ -63,28 +63,9 @@ namespace osu.Game.Screens.Ranking { new ResultsScrollContainer { - Children = new Drawable[] + Child = panels = new ScorePanelList { - new ScorePanel(Score) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - State = PanelState.Expanded - }, - new OsuScrollContainer(Direction.Horizontal) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - ScrollbarVisible = false, - Child = contractedPanels = new FillFlowContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Direction = FillDirection.Horizontal, - AutoSizeAxes = Axes.Both - } - } + RelativeSizeAxes = Axes.Both, } }, bottomPanel = new Container @@ -117,6 +98,8 @@ namespace osu.Game.Screens.Ranking } }; + panels.AddScore(Score); + if (player != null && allowRetry) { buttons.Add(new RetryButton { Width = 300 }); @@ -141,12 +124,13 @@ namespace osu.Game.Screens.Ranking req.Success += r => { - contractedPanels.ChildrenEnumerable = r.Scores.Select(s => s.CreateScoreInfo(rulesets)).Select(s => new ScorePanel(s) + foreach (var s in r.Scores.Select(s => s.CreateScoreInfo(rulesets))) { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - State = PanelState.Contracted - }); + if (s.OnlineScoreID == Score.OnlineScoreID) + continue; + + panels.AddScore(s); + } }; api.Queue(req); From ce223a2bd84c6362fd1f78aa6bef4feadc8bacb8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 May 2020 10:58:30 +0900 Subject: [PATCH 0827/1117] Silence hit sounds while seeking --- .../Objects/Drawables/DrawableHitObject.cs | 6 ++++- .../Rulesets/UI/FrameStabilityContainer.cs | 22 +++++++++++++++---- osu.Game/Screens/Play/GameplayClock.cs | 5 +++++ 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index c32d4e441e..d594909cda 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -17,6 +17,7 @@ using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Scoring; using osu.Game.Skinning; using osu.Game.Configuration; +using osu.Game.Screens.Play; using osuTK.Graphics; namespace osu.Game.Rulesets.Objects.Drawables @@ -348,6 +349,9 @@ namespace osu.Game.Rulesets.Objects.Drawables { } + [Resolved(canBeNull: true)] + private GameplayClock gameplayClock { get; set; } + /// /// Plays all the hit sounds for this . /// This is invoked automatically when this is hit. @@ -356,7 +360,7 @@ namespace osu.Game.Rulesets.Objects.Drawables { const float balance_adjust_amount = 0.4f; - if (Samples != null) + if (Samples != null && gameplayClock?.IsSeeking != true) { Samples.Balance.Value = balance_adjust_amount * (userPositionalHitSounds.Value ? SamplePlaybackPosition - 0.5f : 0); Samples.Play(); diff --git a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs index 3ba28aad45..bc9401a095 100644 --- a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs +++ b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs @@ -29,14 +29,16 @@ namespace osu.Game.Rulesets.UI /// internal bool FrameStablePlayback = true; - [Cached] - public GameplayClock GameplayClock { get; } + public GameplayClock GameplayClock => stabilityGameplayClock; + + [Cached(typeof(GameplayClock))] + private readonly StabilityGameplayClock stabilityGameplayClock; public FrameStabilityContainer(double gameplayStartTime = double.MinValue) { RelativeSizeAxes = Axes.Both; - GameplayClock = new GameplayClock(framedClock = new FramedClock(manualClock = new ManualClock())); + stabilityGameplayClock = new StabilityGameplayClock(framedClock = new FramedClock(manualClock = new ManualClock())); this.gameplayStartTime = gameplayStartTime; } @@ -57,7 +59,7 @@ namespace osu.Game.Rulesets.UI { if (clock != null) { - parentGameplayClock = clock; + stabilityGameplayClock.ParentGameplayClock = parentGameplayClock = clock; GameplayClock.IsPaused.BindTo(clock.IsPaused); } } @@ -187,5 +189,17 @@ namespace osu.Game.Rulesets.UI } public ReplayInputHandler ReplayInputHandler { get; set; } + + private class StabilityGameplayClock : GameplayClock + { + public IFrameBasedClock ParentGameplayClock; + + public StabilityGameplayClock(FramedClock underlyingClock) + : base(underlyingClock) + { + } + + public override bool IsSeeking => ParentGameplayClock != null && Math.Abs(CurrentTime - ParentGameplayClock.CurrentTime) > 200; + } } } diff --git a/osu.Game/Screens/Play/GameplayClock.cs b/osu.Game/Screens/Play/GameplayClock.cs index d5f75f6ad1..4f2cf5005c 100644 --- a/osu.Game/Screens/Play/GameplayClock.cs +++ b/osu.Game/Screens/Play/GameplayClock.cs @@ -31,6 +31,11 @@ namespace osu.Game.Screens.Play public bool IsRunning => underlyingClock.IsRunning; + /// + /// Whether an ongoing seek operation is active. + /// + public virtual bool IsSeeking => false; + public void ProcessFrame() { // we do not want to process the underlying clock. From c0e68f98540303aa02932718d1d96f2bf8a94c20 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 May 2020 10:59:30 +0900 Subject: [PATCH 0828/1117] Also support taiko drum --- osu.Game.Rulesets.Taiko/UI/InputDrum.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs index 38026517d9..06ccd45cb8 100644 --- a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs +++ b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs @@ -12,6 +12,7 @@ using osu.Framework.Input.Bindings; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; using osu.Game.Rulesets.Taiko.Audio; +using osu.Game.Screens.Play; using osu.Game.Skinning; namespace osu.Game.Rulesets.Taiko.UI @@ -145,6 +146,9 @@ namespace osu.Game.Rulesets.Taiko.UI centreHit.Colour = colours.Pink; } + [Resolved(canBeNull: true)] + private GameplayClock gameplayClock { get; set; } + public bool OnPressed(TaikoAction action) { Drawable target = null; @@ -157,14 +161,16 @@ namespace osu.Game.Rulesets.Taiko.UI target = centreHit; back = centre; - drumSample.Centre?.Play(); + if (gameplayClock?.IsSeeking != true) + drumSample.Centre?.Play(); } else if (action == RimAction) { target = rimHit; back = rim; - drumSample.Rim?.Play(); + if (gameplayClock?.IsSeeking != true) + drumSample.Rim?.Play(); } if (target != null) From 83a5913b8d7a4ddb3adc9610b39e3e5e28041933 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 May 2020 12:11:39 +0900 Subject: [PATCH 0829/1117] Undo beat snapping related changes --- .../Edit/Blueprints/ManiaPlacementBlueprint.cs | 16 ++-------------- .../Edit/ManiaHitObjectComposer.cs | 17 +++++------------ 2 files changed, 7 insertions(+), 26 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs index 184356b89c..3fb03d642f 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs @@ -24,15 +24,10 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints protected Column Column; /// - /// The current beat-snapped mouse position, snapped to the closest column. + /// The current mouse position, snapped to the closest column. /// protected Vector2 SnappedMousePosition { get; private set; } - /// - /// The gameplay time at the current beat-snapped mouse position (). - /// - protected double SnappedTime { get; private set; } - /// /// The width of the closest column to the current mouse position. /// @@ -44,9 +39,6 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints [Resolved] private IScrollingInfo scrollingInfo { get; set; } - [Resolved(CanBeNull = true)] - private IDistanceSnapProvider snapProvider { get; set; } - protected ManiaPlacementBlueprint(T hitObject) : base(hitObject) { @@ -62,7 +54,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints return base.OnMouseDown(e); HitObject.Column = Column.Index; - BeginPlacement(SnappedTime, true); + BeginPlacement(TimeAt(e.ScreenSpaceMousePosition), true); return true; } @@ -78,10 +70,6 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints // Snap to the column var parentPos = Parent.ToLocalSpace(Column.ToScreenSpace(new Vector2(Column.DrawWidth / 2, 0))); SnappedMousePosition = new Vector2(parentPos.X, Parent.ToLocalSpace(screenSpacePosition).Y); - - SnappedTime = TimeAt(screenSpacePosition); - if (snapProvider != null) - (SnappedMousePosition, SnappedTime) = snapProvider.GetSnappedPosition(SnappedMousePosition, SnappedTime); } protected double TimeAt(Vector2 screenSpacePosition) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index 475320ece3..7677ac6f07 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -82,18 +82,9 @@ namespace osu.Game.Rulesets.Mania.Edit public override (Vector2 position, double time) GetSnappedPosition(Vector2 position, double time) { - var beatSnapped = beatSnapGrid.GetSnappedPosition(position); + var hoc = Playfield.GetColumn(0).HitObjectContainer; - if (beatSnapped != null) - return beatSnapped.Value; - - return base.GetSnappedPosition(position, getTimeFromPosition(ToScreenSpace(position))); - } - - private double getTimeFromPosition(Vector2 screenSpacePosition) - { - var hoc = Playfield.Stages[0].HitObjectContainer; - float targetPosition = hoc.ToLocalSpace(screenSpacePosition).Y; + float targetPosition = hoc.ToLocalSpace(ToScreenSpace(position)).Y; if (drawableRuleset.ScrollingInfo.Direction.Value == ScrollingDirection.Down) { @@ -103,10 +94,12 @@ namespace osu.Game.Rulesets.Mania.Edit targetPosition = hoc.DrawHeight - targetPosition; } - return drawableRuleset.ScrollingInfo.Algorithm.TimeAt(targetPosition, + double targetTime = drawableRuleset.ScrollingInfo.Algorithm.TimeAt(targetPosition, EditorClock.CurrentTime, drawableRuleset.ScrollingInfo.TimeRange.Value, hoc.DrawHeight); + + return base.GetSnappedPosition(position, targetTime); } protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) From 6d29ff092869b2e58ae983c8919bec93b2b6cc9b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 21 May 2020 12:13:02 +0900 Subject: [PATCH 0830/1117] Fix banana showers not using cancellation token --- osu.Game.Rulesets.Catch/Objects/BananaShower.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/BananaShower.cs b/osu.Game.Rulesets.Catch/Objects/BananaShower.cs index 96ab66048a..3a0b5ace53 100644 --- a/osu.Game.Rulesets.Catch/Objects/BananaShower.cs +++ b/osu.Game.Rulesets.Catch/Objects/BananaShower.cs @@ -18,10 +18,10 @@ namespace osu.Game.Rulesets.Catch.Objects protected override void CreateNestedHitObjects(CancellationToken cancellationToken) { base.CreateNestedHitObjects(cancellationToken); - createBananas(); + createBananas(cancellationToken); } - private void createBananas() + private void createBananas(CancellationToken cancellationToken) { double spacing = Duration; while (spacing > 100) @@ -32,6 +32,8 @@ namespace osu.Game.Rulesets.Catch.Objects for (double i = StartTime; i <= EndTime; i += spacing) { + cancellationToken.ThrowIfCancellationRequested(); + AddNested(new Banana { Samples = Samples, From 922b793a5aed03ee2ed1db3ae668eafc90a6eda8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 May 2020 13:04:35 +0900 Subject: [PATCH 0831/1117] Update hit object composer tests --- .../TestSceneManiaHitObjectComposer.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs index 6274bb1005..bad3d7854e 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs @@ -42,6 +42,7 @@ namespace osu.Game.Rulesets.Mania.Tests public void TestDragOffscreenSelectionVerticallyUpScroll() { DrawableHitObject lastObject = null; + double originalTime = 0; Vector2 originalPosition = Vector2.Zero; setScrollStep(ScrollingDirection.Up); @@ -49,6 +50,7 @@ namespace osu.Game.Rulesets.Mania.Tests AddStep("seek to last object", () => { lastObject = this.ChildrenOfType().Single(d => d.HitObject == composer.EditorBeatmap.HitObjects.Last()); + originalTime = lastObject.HitObject.StartTime; Clock.Seek(composer.EditorBeatmap.HitObjects.Last().StartTime); }); @@ -64,19 +66,20 @@ namespace osu.Game.Rulesets.Mania.Tests AddStep("move mouse downwards", () => { - InputManager.MoveMouseTo(lastObject, new Vector2(0, 20)); + InputManager.MoveMouseTo(lastObject, new Vector2(0, lastObject.ScreenSpaceDrawQuad.Height * 2)); InputManager.ReleaseButton(MouseButton.Left); }); AddAssert("hitobjects not moved columns", () => composer.EditorBeatmap.HitObjects.All(h => ((ManiaHitObject)h).Column == 0)); AddAssert("hitobjects moved downwards", () => lastObject.DrawPosition.Y - originalPosition.Y > 0); - AddAssert("hitobjects not moved too far", () => lastObject.DrawPosition.Y - originalPosition.Y < 50); + AddAssert("hitobject has moved time", () => lastObject.HitObject.StartTime == originalTime + 125); } [Test] public void TestDragOffscreenSelectionVerticallyDownScroll() { DrawableHitObject lastObject = null; + double originalTime = 0; Vector2 originalPosition = Vector2.Zero; setScrollStep(ScrollingDirection.Down); @@ -84,6 +87,7 @@ namespace osu.Game.Rulesets.Mania.Tests AddStep("seek to last object", () => { lastObject = this.ChildrenOfType().Single(d => d.HitObject == composer.EditorBeatmap.HitObjects.Last()); + originalTime = lastObject.HitObject.StartTime; Clock.Seek(composer.EditorBeatmap.HitObjects.Last().StartTime); }); @@ -99,13 +103,13 @@ namespace osu.Game.Rulesets.Mania.Tests AddStep("move mouse upwards", () => { - InputManager.MoveMouseTo(lastObject, new Vector2(0, -20)); + InputManager.MoveMouseTo(lastObject, new Vector2(0, -lastObject.ScreenSpaceDrawQuad.Height * 2)); InputManager.ReleaseButton(MouseButton.Left); }); AddAssert("hitobjects not moved columns", () => composer.EditorBeatmap.HitObjects.All(h => ((ManiaHitObject)h).Column == 0)); AddAssert("hitobjects moved upwards", () => originalPosition.Y - lastObject.DrawPosition.Y > 0); - AddAssert("hitobjects not moved too far", () => originalPosition.Y - lastObject.DrawPosition.Y < 50); + AddAssert("hitobject has moved time", () => lastObject.HitObject.StartTime == originalTime + 125); } [Test] @@ -207,7 +211,7 @@ namespace osu.Game.Rulesets.Mania.Tests }; for (int i = 0; i < 10; i++) - EditorBeatmap.Add(new Note { StartTime = 100 * i }); + EditorBeatmap.Add(new Note { StartTime = 125 * i }); } } } From 5ad7842b917e862d3d5ce722ff0b0169a7660d53 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 May 2020 13:33:02 +0900 Subject: [PATCH 0832/1117] Move ScreenSpacePositionAtTime to inside Column implementation --- .../ManiaPlacementBlueprintTestScene.cs | 2 -- .../ManiaSelectionBlueprintTestScene.cs | 2 -- .../Blueprints/HoldNotePlacementBlueprint.cs | 5 ++--- .../Edit/IManiaHitObjectComposer.cs | 2 -- .../Edit/ManiaHitObjectComposer.cs | 18 +----------------- osu.Game.Rulesets.Mania/UI/Column.cs | 13 +++++++++++++ .../UI/Scrolling/ScrollingPlayfield.cs | 4 ++-- 7 files changed, 18 insertions(+), 28 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestScene.cs b/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestScene.cs index be3e205f36..aac77c9c1c 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestScene.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestScene.cs @@ -49,8 +49,6 @@ namespace osu.Game.Rulesets.Mania.Tests public Column ColumnAt(Vector2 screenSpacePosition) => column; - public Vector2 ScreenSpacePositionAtTime(double time, Column column = null) => Vector2.Zero; - public int TotalColumns => 1; } } diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaSelectionBlueprintTestScene.cs b/osu.Game.Rulesets.Mania.Tests/ManiaSelectionBlueprintTestScene.cs index 3d654466ed..b598893e8c 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaSelectionBlueprintTestScene.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaSelectionBlueprintTestScene.cs @@ -33,8 +33,6 @@ namespace osu.Game.Rulesets.Mania.Tests public Column ColumnAt(Vector2 screenSpacePosition) => column; - public Vector2 ScreenSpacePositionAtTime(double time, Column column = null) => Vector2.Zero; - public int TotalColumns => 1; } } diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs index 31bf76edd0..8689d479b4 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs @@ -8,7 +8,6 @@ using osu.Framework.Input.Events; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Mania.Edit.Blueprints.Components; using osu.Game.Rulesets.Mania.Objects; -using osu.Game.Rulesets.UI.Scrolling; using osuTK; using osuTK.Input; @@ -42,8 +41,8 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints if (Column != null) { - headPiece.Y = Parent.ToLocalSpace(composer.ScreenSpacePositionAtTime(HitObject.StartTime, Column)).Y; - tailPiece.Y = Parent.ToLocalSpace(composer.ScreenSpacePositionAtTime(HitObject.EndTime, Column)).Y; + headPiece.Y = Parent.ToLocalSpace(Column.ScreenSpacePositionAtTime(HitObject.StartTime, Column)).Y; + tailPiece.Y = Parent.ToLocalSpace(Column.ScreenSpacePositionAtTime(HitObject.EndTime, Column)).Y; } var topPosition = new Vector2(headPiece.DrawPosition.X, Math.Min(headPiece.DrawPosition.Y, tailPiece.DrawPosition.Y)); diff --git a/osu.Game.Rulesets.Mania/Edit/IManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/IManiaHitObjectComposer.cs index f1915cd85a..f64bab1fae 100644 --- a/osu.Game.Rulesets.Mania/Edit/IManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/IManiaHitObjectComposer.cs @@ -10,8 +10,6 @@ namespace osu.Game.Rulesets.Mania.Edit { Column ColumnAt(Vector2 screenSpacePosition); - Vector2 ScreenSpacePositionAtTime(double time, Column column = null); - int TotalColumns { get; } } } diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index 0cae26b51c..5eafaefe37 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -72,27 +72,11 @@ namespace osu.Game.Rulesets.Mania.Edit targetTime = BeatSnapProvider.SnapTime(targetTime); // convert back to screen space - screenSpacePosition = ScreenSpacePositionAtTime(targetTime, column); + screenSpacePosition = column.ScreenSpacePositionAtTime(targetTime, column); return new ManiaSnapResult(screenSpacePosition, targetTime, column); } - public Vector2 ScreenSpacePositionAtTime(double time, Column column = null) - { - var hoc = (column ?? Playfield.GetColumn(0)).HitObjectContainer; - var scrollInfo = drawableRuleset.ScrollingInfo; - - var pos = scrollInfo.Algorithm.PositionAt(time, EditorClock.CurrentTime, scrollInfo.TimeRange.Value, hoc.DrawHeight); - - if (scrollInfo.Direction.Value == ScrollingDirection.Down) - { - // as explained above - pos = hoc.DrawHeight - pos; - } - - return hoc.ToScreenSpace(new Vector2(hoc.DrawWidth / 2, pos)); - } - protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) { drawableRuleset = new DrawableManiaEditRuleset(ruleset, beatmap, mods); diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index 506a07f26b..3f85f449ce 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -140,5 +140,18 @@ namespace osu.Game.Rulesets.Mania.UI public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) // This probably shouldn't exist as is, but the columns in the stage are separated by a 1px border => DrawRectangle.Inflate(new Vector2(Stage.COLUMN_SPACING / 2, 0)).Contains(ToLocalSpace(screenSpacePos)); + + public Vector2 ScreenSpacePositionAtTime(double time, Column column = null) + { + var pos = ScrollingInfo.Algorithm.PositionAt(time, Time.Current, ScrollingInfo.TimeRange.Value, HitObjectContainer.DrawHeight); + + if (ScrollingInfo.Direction.Value == ScrollingDirection.Down) + { + // as explained above + pos = HitObjectContainer.DrawHeight - pos; + } + + return HitObjectContainer.ToScreenSpace(new Vector2(HitObjectContainer.DrawWidth / 2, pos)); + } } } diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs index bf2203e176..fd143a3687 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs @@ -15,12 +15,12 @@ namespace osu.Game.Rulesets.UI.Scrolling protected readonly IBindable Direction = new Bindable(); [Resolved] - private IScrollingInfo scrollingInfo { get; set; } + protected IScrollingInfo ScrollingInfo { get; private set; } [BackgroundDependencyLoader] private void load() { - Direction.BindTo(scrollingInfo.Direction); + Direction.BindTo(ScrollingInfo.Direction); } protected sealed override HitObjectContainer CreateHitObjectContainer() => new ScrollingHitObjectContainer(); From bac78707de161819947b5b26ec4d7ea830ee1699 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 May 2020 14:25:37 +0900 Subject: [PATCH 0833/1117] Move more logic to column to both clean things up and fix tests --- .../ManiaPlacementBlueprintTestScene.cs | 9 +++++++++ .../Edit/ManiaHitObjectComposer.cs | 17 +---------------- osu.Game.Rulesets.Mania/UI/Column.cs | 16 ++++++++++++++++ .../Tests/Visual/PlacementBlueprintTestScene.cs | 7 +++++-- 4 files changed, 31 insertions(+), 18 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestScene.cs b/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestScene.cs index aac77c9c1c..547786847b 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestScene.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestScene.cs @@ -7,6 +7,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Timing; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Mania.Edit; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.UI; @@ -43,6 +44,14 @@ namespace osu.Game.Rulesets.Mania.Tests }); } + protected override SnapResult SnapForBlueprint(PlacementBlueprint blueprint) + { + var time = column.TimeAtScreenSpacePosition(InputManager.CurrentState.Mouse.Position); + var pos = column.ScreenSpacePositionAtTime(time); + + return new ManiaSnapResult(pos, time, column); + } + protected override Container CreateHitObjectContainer() => new ScrollingTestContainer(ScrollingDirection.Down) { RelativeSizeAxes = Axes.Both }; protected override void AddHitObject(DrawableHitObject hitObject) => column.Add((DrawableManiaHitObject)hitObject); diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index 5eafaefe37..9085033140 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -51,22 +51,7 @@ namespace osu.Game.Rulesets.Mania.Edit if (column == null) return new SnapResult(screenSpacePosition, null); - var hoc = column.HitObjectContainer; - - // convert to local space of column so we can snap and fetch correct location. - Vector2 localPosition = hoc.ToLocalSpace(screenSpacePosition); - - var scrollInfo = drawableRuleset.ScrollingInfo; - - if (scrollInfo.Direction.Value == ScrollingDirection.Down) - { - // We're dealing with screen coordinates in which the position decreases towards the centre of the screen resulting in an increase in start time. - // The scrolling algorithm instead assumes a top anchor meaning an increase in time corresponds to an increase in position, - // so when scrolling downwards the coordinates need to be flipped. - localPosition.Y = hoc.DrawHeight - localPosition.Y; - } - - double targetTime = scrollInfo.Algorithm.TimeAt(localPosition.Y, EditorClock.CurrentTime, scrollInfo.TimeRange.Value, hoc.DrawHeight); + double targetTime = column.TimeAtScreenSpacePosition(screenSpacePosition); // apply beat snapping targetTime = BeatSnapProvider.SnapTime(targetTime); diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index 3f85f449ce..c582eb1c75 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -153,5 +153,21 @@ namespace osu.Game.Rulesets.Mania.UI return HitObjectContainer.ToScreenSpace(new Vector2(HitObjectContainer.DrawWidth / 2, pos)); } + + public double TimeAtScreenSpacePosition(Vector2 screenSpacePosition) + { + // convert to local space of column so we can snap and fetch correct location. + Vector2 localPosition = HitObjectContainer.ToLocalSpace(screenSpacePosition); + + if (ScrollingInfo.Direction.Value == ScrollingDirection.Down) + { + // We're dealing with screen coordinates in which the position decreases towards the centre of the screen resulting in an increase in start time. + // The scrolling algorithm instead assumes a top anchor meaning an increase in time corresponds to an increase in position, + // so when scrolling downwards the coordinates need to be flipped. + localPosition.Y = HitObjectContainer.DrawHeight - localPosition.Y; + } + + return ScrollingInfo.Algorithm.TimeAt(localPosition.Y, Time.Current, ScrollingInfo.TimeRange.Value, HitObjectContainer.DrawHeight); + } } } diff --git a/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs b/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs index a4e629b6f5..feecea473c 100644 --- a/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs +++ b/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs @@ -71,9 +71,12 @@ namespace osu.Game.Tests.Visual { base.Update(); - currentBlueprint.UpdatePosition(new SnapResult(InputManager.CurrentState.Mouse.Position, null)); + currentBlueprint.UpdatePosition(SnapForBlueprint(currentBlueprint)); } + protected virtual SnapResult SnapForBlueprint(PlacementBlueprint blueprint) => + new SnapResult(InputManager.CurrentState.Mouse.Position, null); + public override void Add(Drawable drawable) { base.Add(drawable); @@ -81,7 +84,7 @@ namespace osu.Game.Tests.Visual if (drawable is PlacementBlueprint blueprint) { blueprint.Show(); - blueprint.UpdatePosition(new SnapResult(InputManager.CurrentState.Mouse.Position, null)); + blueprint.UpdatePosition(SnapForBlueprint(blueprint)); } } From a9a1c00cf1d2b54a757bc6a69a70cee31a39ce04 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 May 2020 14:38:40 +0900 Subject: [PATCH 0834/1117] Move responsibility placement blueprint's StartTime set to within --- .../Edit/Blueprints/ManiaPlacementBlueprint.cs | 2 ++ .../HitCircles/HitCirclePlacementBlueprint.cs | 6 +++++- .../Blueprints/Sliders/SliderPlacementBlueprint.cs | 2 ++ .../Blueprints/Spinners/SpinnerPlacementBlueprint.cs | 4 ---- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 2 -- osu.Game/Rulesets/Edit/PlacementBlueprint.cs | 10 +++++++++- 6 files changed, 18 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs index 8d3b3ea583..d173da9d9a 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs @@ -50,6 +50,8 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints public override void UpdatePosition(SnapResult result) { + base.UpdatePosition(result); + if (!PlacementActive) Column = (result as ManiaSnapResult)?.Column; } diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/HitCirclePlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/HitCirclePlacementBlueprint.cs index e12dec2668..3dbbdcc5d0 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/HitCirclePlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/HitCirclePlacementBlueprint.cs @@ -39,6 +39,10 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles return base.OnMouseDown(e); } - public override void UpdatePosition(SnapResult result) => HitObject.Position = ToLocalSpace(result.ScreenSpacePosition); + public override void UpdatePosition(SnapResult result) + { + base.UpdatePosition(result); + HitObject.Position = ToLocalSpace(result.ScreenSpacePosition); + } } } diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 59ec92c79e..4b99cc23ed 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -69,6 +69,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders public override void UpdatePosition(SnapResult result) { + base.UpdatePosition(result); + switch (state) { case PlacementState.Initial: diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/SpinnerPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/SpinnerPlacementBlueprint.cs index 546f0e5981..cc4ed0eccf 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/SpinnerPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/SpinnerPlacementBlueprint.cs @@ -59,9 +59,5 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners return true; } - - public override void UpdatePosition(SnapResult result) - { - } } } diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 1e328e6b6b..6edd01cd15 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -244,8 +244,6 @@ namespace osu.Game.Rulesets.Edit public void BeginPlacement(HitObject hitObject) { EditorBeatmap.PlacementObject.Value = hitObject; - - hitObject.StartTime = SnapScreenSpacePositionToValidTime(inputManager.CurrentState.Mouse.Position).Time ?? EditorClock.CurrentTime; } public void EndPlacement(HitObject hitObject, bool commit) diff --git a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs index bab9bf71ef..2fd8c4b9d9 100644 --- a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs +++ b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs @@ -10,6 +10,7 @@ using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Objects; +using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Compose; using osuTK; @@ -83,11 +84,18 @@ namespace osu.Game.Rulesets.Edit PlacementActive = false; } + [Resolved(canBeNull: true)] + private IFrameBasedClock editorClock { get; set; } + /// /// Updates the position of this to a new screen-space position. /// /// The snap result information. - public abstract void UpdatePosition(SnapResult snapResult); + public virtual void UpdatePosition(SnapResult snapResult) + { + if (!PlacementActive) + HitObject.StartTime = snapResult.Time ?? editorClock?.CurrentTime ?? Time.Current; + } /// /// Invokes , From 776b842fdbbabff264fd6120833be30bacd57053 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 May 2020 14:53:36 +0900 Subject: [PATCH 0835/1117] Remove unused using --- osu.Game/Rulesets/Edit/PlacementBlueprint.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs index 2fd8c4b9d9..f0b63f8ea5 100644 --- a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs +++ b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs @@ -10,7 +10,6 @@ using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Objects; -using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Compose; using osuTK; From ce8b6b7383d2c32d7518b9a1091c1af1825bd334 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 May 2020 15:15:24 +0900 Subject: [PATCH 0836/1117] Correctly account for blueprint origins --- osu.Game.Rulesets.Mania/UI/Column.cs | 55 ++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index c582eb1c75..0fdefe6dc9 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -17,6 +17,7 @@ using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Skinning; using osuTK; using osu.Game.Rulesets.Mania.Beatmaps; +using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; namespace osu.Game.Rulesets.Mania.UI { @@ -145,10 +146,21 @@ namespace osu.Game.Rulesets.Mania.UI { var pos = ScrollingInfo.Algorithm.PositionAt(time, Time.Current, ScrollingInfo.TimeRange.Value, HitObjectContainer.DrawHeight); - if (ScrollingInfo.Direction.Value == ScrollingDirection.Down) + switch (ScrollingInfo.Direction.Value) { - // as explained above - pos = HitObjectContainer.DrawHeight - pos; + case ScrollingDirection.Down: + // We're dealing with screen coordinates in which the position decreases towards the centre of the screen resulting in an increase in start time. + // The scrolling algorithm instead assumes a top anchor meaning an increase in time corresponds to an increase in position, + // so when scrolling downwards the coordinates need to be flipped. + pos = HitObjectContainer.DrawHeight - pos; + + // Blueprints are centred on the mouse position, such that the hitobject position is anchored at the top or bottom of the blueprint depending on the scroll direction. + pos -= DefaultNotePiece.NOTE_HEIGHT / 2; + break; + + case ScrollingDirection.Up: + pos += DefaultNotePiece.NOTE_HEIGHT / 2; + break; } return HitObjectContainer.ToScreenSpace(new Vector2(HitObjectContainer.DrawWidth / 2, pos)); @@ -159,15 +171,42 @@ namespace osu.Game.Rulesets.Mania.UI // convert to local space of column so we can snap and fetch correct location. Vector2 localPosition = HitObjectContainer.ToLocalSpace(screenSpacePosition); - if (ScrollingInfo.Direction.Value == ScrollingDirection.Down) + switch (ScrollingInfo.Direction.Value) { - // We're dealing with screen coordinates in which the position decreases towards the centre of the screen resulting in an increase in start time. - // The scrolling algorithm instead assumes a top anchor meaning an increase in time corresponds to an increase in position, - // so when scrolling downwards the coordinates need to be flipped. - localPosition.Y = HitObjectContainer.DrawHeight - localPosition.Y; + case ScrollingDirection.Down: + // as above + localPosition.Y = HitObjectContainer.DrawHeight - localPosition.Y; + break; } + // offset for the fact that blueprints are centered, as above. + localPosition.Y -= DefaultNotePiece.NOTE_HEIGHT / 2; + return ScrollingInfo.Algorithm.TimeAt(localPosition.Y, Time.Current, ScrollingInfo.TimeRange.Value, HitObjectContainer.DrawHeight); } + + /// + /// Converts a mouse position to a hitobject position. + /// + /// + /// Blueprints are centred on the mouse position, such that the hitobject position is anchored at the top or bottom of the blueprint depending on the scroll direction. + /// + /// The mouse position. + /// The resulting hitobject position, acnhored at the top or bottom of the blueprint depending on the scroll direction. + private Vector2 mouseToHitObjectPosition(Vector2 mousePosition) + { + switch (ScrollingInfo.Direction.Value) + { + case ScrollingDirection.Up: + mousePosition.Y -= DefaultNotePiece.NOTE_HEIGHT / 2; + break; + + case ScrollingDirection.Down: + mousePosition.Y += DefaultNotePiece.NOTE_HEIGHT / 2; + break; + } + + return mousePosition; + } } } From a756e6d21241080204da38772a8e34ff9d9946a9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 May 2020 15:16:30 +0900 Subject: [PATCH 0837/1117] Add xmldoc and remove unnecessary parameter --- .../Edit/Blueprints/HoldNotePlacementBlueprint.cs | 4 ++-- osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs | 2 +- osu.Game.Rulesets.Mania/UI/Column.cs | 8 +++++++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs index 8689d479b4..b757c17a48 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs @@ -41,8 +41,8 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints if (Column != null) { - headPiece.Y = Parent.ToLocalSpace(Column.ScreenSpacePositionAtTime(HitObject.StartTime, Column)).Y; - tailPiece.Y = Parent.ToLocalSpace(Column.ScreenSpacePositionAtTime(HitObject.EndTime, Column)).Y; + headPiece.Y = Parent.ToLocalSpace(Column.ScreenSpacePositionAtTime(HitObject.StartTime)).Y; + tailPiece.Y = Parent.ToLocalSpace(Column.ScreenSpacePositionAtTime(HitObject.EndTime)).Y; } var topPosition = new Vector2(headPiece.DrawPosition.X, Math.Min(headPiece.DrawPosition.Y, tailPiece.DrawPosition.Y)); diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index 9085033140..cfb04c8e50 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Mania.Edit targetTime = BeatSnapProvider.SnapTime(targetTime); // convert back to screen space - screenSpacePosition = column.ScreenSpacePositionAtTime(targetTime, column); + screenSpacePosition = column.ScreenSpacePositionAtTime(targetTime); return new ManiaSnapResult(screenSpacePosition, targetTime, column); } diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index 0fdefe6dc9..f7339fdacd 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -142,7 +142,10 @@ namespace osu.Game.Rulesets.Mania.UI // This probably shouldn't exist as is, but the columns in the stage are separated by a 1px border => DrawRectangle.Inflate(new Vector2(Stage.COLUMN_SPACING / 2, 0)).Contains(ToLocalSpace(screenSpacePos)); - public Vector2 ScreenSpacePositionAtTime(double time, Column column = null) + /// + /// Given a time, return the screen space position within this column. + /// + public Vector2 ScreenSpacePositionAtTime(double time) { var pos = ScrollingInfo.Algorithm.PositionAt(time, Time.Current, ScrollingInfo.TimeRange.Value, HitObjectContainer.DrawHeight); @@ -166,6 +169,9 @@ namespace osu.Game.Rulesets.Mania.UI return HitObjectContainer.ToScreenSpace(new Vector2(HitObjectContainer.DrawWidth / 2, pos)); } + /// + /// Given a position in screen space, return the time within this column. + /// public double TimeAtScreenSpacePosition(Vector2 screenSpacePosition) { // convert to local space of column so we can snap and fetch correct location. From 7dd3b3eeb56473e200d07af163204022126b5e11 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 May 2020 15:16:59 +0900 Subject: [PATCH 0838/1117] Remove unused method --- osu.Game.Rulesets.Mania/UI/Column.cs | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index f7339fdacd..2d88670d77 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -190,29 +190,5 @@ namespace osu.Game.Rulesets.Mania.UI return ScrollingInfo.Algorithm.TimeAt(localPosition.Y, Time.Current, ScrollingInfo.TimeRange.Value, HitObjectContainer.DrawHeight); } - - /// - /// Converts a mouse position to a hitobject position. - /// - /// - /// Blueprints are centred on the mouse position, such that the hitobject position is anchored at the top or bottom of the blueprint depending on the scroll direction. - /// - /// The mouse position. - /// The resulting hitobject position, acnhored at the top or bottom of the blueprint depending on the scroll direction. - private Vector2 mouseToHitObjectPosition(Vector2 mousePosition) - { - switch (ScrollingInfo.Direction.Value) - { - case ScrollingDirection.Up: - mousePosition.Y -= DefaultNotePiece.NOTE_HEIGHT / 2; - break; - - case ScrollingDirection.Down: - mousePosition.Y += DefaultNotePiece.NOTE_HEIGHT / 2; - break; - } - - return mousePosition; - } } } From 0db1ea6a9d460c208e5cf513625b34968f686aa2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 May 2020 15:47:12 +0900 Subject: [PATCH 0839/1117] Fix failing tests --- .../TestSceneManiaHitObjectComposer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs index bad3d7854e..1a3fa29d4a 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs @@ -66,7 +66,7 @@ namespace osu.Game.Rulesets.Mania.Tests AddStep("move mouse downwards", () => { - InputManager.MoveMouseTo(lastObject, new Vector2(0, lastObject.ScreenSpaceDrawQuad.Height * 2)); + InputManager.MoveMouseTo(lastObject, new Vector2(0, lastObject.ScreenSpaceDrawQuad.Height * 4)); InputManager.ReleaseButton(MouseButton.Left); }); @@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Mania.Tests AddStep("move mouse upwards", () => { - InputManager.MoveMouseTo(lastObject, new Vector2(0, -lastObject.ScreenSpaceDrawQuad.Height * 2)); + InputManager.MoveMouseTo(lastObject, new Vector2(0, -lastObject.ScreenSpaceDrawQuad.Height * 4)); InputManager.ReleaseButton(MouseButton.Left); }); From 8a47e2431bbe7c3207e75004d61fd5fcbce103f0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 May 2020 17:13:22 +0900 Subject: [PATCH 0840/1117] Move distance snap grid implementation to OsuHitObjectComposer --- .../Edit/OsuHitObjectComposer.cs | 64 +++++++++++++++- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 73 +++---------------- .../Compose/Components/BlueprintContainer.cs | 5 -- 3 files changed, 74 insertions(+), 68 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index cdf78a5902..9ba3e30445 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -4,6 +4,10 @@ using System; using System.Collections.Generic; using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Caching; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; @@ -12,6 +16,7 @@ using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.UI; using osu.Game.Screens.Edit.Compose.Components; +using osuTK; namespace osu.Game.Rulesets.Osu.Edit { @@ -32,9 +37,66 @@ namespace osu.Game.Rulesets.Osu.Edit new SpinnerCompositionTool() }; + [BackgroundDependencyLoader] + private void load() + { + EditorBeatmap.SelectedHitObjects.CollectionChanged += (_, __) => updateDistanceSnapGrid(); + EditorBeatmap.PlacementObject.ValueChanged += _ => updateDistanceSnapGrid(); + + LayerBelowRuleset.Add(distanceSnapGridContainer = new Container { RelativeSizeAxes = Axes.Both }); + } + protected override ComposeBlueprintContainer CreateBlueprintContainer() => new OsuBlueprintContainer(HitObjects); - protected override DistanceSnapGrid CreateDistanceSnapGrid(IEnumerable selectedHitObjects) + private DistanceSnapGrid distanceSnapGrid; + private Container distanceSnapGridContainer; + + private readonly Cached distanceSnapGridCache = new Cached(); + private double? lastDistanceSnapGridTime; + + protected override void Update() + { + base.Update(); + + if (!(BlueprintContainer.CurrentTool is SelectTool)) + { + if (EditorClock.CurrentTime != lastDistanceSnapGridTime) + { + distanceSnapGridCache.Invalidate(); + lastDistanceSnapGridTime = EditorClock.CurrentTime; + } + + if (!distanceSnapGridCache.IsValid) + updateDistanceSnapGrid(); + } + } + + public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) + { + if (distanceSnapGrid == null) + return base.SnapScreenSpacePositionToValidTime(screenSpacePosition); + + (Vector2 pos, double time) = distanceSnapGrid.GetSnappedPosition(distanceSnapGrid.ToLocalSpace(screenSpacePosition)); + + return new SnapResult(distanceSnapGrid.ToScreenSpace(pos), time); + } + + private void updateDistanceSnapGrid() + { + distanceSnapGridContainer.Clear(); + distanceSnapGridCache.Invalidate(); + + if (BlueprintContainer.CurrentTool is SelectTool && !EditorBeatmap.SelectedHitObjects.Any()) + return; + + if ((distanceSnapGrid = createDistanceSnapGrid(EditorBeatmap.SelectedHitObjects)) != null) + { + distanceSnapGridContainer.Add(distanceSnapGrid); + distanceSnapGridCache.Validate(); + } + } + + private DistanceSnapGrid createDistanceSnapGrid(IEnumerable selectedHitObjects) { if (BlueprintContainer.CurrentTool is SpinnerCompositionTool) return null; diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index b437d81054..fd8af0afd5 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -3,8 +3,8 @@ using System; using System.Collections.Generic; +using System.Collections.Specialized; using System.Linq; -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -52,8 +52,9 @@ namespace osu.Game.Rulesets.Edit protected ComposeBlueprintContainer BlueprintContainer { get; private set; } private DrawableEditRulesetWrapper drawableRulesetWrapper; - private Container distanceSnapGridContainer; - private DistanceSnapGrid distanceSnapGrid; + + protected readonly Container LayerBelowRuleset = new Container { RelativeSizeAxes = Axes.Both }; + private readonly List layerContainers = new List(); private InputManager inputManager; @@ -87,7 +88,7 @@ namespace osu.Game.Rulesets.Edit var layerBelowRuleset = drawableRulesetWrapper.CreatePlayfieldAdjustmentContainer().WithChildren(new Drawable[] { - distanceSnapGridContainer = new Container { RelativeSizeAxes = Axes.Both }, + LayerBelowRuleset, new EditorPlayfieldBorder { RelativeSizeAxes = Axes.Both } }); @@ -139,7 +140,7 @@ namespace osu.Game.Rulesets.Edit setSelectTool(); - BlueprintContainer.SelectionChanged += selectionChanged; + EditorBeatmap.SelectedHitObjects.CollectionChanged += selectionChanged; } protected override bool OnKeyDown(KeyDownEvent e) @@ -165,16 +166,6 @@ namespace osu.Game.Rulesets.Edit inputManager = GetContainingInputManager(); } - private double lastGridUpdateTime; - - protected override void Update() - { - base.Update(); - - if (EditorClock.CurrentTime != lastGridUpdateTime && !(BlueprintContainer.CurrentTool is SelectTool)) - showGridFor(Enumerable.Empty()); - } - protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); @@ -188,19 +179,13 @@ namespace osu.Game.Rulesets.Edit }); } - private void selectionChanged(IEnumerable selectedHitObjects) + private void selectionChanged(object sender, NotifyCollectionChangedEventArgs changedArgs) { - var hitObjects = selectedHitObjects.ToArray(); - - if (hitObjects.Any()) + if (EditorBeatmap.SelectedHitObjects.Any()) { // ensure in selection mode if a selection is made. setSelectTool(); - - showGridFor(hitObjects); } - else - distanceSnapGridContainer.Hide(); } private void setSelectTool() => toolboxCollection.Items.First().Select(); @@ -209,30 +194,12 @@ namespace osu.Game.Rulesets.Edit { BlueprintContainer.CurrentTool = tool; - if (tool is SelectTool) - distanceSnapGridContainer.Hide(); - else - { + if (!(tool is SelectTool)) EditorBeatmap.SelectedHitObjects.Clear(); - showGridFor(Enumerable.Empty()); - } - } - - private void showGridFor(IEnumerable selectedHitObjects) - { - distanceSnapGridContainer.Clear(); - distanceSnapGrid = CreateDistanceSnapGrid(selectedHitObjects); - - if (distanceSnapGrid != null) - { - distanceSnapGridContainer.Child = distanceSnapGrid; - distanceSnapGridContainer.Show(); - } - - lastGridUpdateTime = EditorClock.CurrentTime; } public override IEnumerable HitObjects => drawableRulesetWrapper.Playfield.AllHitObjects; + public override bool CursorInPlacementArea => drawableRulesetWrapper.Playfield.ReceivePositionalInputAt(inputManager.CurrentState.Mouse.Position); protected abstract IReadOnlyList CompositionTools { get; } @@ -257,21 +224,11 @@ namespace osu.Game.Rulesets.Edit if (adjustableClock.CurrentTime < hitObject.StartTime) adjustableClock.Seek(hitObject.StartTime); } - - showGridFor(Enumerable.Empty()); } public void Delete(HitObject hitObject) => EditorBeatmap.Remove(hitObject); - public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) - { - if (distanceSnapGrid == null) return new SnapResult(screenSpacePosition, null); - - // TODO: move distance snap grid to OsuHitObjectComposer. - (Vector2 pos, double time) = distanceSnapGrid.GetSnappedPosition(distanceSnapGrid.ToLocalSpace(screenSpacePosition)); - - return new SnapResult(distanceSnapGrid.ToScreenSpace(pos), time); - } + public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, null); public override float GetBeatSnapDistanceAt(double referenceTime) { @@ -321,14 +278,6 @@ namespace osu.Game.Rulesets.Edit /// public abstract bool CursorInPlacementArea { get; } - /// - /// Creates the applicable for a selection. - /// - /// The selection. - /// The for . If empty, a grid is returned for the current point in time. - [CanBeNull] - protected virtual DistanceSnapGrid CreateDistanceSnapGrid([NotNull] IEnumerable selectedHitObjects) => null; - public abstract SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition); public abstract float GetBeatSnapDistanceAt(double referenceTime); diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index e38df3d812..1e8a35c047 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.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.Collections.Specialized; using System.Diagnostics; using System.Linq; @@ -29,8 +28,6 @@ namespace osu.Game.Screens.Edit.Compose.Components /// public abstract class BlueprintContainer : CompositeDrawable, IKeyBindingHandler { - public event Action> SelectionChanged; - protected DragBox DragBox { get; private set; } protected Container SelectionBlueprints { get; private set; } @@ -88,8 +85,6 @@ namespace osu.Game.Screens.Edit.Compose.Components SelectionBlueprints.FirstOrDefault(b => b.HitObject == o)?.Deselect(); break; } - - SelectionChanged?.Invoke(selectedHitObjects); }; } From 700b5e0c73c75566a9b73267977111c819e6737c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 21 May 2020 17:47:14 +0900 Subject: [PATCH 0841/1117] Adjust design --- .../ContractedPanelMiddleContent.cs | 30 ++++++------------- osu.Game/Screens/Ranking/ScorePanel.cs | 6 ++-- 2 files changed, 12 insertions(+), 24 deletions(-) diff --git a/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs index 1d7d5c4130..a263a03a77 100644 --- a/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs @@ -65,28 +65,16 @@ namespace osu.Game.Screens.Ranking.Contracted }, Children = new Drawable[] { - // Buffered container is used to prevent 1px bleed outside the masking region - new BufferedContainer + new Box { RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4Extensions.FromHex("444") - }, - new UserCoverBackground - { - RelativeSizeAxes = Axes.Both, - User = score.User, - }, - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.5f), Color4Extensions.FromHex("#444")) - }, - } + Colour = Color4Extensions.FromHex("444") + }, + new UserCoverBackground + { + RelativeSizeAxes = Axes.Both, + User = score.User, + Colour = ColourInfo.GradientVertical(Color4.White.Opacity(0.5f), Color4Extensions.FromHex("#444").Opacity(0)) }, new FillFlowContainer { @@ -100,7 +88,7 @@ namespace osu.Game.Screens.Ranking.Contracted { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Size = new Vector2(140), + Size = new Vector2(110), Masking = true, CornerExponent = 2.5f, CornerRadius = 20, diff --git a/osu.Game/Screens/Ranking/ScorePanel.cs b/osu.Game/Screens/Ranking/ScorePanel.cs index baca2fd9e1..305d4ee921 100644 --- a/osu.Game/Screens/Ranking/ScorePanel.cs +++ b/osu.Game/Screens/Ranking/ScorePanel.cs @@ -23,12 +23,12 @@ namespace osu.Game.Screens.Ranking /// /// Width of the panel when contracted. /// - public const float CONTRACTED_WIDTH = 160; + public const float CONTRACTED_WIDTH = 130; /// /// Height of the panel when contracted. /// - private const float contracted_height = 385; + private const float contracted_height = 355; /// /// Width of the panel when expanded. @@ -48,7 +48,7 @@ namespace osu.Game.Screens.Ranking /// /// Height of the top layer when the panel is contracted. /// - private const float contracted_top_layer_height = 40; + private const float contracted_top_layer_height = 30; /// /// Duration for the panel to resize into its expanded/contracted size. From b6a1d1a2fc5d9a1927b4994861b23c6f46f09fd4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 21 May 2020 18:07:31 +0900 Subject: [PATCH 0842/1117] Improve transforms between state changes --- osu.Game/Screens/Ranking/ScorePanel.cs | 8 ++++---- osu.Game/Screens/Ranking/ScorePanelList.cs | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Ranking/ScorePanel.cs b/osu.Game/Screens/Ranking/ScorePanel.cs index 305d4ee921..2f6146a5e7 100644 --- a/osu.Game/Screens/Ranking/ScorePanel.cs +++ b/osu.Game/Screens/Ranking/ScorePanel.cs @@ -175,9 +175,6 @@ namespace osu.Game.Screens.Ranking private void updateState() { - topLayerContainer.MoveToY(0, resize_duration, Easing.OutQuint); - middleLayerContainer.MoveToY(0, resize_duration, Easing.OutQuint); - topLayerContent?.FadeOut(content_fade_duration).Expire(); middleLayerContent?.FadeOut(content_fade_duration).Expire(); @@ -203,7 +200,10 @@ namespace osu.Game.Screens.Ranking break; } - using (BeginDelayedSequence(resize_duration + top_layer_expand_delay, true)) + bool topLayerExpanded = topLayerContainer.Y < 0; + + // If the top layer was already expanded, then we don't need to wait for the resize and can instead transform immediately. This looks better when changing the panel state. + using (BeginDelayedSequence(topLayerExpanded ? 0 : resize_duration + top_layer_expand_delay, true)) { switch (state) { diff --git a/osu.Game/Screens/Ranking/ScorePanelList.cs b/osu.Game/Screens/Ranking/ScorePanelList.cs index cc6842b2dd..894be7e775 100644 --- a/osu.Game/Screens/Ranking/ScorePanelList.cs +++ b/osu.Game/Screens/Ranking/ScorePanelList.cs @@ -68,6 +68,7 @@ namespace osu.Game.Screens.Ranking if (expandedPanel != null) expandedPanel.State = PanelState.Contracted; + expandedPanel = panel; } From 9f868be872f0d55d9209536489008606f5dc171d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 21 May 2020 18:39:22 +0900 Subject: [PATCH 0843/1117] Create common TestScoreInfo type --- .../TestSceneContractedPanelMiddleContent.cs | 32 +-------- .../TestSceneExpandedPanelMiddleContent.cs | 31 +-------- .../TestSceneExpandedPanelTopContent.cs | 4 +- .../Visual/Ranking/TestSceneResultsScreen.cs | 26 +------- .../Visual/Ranking/TestSceneScorePanel.cs | 65 +++---------------- .../Visual/Ranking/TestSceneScorePanelList.cs | 59 ++++------------- osu.Game/Tests/TestScoreInfo.cs | 50 ++++++++++++++ 7 files changed, 80 insertions(+), 187 deletions(-) create mode 100644 osu.Game/Tests/TestScoreInfo.cs diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneContractedPanelMiddleContent.cs b/osu.Game.Tests/Visual/Ranking/TestSceneContractedPanelMiddleContent.cs index f7694c10ec..e1e00e3c2b 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneContractedPanelMiddleContent.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneContractedPanelMiddleContent.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 NUnit.Framework; using osu.Framework.Allocation; @@ -14,10 +13,7 @@ using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets; -using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; -using osu.Game.Rulesets.Osu.Mods; -using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens.Ranking; using osu.Game.Screens.Ranking.Contracted; @@ -37,7 +33,7 @@ namespace osu.Game.Tests.Visual.Ranking { var author = new User { Username = "mapper_name" }; - AddStep("show example score", () => showPanel(createTestBeatmap(author), createTestScore())); + AddStep("show example score", () => showPanel(createTestBeatmap(author), new TestScoreInfo(new OsuRuleset().RulesetInfo))); AddAssert("mapper name present", () => this.ChildrenOfType().Any(spriteText => spriteText.Text == "mapper_name")); } @@ -45,7 +41,7 @@ namespace osu.Game.Tests.Visual.Ranking [Test] public void TestMapWithUnknownMapper() { - AddStep("show example score", () => showPanel(createTestBeatmap(null), createTestScore())); + AddStep("show example score", () => showPanel(createTestBeatmap(null), new TestScoreInfo(new OsuRuleset().RulesetInfo))); AddAssert("mapped by text not present", () => this.ChildrenOfType().All(spriteText => !containsAny(spriteText.Text, "mapped", "by"))); @@ -66,30 +62,6 @@ namespace osu.Game.Tests.Visual.Ranking return new TestWorkingBeatmap(beatmap); } - private ScoreInfo createTestScore() => new ScoreInfo - { - User = new User - { - Id = 2, - Username = "peppy", - CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", - }, - Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo, - Mods = new Mod[] { new OsuModHardRock(), new OsuModDoubleTime() }, - TotalScore = 999999, - Accuracy = 0.95, - MaxCombo = 999, - Rank = ScoreRank.S, - Date = DateTimeOffset.Now, - Statistics = - { - { HitResult.Miss, 1 }, - { HitResult.Meh, 50 }, - { HitResult.Good, 100 }, - { HitResult.Great, 300 }, - } - }; - private bool containsAny(string text, params string[] stringsToMatch) => stringsToMatch.Any(text.Contains); private class ContractedPanelMiddleContentContainer : Container diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs index 106b4187ee..69511b85c0 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.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 NUnit.Framework; using osu.Framework.Allocation; @@ -14,10 +13,7 @@ using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets; -using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; -using osu.Game.Rulesets.Osu.Mods; -using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens.Ranking; using osu.Game.Screens.Ranking.Expanded; @@ -37,7 +33,7 @@ namespace osu.Game.Tests.Visual.Ranking { var author = new User { Username = "mapper_name" }; - AddStep("show example score", () => showPanel(createTestBeatmap(author), createTestScore())); + AddStep("show example score", () => showPanel(createTestBeatmap(author), new TestScoreInfo(new OsuRuleset().RulesetInfo))); AddAssert("mapper name present", () => this.ChildrenOfType().Any(spriteText => spriteText.Text == "mapper_name")); } @@ -45,7 +41,7 @@ namespace osu.Game.Tests.Visual.Ranking [Test] public void TestMapWithUnknownMapper() { - AddStep("show example score", () => showPanel(createTestBeatmap(null), createTestScore())); + AddStep("show example score", () => showPanel(createTestBeatmap(null), new TestScoreInfo(new OsuRuleset().RulesetInfo))); AddAssert("mapped by text not present", () => this.ChildrenOfType().All(spriteText => !containsAny(spriteText.Text, "mapped", "by"))); @@ -66,29 +62,6 @@ namespace osu.Game.Tests.Visual.Ranking return new TestWorkingBeatmap(beatmap); } - private ScoreInfo createTestScore() => new ScoreInfo - { - User = new User - { - Id = 2, - Username = "peppy", - }, - Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo, - Mods = new Mod[] { new OsuModHardRock(), new OsuModDoubleTime() }, - TotalScore = 999999, - Accuracy = 0.95, - MaxCombo = 999, - Rank = ScoreRank.S, - Date = DateTimeOffset.Now, - Statistics = - { - { HitResult.Miss, 1 }, - { HitResult.Meh, 50 }, - { HitResult.Good, 100 }, - { HitResult.Great, 300 }, - } - }; - private bool containsAny(string text, params string[] stringsToMatch) => stringsToMatch.Any(text.Contains); private class ExpandedPanelMiddleContentContainer : Container diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelTopContent.cs b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelTopContent.cs index afaa607099..a32bcbe7f0 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelTopContent.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelTopContent.cs @@ -5,8 +5,8 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Game.Rulesets.Osu; using osu.Game.Screens.Ranking.Expanded; -using osu.Game.Users; using osuTK; namespace osu.Game.Tests.Visual.Ranking @@ -27,7 +27,7 @@ namespace osu.Game.Tests.Visual.Ranking RelativeSizeAxes = Axes.Both, Colour = Color4Extensions.FromHex("#444"), }, - new ExpandedPanelTopContent(new User { Id = 2, Username = "peppy" }), + new ExpandedPanelTopContent(new TestScoreInfo(new OsuRuleset().RulesetInfo).User), } }; } diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs b/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs index aa0ce89d93..242766ad4b 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.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. -using System; -using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; @@ -11,13 +9,10 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Screens; using osu.Game.Beatmaps; using osu.Game.Rulesets.Osu; -using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens; using osu.Game.Screens.Play; using osu.Game.Screens.Ranking; -using osu.Game.Tests.Beatmaps; -using osu.Game.Users; namespace osu.Game.Tests.Visual.Ranking { @@ -41,26 +36,7 @@ namespace osu.Game.Tests.Visual.Ranking Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmapInfo); } - private TestSoloResults createResultsScreen() => new TestSoloResults(new ScoreInfo - { - TotalScore = 2845370, - Accuracy = 0.98, - MaxCombo = 123, - Rank = ScoreRank.A, - Date = DateTimeOffset.Now, - Statistics = new Dictionary - { - { HitResult.Great, 50 }, - { HitResult.Good, 20 }, - { HitResult.Meh, 50 }, - { HitResult.Miss, 1 } - }, - Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo, - User = new User - { - Username = "peppy", - } - }); + private TestSoloResults createResultsScreen() => new TestSoloResults(new TestScoreInfo(new OsuRuleset().RulesetInfo)); [Test] public void ResultsWithoutPlayer() diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneScorePanel.cs b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanel.cs index 0dbafb18bc..fdb77c14a3 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneScorePanel.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanel.cs @@ -1,17 +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 NUnit.Framework; using osu.Framework.Graphics; -using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; -using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens.Ranking; -using osu.Game.Tests.Beatmaps; -using osu.Game.Users; namespace osu.Game.Tests.Visual.Ranking { @@ -20,9 +15,7 @@ namespace osu.Game.Tests.Visual.Ranking [Test] public void TestDRank() { - var score = createScore(); - score.Accuracy = 0.5; - score.Rank = ScoreRank.D; + var score = new TestScoreInfo(new OsuRuleset().RulesetInfo) { Accuracy = 0.5, Rank = ScoreRank.D }; addPanelStep(score); } @@ -30,9 +23,7 @@ namespace osu.Game.Tests.Visual.Ranking [Test] public void TestCRank() { - var score = createScore(); - score.Accuracy = 0.75; - score.Rank = ScoreRank.C; + var score = new TestScoreInfo(new OsuRuleset().RulesetInfo) { Accuracy = 0.75, Rank = ScoreRank.C }; addPanelStep(score); } @@ -40,9 +31,7 @@ namespace osu.Game.Tests.Visual.Ranking [Test] public void TestBRank() { - var score = createScore(); - score.Accuracy = 0.85; - score.Rank = ScoreRank.B; + var score = new TestScoreInfo(new OsuRuleset().RulesetInfo) { Accuracy = 0.85, Rank = ScoreRank.B }; addPanelStep(score); } @@ -50,9 +39,7 @@ namespace osu.Game.Tests.Visual.Ranking [Test] public void TestARank() { - var score = createScore(); - score.Accuracy = 0.925; - score.Rank = ScoreRank.A; + var score = new TestScoreInfo(new OsuRuleset().RulesetInfo) { Accuracy = 0.925, Rank = ScoreRank.A }; addPanelStep(score); } @@ -60,9 +47,7 @@ namespace osu.Game.Tests.Visual.Ranking [Test] public void TestSRank() { - var score = createScore(); - score.Accuracy = 0.975; - score.Rank = ScoreRank.S; + var score = new TestScoreInfo(new OsuRuleset().RulesetInfo) { Accuracy = 0.975, Rank = ScoreRank.S }; addPanelStep(score); } @@ -70,9 +55,7 @@ namespace osu.Game.Tests.Visual.Ranking [Test] public void TestAlmostSSRank() { - var score = createScore(); - score.Accuracy = 0.9999; - score.Rank = ScoreRank.S; + var score = new TestScoreInfo(new OsuRuleset().RulesetInfo) { Accuracy = 0.9999, Rank = ScoreRank.S }; addPanelStep(score); } @@ -80,9 +63,7 @@ namespace osu.Game.Tests.Visual.Ranking [Test] public void TestSSRank() { - var score = createScore(); - score.Accuracy = 1; - score.Rank = ScoreRank.X; + var score = new TestScoreInfo(new OsuRuleset().RulesetInfo) { Accuracy = 1, Rank = ScoreRank.X }; addPanelStep(score); } @@ -90,9 +71,7 @@ namespace osu.Game.Tests.Visual.Ranking [Test] public void TestAllHitResults() { - var score = createScore(); - score.Statistics[HitResult.Perfect] = 350; - score.Statistics[HitResult.Ok] = 200; + var score = new TestScoreInfo(new OsuRuleset().RulesetInfo) { Statistics = { [HitResult.Perfect] = 350, [HitResult.Ok] = 200 } }; addPanelStep(score); } @@ -100,9 +79,7 @@ namespace osu.Game.Tests.Visual.Ranking [Test] public void TestContractedPanel() { - var score = createScore(); - score.Accuracy = 0.925; - score.Rank = ScoreRank.A; + var score = new TestScoreInfo(new OsuRuleset().RulesetInfo) { Accuracy = 0.925, Rank = ScoreRank.A }; addPanelStep(score, PanelState.Contracted); } @@ -116,29 +93,5 @@ namespace osu.Game.Tests.Visual.Ranking State = state }; }); - - private ScoreInfo createScore() => new ScoreInfo - { - User = new User - { - Id = 2, - Username = "peppy", - CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", - }, - Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo, - Mods = new Mod[] { new OsuModHardRock(), new OsuModDoubleTime() }, - TotalScore = 2845370, - Accuracy = 0.95, - MaxCombo = 999, - Rank = ScoreRank.S, - Date = DateTimeOffset.Now, - Statistics = - { - { HitResult.Miss, 1 }, - { HitResult.Meh, 50 }, - { HitResult.Good, 100 }, - { HitResult.Great, 300 }, - } - }; } } diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs index 4964af8784..81a9b22992 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs @@ -1,16 +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.Framework.Graphics; -using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; -using osu.Game.Rulesets.Osu.Mods; -using osu.Game.Rulesets.Scoring; -using osu.Game.Scoring; using osu.Game.Screens.Ranking; -using osu.Game.Tests.Beatmaps; -using osu.Game.Users; namespace osu.Game.Tests.Visual.Ranking { @@ -26,44 +19,20 @@ namespace osu.Game.Tests.Visual.Ranking Add(list); - list.AddScore(createScore()); - list.AddScore(createScore()); - list.AddScore(createScore()); - list.AddScore(createScore()); - list.AddScore(createScore()); - list.AddScore(createScore()); - list.AddScore(createScore()); - list.AddScore(createScore()); - list.AddScore(createScore()); - list.AddScore(createScore()); - list.AddScore(createScore()); - list.AddScore(createScore()); - list.AddScore(createScore()); - list.AddScore(createScore()); + list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo)); + list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo)); + list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo)); + list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo)); + list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo)); + list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo)); + list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo)); + list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo)); + list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo)); + list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo)); + list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo)); + list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo)); + list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo)); + list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo)); } - - private ScoreInfo createScore() => new ScoreInfo - { - User = new User - { - Id = 2, - Username = "peppy", - CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", - }, - Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo, - Mods = new Mod[] { new OsuModHardRock(), new OsuModDoubleTime() }, - TotalScore = 2845370, - Accuracy = 0.95, - MaxCombo = 999, - Rank = ScoreRank.S, - Date = DateTimeOffset.Now, - Statistics = - { - { HitResult.Miss, 1 }, - { HitResult.Meh, 50 }, - { HitResult.Good, 100 }, - { HitResult.Great, 300 }, - } - }; } } diff --git a/osu.Game/Tests/TestScoreInfo.cs b/osu.Game/Tests/TestScoreInfo.cs new file mode 100644 index 0000000000..155129e181 --- /dev/null +++ b/osu.Game/Tests/TestScoreInfo.cs @@ -0,0 +1,50 @@ +// 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; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Scoring; +using osu.Game.Scoring; +using osu.Game.Tests.Beatmaps; +using osu.Game.Users; + +namespace osu.Game.Tests +{ + public class TestScoreInfo : ScoreInfo + { + public TestScoreInfo(RulesetInfo ruleset) + { + User = new User + { + Id = 2, + Username = "peppy", + CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", + }; + + Beatmap = new TestBeatmap(ruleset).BeatmapInfo; + Mods = new Mod[] { new TestModHardRock(), new TestModDoubleTime() }; + + TotalScore = 2845370; + Accuracy = 0.95; + MaxCombo = 999; + Rank = ScoreRank.S; + Date = DateTimeOffset.Now; + + Statistics[HitResult.Miss] = 1; + Statistics[HitResult.Meh] = 50; + Statistics[HitResult.Good] = 100; + Statistics[HitResult.Great] = 300; + } + + private class TestModHardRock : ModHardRock + { + public override double ScoreMultiplier => 1; + } + + private class TestModDoubleTime : ModDoubleTime + { + public override double ScoreMultiplier => 1; + } + } +} From 45b59f574dcd09ba389adc85ec2619a177f92e5e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 21 May 2020 18:43:12 +0900 Subject: [PATCH 0844/1117] Fix TestSceneResultsScreen crashing --- osu.Game/Tests/TestScoreInfo.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Tests/TestScoreInfo.cs b/osu.Game/Tests/TestScoreInfo.cs index 155129e181..1193a29d70 100644 --- a/osu.Game/Tests/TestScoreInfo.cs +++ b/osu.Game/Tests/TestScoreInfo.cs @@ -23,6 +23,8 @@ namespace osu.Game.Tests }; Beatmap = new TestBeatmap(ruleset).BeatmapInfo; + Ruleset = ruleset; + RulesetID = ruleset.ID ?? 0; Mods = new Mod[] { new TestModHardRock(), new TestModDoubleTime() }; TotalScore = 2845370; From 717869225e55f0ff2a6cd27d9f797ffb9f62b868 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 21 May 2020 19:51:36 +0900 Subject: [PATCH 0845/1117] Rework list to use a scroll container + add spacing --- osu.Game/Screens/Ranking/ScorePanelList.cs | 60 ++++++++++++++-------- 1 file changed, 38 insertions(+), 22 deletions(-) diff --git a/osu.Game/Screens/Ranking/ScorePanelList.cs b/osu.Game/Screens/Ranking/ScorePanelList.cs index 894be7e775..52a9f27db8 100644 --- a/osu.Game/Screens/Ranking/ScorePanelList.cs +++ b/osu.Game/Screens/Ranking/ScorePanelList.cs @@ -1,12 +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.Collections.Generic; using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Utils; +using osu.Game.Graphics.Containers; using osu.Game.Scoring; using osuTK; @@ -14,19 +13,36 @@ namespace osu.Game.Screens.Ranking { public class ScorePanelList : CompositeDrawable { - private readonly Flow panels; + /// + /// Normal spacing between all panels. + /// + private const float panel_spacing = 5; + + /// + /// Spacing around both sides of the expanded panel. This is added on top of . + /// + private const float expanded_panel_spacing = 15; + + private readonly Flow flow; + private readonly ScrollContainer scroll; + private ScorePanel expandedPanel; public ScorePanelList() { RelativeSizeAxes = Axes.Both; - InternalChild = panels = new Flow + InternalChild = scroll = new OsuScrollContainer(Direction.Horizontal) { - Anchor = Anchor.Centre, - Origin = Anchor.Custom, - Direction = FillDirection.Horizontal, - AutoSizeAxes = Axes.Both, + RelativeSizeAxes = Axes.Both, + Child = flow = new Flow + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(panel_spacing, 0), + AutoSizeAxes = Axes.Both, + } }; } @@ -34,8 +50,8 @@ namespace osu.Game.Screens.Ranking { var panel = new ScorePanel(score) { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, }; panel.StateChanged += s => onPanelStateChanged(panel, s); @@ -43,22 +59,14 @@ namespace osu.Game.Screens.Ranking // Todo: Temporary panel.State = expandedPanel == null ? PanelState.Expanded : PanelState.Contracted; - panels.Add(panel); + flow.Add(panel); } - public void RemoveScore(ScoreInfo score) => panels.RemoveAll(p => p.Score == score); - - protected override void UpdateAfterChildren() + protected override void Update() { - base.UpdateAfterChildren(); + base.Update(); - if (expandedPanel != null) - { - var firstPanel = panels.FlowingChildren.First(); - var target = expandedPanel.DrawPosition.X - firstPanel.DrawPosition.X + expandedPanel.DrawSize.X / 2; - - panels.OriginPosition = new Vector2((float)Interpolation.Lerp(panels.OriginPosition.X, target, Math.Clamp(Math.Abs(Time.Elapsed) / 80, 0, 1)), panels.DrawHeight / 2); - } + flow.Padding = new MarginPadding { Horizontal = DrawWidth / 2f - ScorePanel.EXPANDED_WIDTH / 2f - expanded_panel_spacing }; } private void onPanelStateChanged(ScorePanel panel, PanelState state) @@ -67,9 +75,17 @@ namespace osu.Game.Screens.Ranking return; if (expandedPanel != null) + { + expandedPanel.Margin = new MarginPadding(0); expandedPanel.State = PanelState.Contracted; + } expandedPanel = panel; + expandedPanel.Margin = new MarginPadding { Horizontal = expanded_panel_spacing }; + + float panelOffset = flow.IndexOf(expandedPanel) * (ScorePanel.CONTRACTED_WIDTH + panel_spacing); + + scroll.ScrollTo(panelOffset); } private class Flow : FillFlowContainer From 7b82a5d792d4fae9103bff89de4db4e81a8e5063 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 21 May 2020 20:48:08 +0900 Subject: [PATCH 0846/1117] Fix score order --- osu.Game/Screens/Ranking/ScorePanelList.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Screens/Ranking/ScorePanelList.cs b/osu.Game/Screens/Ranking/ScorePanelList.cs index 52a9f27db8..0e0ed4f60d 100644 --- a/osu.Game/Screens/Ranking/ScorePanelList.cs +++ b/osu.Game/Screens/Ranking/ScorePanelList.cs @@ -90,8 +90,7 @@ namespace osu.Game.Screens.Ranking private class Flow : FillFlowContainer { - // Todo: Order is wrong. - public override IEnumerable FlowingChildren => AliveInternalChildren.OfType().OrderBy(s => s.Score.TotalScore); + public override IEnumerable FlowingChildren => AliveInternalChildren.OfType().OrderByDescending(s => s.Score.TotalScore).ThenByDescending(s => s.Score.OnlineScoreID); } } } From d0f74c2b683e949ba780c7d6e8011a184b5468d6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 21 May 2020 20:48:25 +0900 Subject: [PATCH 0847/1117] Refactor initial state --- .../Visual/Ranking/TestSceneScorePanelList.cs | 47 ++++++++++------ osu.Game/Screens/Ranking/ResultsScreen.cs | 4 +- osu.Game/Screens/Ranking/ScorePanelList.cs | 54 +++++++++---------- 3 files changed, 58 insertions(+), 47 deletions(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs index 81a9b22992..f00bf7e151 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs @@ -1,38 +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 NUnit.Framework; using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; using osu.Game.Rulesets.Osu; using osu.Game.Screens.Ranking; +using osuTK.Graphics; namespace osu.Game.Tests.Visual.Ranking { public class TestSceneScorePanelList : OsuTestScene { - public TestSceneScorePanelList() + private ScorePanelList list; + + [SetUp] + public void Setup() => Schedule(() => { - var list = new ScorePanelList + Child = list = new ScorePanelList(new TestScoreInfo(new OsuRuleset().RulesetInfo)) { Anchor = Anchor.Centre, Origin = Anchor.Centre, }; - Add(list); + Add(new Box + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.Y, + Width = 1, + Colour = Color4.Red + }); + }); - list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo)); - list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo)); - list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo)); - list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo)); - list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo)); - list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo)); - list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo)); - list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo)); - list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo)); - list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo)); - list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo)); - list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo)); - list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo)); - list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo)); + [Test] + public void TestSingleScore() + { + } + + [Test] + public void TestManyScores() + { + AddStep("add many scores", () => + { + for (int i = 0; i < 20; i++) + list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo)); + }); } } } diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index 652d158fbb..cdceaa939e 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -63,7 +63,7 @@ namespace osu.Game.Screens.Ranking { new ResultsScrollContainer { - Child = panels = new ScorePanelList + Child = panels = new ScorePanelList(Score) { RelativeSizeAxes = Axes.Both, } @@ -98,8 +98,6 @@ namespace osu.Game.Screens.Ranking } }; - panels.AddScore(Score); - if (player != null && allowRetry) { buttons.Add(new RetryButton { Width = 300 }); diff --git a/osu.Game/Screens/Ranking/ScorePanelList.cs b/osu.Game/Screens/Ranking/ScorePanelList.cs index 0e0ed4f60d..c2fd487767 100644 --- a/osu.Game/Screens/Ranking/ScorePanelList.cs +++ b/osu.Game/Screens/Ranking/ScorePanelList.cs @@ -28,7 +28,7 @@ namespace osu.Game.Screens.Ranking private ScorePanel expandedPanel; - public ScorePanelList() + public ScorePanelList(ScoreInfo initialScore) { RelativeSizeAxes = Axes.Both; @@ -44,48 +44,48 @@ namespace osu.Game.Screens.Ranking AutoSizeAxes = Axes.Both, } }; + + AddScore(initialScore); + ShowScore(initialScore); } public void AddScore(ScoreInfo score) { - var panel = new ScorePanel(score) + flow.Add(new ScorePanel(score) { Anchor = Anchor.Centre, Origin = Anchor.Centre, - }; + }.With(p => + { + p.StateChanged += s => + { + if (s == PanelState.Expanded) + ShowScore(score); + }; + })); + } - panel.StateChanged += s => onPanelStateChanged(panel, s); + public void ShowScore(ScoreInfo score) + { + foreach (var p in flow.Where(p => p.Score != score)) + p.State = PanelState.Contracted; - // Todo: Temporary - panel.State = expandedPanel == null ? PanelState.Expanded : PanelState.Contracted; + if (expandedPanel != null) + expandedPanel.Margin = new MarginPadding(0); - flow.Add(panel); + expandedPanel = flow.Single(p => p.Score == score); + expandedPanel.State = PanelState.Expanded; + expandedPanel.Margin = new MarginPadding { Horizontal = expanded_panel_spacing }; + + float scrollOffset = flow.IndexOf(expandedPanel) * (ScorePanel.CONTRACTED_WIDTH + panel_spacing); + scroll.ScrollTo(scrollOffset); } protected override void Update() { base.Update(); - flow.Padding = new MarginPadding { Horizontal = DrawWidth / 2f - ScorePanel.EXPANDED_WIDTH / 2f - expanded_panel_spacing }; - } - - private void onPanelStateChanged(ScorePanel panel, PanelState state) - { - if (state == PanelState.Contracted) - return; - - if (expandedPanel != null) - { - expandedPanel.Margin = new MarginPadding(0); - expandedPanel.State = PanelState.Contracted; - } - - expandedPanel = panel; - expandedPanel.Margin = new MarginPadding { Horizontal = expanded_panel_spacing }; - - float panelOffset = flow.IndexOf(expandedPanel) * (ScorePanel.CONTRACTED_WIDTH + panel_spacing); - - scroll.ScrollTo(panelOffset); + flow.Padding = new MarginPadding { Horizontal = DrawWidth / 2f - expandedPanel.DrawWidth / 2f - expanded_panel_spacing }; } private class Flow : FillFlowContainer From 45244683de150597b66234e5ee78b78c0f718189 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 21 May 2020 22:07:06 +0900 Subject: [PATCH 0848/1117] Fix scrolling (1-frame + maintain scroll position) --- .../Visual/Ranking/TestSceneScorePanelList.cs | 49 +++++++++++- osu.Game/Screens/Ranking/ScorePanel.cs | 71 +++++++++------- osu.Game/Screens/Ranking/ScorePanelList.cs | 80 ++++++++++++++++--- 3 files changed, 154 insertions(+), 46 deletions(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs index f00bf7e151..89aef377c8 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs @@ -1,10 +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.Linq; using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; +using osu.Framework.Testing; +using osu.Framework.Utils; using osu.Game.Rulesets.Osu; +using osu.Game.Scoring; using osu.Game.Screens.Ranking; using osuTK.Graphics; @@ -12,12 +16,13 @@ namespace osu.Game.Tests.Visual.Ranking { public class TestSceneScorePanelList : OsuTestScene { + private ScoreInfo initialScore; private ScorePanelList list; [SetUp] public void Setup() => Schedule(() => { - Child = list = new ScorePanelList(new TestScoreInfo(new OsuRuleset().RulesetInfo)) + Child = list = new ScorePanelList(initialScore = new TestScoreInfo(new OsuRuleset().RulesetInfo)) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -36,16 +41,52 @@ namespace osu.Game.Tests.Visual.Ranking [Test] public void TestSingleScore() { + assertPanelCentred(); } [Test] - public void TestManyScores() + public void TestAddManyScoresAfter() { - AddStep("add many scores", () => + AddStep("add scores", () => { for (int i = 0; i < 20; i++) - list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo)); + list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo) { TotalScore = initialScore.TotalScore - i - 1 }); }); + + assertPanelCentred(); } + + [Test] + public void TestAddManyScoresBefore() + { + AddStep("add scores", () => + { + for (int i = 0; i < 20; i++) + list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo) { TotalScore = initialScore.TotalScore + i + 1 }); + }); + + assertPanelCentred(); + } + + [Test] + public void TestAddManyPanelsOnBothSides() + { + AddStep("add scores after", () => + { + for (int i = 0; i < 20; i++) + list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo) { TotalScore = initialScore.TotalScore - i - 1 }); + + for (int i = 0; i < 20; i++) + list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo) { TotalScore = initialScore.TotalScore + i + 1 }); + }); + + assertPanelCentred(); + } + + private void assertPanelCentred() => AddUntilStep("expanded panel centred", () => + { + var expandedPanel = list.ChildrenOfType().Single(p => p.State == PanelState.Expanded); + return Precision.AlmostEquals(expandedPanel.ScreenSpaceDrawQuad.Centre.X, list.ScreenSpaceDrawQuad.Centre.X, 1); + }); } } diff --git a/osu.Game/Screens/Ranking/ScorePanel.cs b/osu.Game/Screens/Ranking/ScorePanel.cs index 2f6146a5e7..2933bbddd1 100644 --- a/osu.Game/Screens/Ranking/ScorePanel.cs +++ b/osu.Game/Screens/Ranking/ScorePanel.cs @@ -78,6 +78,8 @@ namespace osu.Game.Screens.Ranking public event Action StateChanged; public readonly ScoreInfo Score; + private Container content; + private Container topLayerContainer; private Drawable topLayerBackground; private Container topLayerContentContainer; @@ -96,41 +98,46 @@ namespace osu.Game.Screens.Ranking [BackgroundDependencyLoader] private void load() { - InternalChildren = new Drawable[] + InternalChild = content = new Container { - topLayerContainer = new Container + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Children = new Drawable[] { - Name = "Top layer", - RelativeSizeAxes = Axes.X, - Height = 120, - Children = new Drawable[] + topLayerContainer = new Container { - new Container + Name = "Top layer", + RelativeSizeAxes = Axes.X, + Height = 120, + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both, - CornerRadius = 20, - CornerExponent = 2.5f, - Masking = true, - Child = topLayerBackground = new Box { RelativeSizeAxes = Axes.Both } - }, - topLayerContentContainer = new Container { RelativeSizeAxes = Axes.Both } - } - }, - middleLayerContainer = new Container - { - Name = "Middle layer", - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] + new Container + { + RelativeSizeAxes = Axes.Both, + CornerRadius = 20, + CornerExponent = 2.5f, + Masking = true, + Child = topLayerBackground = new Box { RelativeSizeAxes = Axes.Both } + }, + topLayerContentContainer = new Container { RelativeSizeAxes = Axes.Both } + } + }, + middleLayerContainer = new Container { - new Container + Name = "Middle layer", + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both, - CornerRadius = 20, - CornerExponent = 2.5f, - Masking = true, - Child = middleLayerBackground = new Box { RelativeSizeAxes = Axes.Both } - }, - middleLayerContentContainer = new Container { RelativeSizeAxes = Axes.Both } + new Container + { + RelativeSizeAxes = Axes.Both, + CornerRadius = 20, + CornerExponent = 2.5f, + Masking = true, + Child = middleLayerBackground = new Box { RelativeSizeAxes = Axes.Both } + }, + middleLayerContentContainer = new Container { RelativeSizeAxes = Axes.Both } + } } } }; @@ -181,7 +188,7 @@ namespace osu.Game.Screens.Ranking switch (state) { case PanelState.Expanded: - this.ResizeTo(new Vector2(EXPANDED_WIDTH, expanded_height), resize_duration, Easing.OutQuint); + Size = new Vector2(EXPANDED_WIDTH, expanded_height); topLayerBackground.FadeColour(expanded_top_layer_colour, resize_duration, Easing.OutQuint); middleLayerBackground.FadeColour(expanded_middle_layer_colour, resize_duration, Easing.OutQuint); @@ -191,7 +198,7 @@ namespace osu.Game.Screens.Ranking break; case PanelState.Contracted: - this.ResizeTo(new Vector2(CONTRACTED_WIDTH, contracted_height), resize_duration, Easing.OutQuint); + Size = new Vector2(CONTRACTED_WIDTH, contracted_height); topLayerBackground.FadeColour(contracted_top_layer_colour, resize_duration, Easing.OutQuint); middleLayerBackground.FadeColour(contracted_middle_layer_colour, resize_duration, Easing.OutQuint); @@ -200,6 +207,8 @@ namespace osu.Game.Screens.Ranking break; } + content.ResizeTo(Size, resize_duration, Easing.OutQuint); + bool topLayerExpanded = topLayerContainer.Y < 0; // If the top layer was already expanded, then we don't need to wait for the resize and can instead transform immediately. This looks better when changing the panel state. diff --git a/osu.Game/Screens/Ranking/ScorePanelList.cs b/osu.Game/Screens/Ranking/ScorePanelList.cs index c2fd487767..6dd21ec49d 100644 --- a/osu.Game/Screens/Ranking/ScorePanelList.cs +++ b/osu.Game/Screens/Ranking/ScorePanelList.cs @@ -24,7 +24,7 @@ namespace osu.Game.Screens.Ranking private const float expanded_panel_spacing = 15; private readonly Flow flow; - private readonly ScrollContainer scroll; + private readonly Scroll scroll; private ScorePanel expandedPanel; @@ -32,7 +32,7 @@ namespace osu.Game.Screens.Ranking { RelativeSizeAxes = Axes.Both; - InternalChild = scroll = new OsuScrollContainer(Direction.Horizontal) + InternalChild = scroll = new Scroll { RelativeSizeAxes = Axes.Both, Child = flow = new Flow @@ -46,9 +46,13 @@ namespace osu.Game.Screens.Ranking }; AddScore(initialScore); - ShowScore(initialScore); + presentScore(initialScore); } + /// + /// Adds a to this list. + /// + /// The to add. public void AddScore(ScoreInfo score) { flow.Add(new ScorePanel(score) @@ -60,24 +64,45 @@ namespace osu.Game.Screens.Ranking p.StateChanged += s => { if (s == PanelState.Expanded) - ShowScore(score); + presentScore(score); }; })); + + // We want the scroll position to remain relative to the expanded panel. When a new panel is added after the expanded panel, nothing needs to be done. + // But when a panel is added before the expanded panel, we need to offset the scroll position by the width of the new panel. + if (expandedPanel != null && flow.GetPanelIndex(score) < flow.GetPanelIndex(expandedPanel.Score)) + { + // A somewhat hacky property is used here because we need to: + // 1) Scroll after the scroll container's visible range is updated. + // 2) Scroll before the scroll container's scroll position is updated. + // Without this, we would have a 1-frame positioning error which looks very jarring. + scroll.InstantScrollTarget = (scroll.InstantScrollTarget ?? scroll.Target) + ScorePanel.CONTRACTED_WIDTH + panel_spacing; + } } - public void ShowScore(ScoreInfo score) + /// + /// Brings a to the centre of the screen and expands it. + /// + /// The to present. + private void presentScore(ScoreInfo score) { + // Contract the old panel. foreach (var p in flow.Where(p => p.Score != score)) + { p.State = PanelState.Contracted; + p.Margin = new MarginPadding(); + } - if (expandedPanel != null) - expandedPanel.Margin = new MarginPadding(0); - + // Expand the new panel. expandedPanel = flow.Single(p => p.Score == score); expandedPanel.State = PanelState.Expanded; expandedPanel.Margin = new MarginPadding { Horizontal = expanded_panel_spacing }; - float scrollOffset = flow.IndexOf(expandedPanel) * (ScorePanel.CONTRACTED_WIDTH + panel_spacing); + // Scroll to the new panel. This is done manually since we need: + // 1) To scroll after the scroll container's visible range is updated. + // 2) To account for the centre anchor/origins of panels. + // In the end, it's easier to compute the scroll position manually. + float scrollOffset = flow.GetPanelIndex(expandedPanel.Score) * (ScorePanel.CONTRACTED_WIDTH + panel_spacing); scroll.ScrollTo(scrollOffset); } @@ -85,12 +110,45 @@ namespace osu.Game.Screens.Ranking { base.Update(); - flow.Padding = new MarginPadding { Horizontal = DrawWidth / 2f - expandedPanel.DrawWidth / 2f - expanded_panel_spacing }; + // Add padding to both sides such that the centre of an expanded panel on either side is in the middle of the screen. + flow.Padding = new MarginPadding { Horizontal = DrawWidth / 2f - ScorePanel.EXPANDED_WIDTH / 2f - expanded_panel_spacing }; } private class Flow : FillFlowContainer { - public override IEnumerable FlowingChildren => AliveInternalChildren.OfType().OrderByDescending(s => s.Score.TotalScore).ThenByDescending(s => s.Score.OnlineScoreID); + public override IEnumerable FlowingChildren => applySorting(AliveInternalChildren); + + public int GetPanelIndex(ScoreInfo score) => applySorting(Children).OfType().TakeWhile(s => s.Score != score).Count(); + + private IEnumerable applySorting(IEnumerable drawables) => drawables.OfType() + .OrderByDescending(s => s.Score.TotalScore) + .ThenByDescending(s => s.Score.OnlineScoreID); + } + + private class Scroll : OsuScrollContainer + { + public new float Target => base.Target; + + public Scroll() + : base(Direction.Horizontal) + { + } + + /// + /// The target that will be scrolled to instantaneously next frame. + /// + public float? InstantScrollTarget; + + protected override void UpdateAfterChildren() + { + if (InstantScrollTarget != null) + { + ScrollTo(InstantScrollTarget.Value, false); + InstantScrollTarget = null; + } + + base.UpdateAfterChildren(); + } } } } From f5c80ac2d5c654794219db95fb5e2c8d48b5a7ce Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 21 May 2020 22:07:24 +0900 Subject: [PATCH 0849/1117] Remove vertical line --- .../Visual/Ranking/TestSceneScorePanelList.cs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs index 89aef377c8..b32b3afbda 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs @@ -4,13 +4,11 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; -using osu.Framework.Graphics.Shapes; using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Rulesets.Osu; using osu.Game.Scoring; using osu.Game.Screens.Ranking; -using osuTK.Graphics; namespace osu.Game.Tests.Visual.Ranking { @@ -27,15 +25,6 @@ namespace osu.Game.Tests.Visual.Ranking Anchor = Anchor.Centre, Origin = Anchor.Centre, }; - - Add(new Box - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - RelativeSizeAxes = Axes.Y, - Width = 1, - Colour = Color4.Red - }); }); [Test] From 899b9f8060928a5ed60984bc5eb073314ab63692 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 21 May 2020 22:26:04 +0900 Subject: [PATCH 0850/1117] Fix incorrect sorting order --- osu.Game/Screens/Ranking/ScorePanelList.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Ranking/ScorePanelList.cs b/osu.Game/Screens/Ranking/ScorePanelList.cs index 6dd21ec49d..ed6d07d078 100644 --- a/osu.Game/Screens/Ranking/ScorePanelList.cs +++ b/osu.Game/Screens/Ranking/ScorePanelList.cs @@ -122,7 +122,7 @@ namespace osu.Game.Screens.Ranking private IEnumerable applySorting(IEnumerable drawables) => drawables.OfType() .OrderByDescending(s => s.Score.TotalScore) - .ThenByDescending(s => s.Score.OnlineScoreID); + .ThenBy(s => s.Score.OnlineScoreID); } private class Scroll : OsuScrollContainer From 8702a1b5a57ba9a70657b244eaec33797727caf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 21 May 2020 20:10:51 +0200 Subject: [PATCH 0851/1117] Fix test scene regression --- .../Visual/Gameplay/TestSceneScrollingHitObjects.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneScrollingHitObjects.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneScrollingHitObjects.cs index 0d15e495e3..20b040dbc3 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneScrollingHitObjects.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneScrollingHitObjects.cs @@ -16,6 +16,7 @@ using osu.Game.Configuration; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Timing; using osu.Game.Rulesets.UI.Scrolling; using osuTK; @@ -221,7 +222,7 @@ namespace osu.Game.Tests.Visual.Gameplay private class TestDrawableControlPoint : DrawableHitObject { public TestDrawableControlPoint(ScrollingDirection direction, double time) - : base(new HitObject { StartTime = time }) + : base(new HitObject { StartTime = time, HitWindows = HitWindows.Empty }) { Origin = Anchor.Centre; @@ -252,7 +253,7 @@ namespace osu.Game.Tests.Visual.Gameplay private class TestDrawableHitObject : DrawableHitObject { public TestDrawableHitObject(double time) - : base(new HitObject { StartTime = time }) + : base(new HitObject { StartTime = time, HitWindows = HitWindows.Empty }) { Origin = Anchor.Custom; OriginPosition = new Vector2(75 / 4.0f); From 24d898c87031a62b21c98812e0ff2939392f7d5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 21 May 2020 21:35:12 +0200 Subject: [PATCH 0852/1117] Demonstrate failure case in visual test scene --- .../Gameplay/TestSceneScrollingHitObjects.cs | 37 +++++++++++++++++-- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneScrollingHitObjects.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneScrollingHitObjects.cs index 20b040dbc3..2f15e549f7 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneScrollingHitObjects.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneScrollingHitObjects.cs @@ -78,19 +78,18 @@ namespace osu.Game.Tests.Visual.Gameplay } }; - setUpHitObjects(); + hitObjectSpawnDelegate?.Cancel(); }); - private void setUpHitObjects() + private void setUpHitObjects() => AddStep("set up hit objects", () => { scrollContainers.ForEach(c => c.ControlPoints.Add(new MultiplierControlPoint(0))); for (int i = spawn_rate / 2; i <= time_range; i += spawn_rate) addHitObject(Time.Current + i); - hitObjectSpawnDelegate?.Cancel(); hitObjectSpawnDelegate = Scheduler.AddDelayed(() => addHitObject(Time.Current + time_range), spawn_rate, true); - } + }); private IList testControlPoints => new List { @@ -102,6 +101,8 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestScrollAlgorithms() { + setUpHitObjects(); + AddStep("constant scroll", () => setScrollAlgorithm(ScrollVisualisationMethod.Constant)); AddStep("overlapping scroll", () => setScrollAlgorithm(ScrollVisualisationMethod.Overlapping)); AddStep("sequential scroll", () => setScrollAlgorithm(ScrollVisualisationMethod.Sequential)); @@ -114,6 +115,8 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestConstantScrollLifetime() { + setUpHitObjects(); + AddStep("set constant scroll", () => setScrollAlgorithm(ScrollVisualisationMethod.Constant)); // scroll container time range must be less than the rate of spawning hitobjects // otherwise the hitobjects will spawn already partly visible on screen and look wrong @@ -123,14 +126,40 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestSequentialScrollLifetime() { + setUpHitObjects(); + AddStep("set sequential scroll", () => setScrollAlgorithm(ScrollVisualisationMethod.Sequential)); AddStep("set time range", () => scrollContainers.ForEach(c => c.TimeRange = time_range / 2.0)); AddStep("add control points", () => addControlPoints(testControlPoints, Time.Current)); } + [Test] + public void TestSlowSequentialScroll() + { + AddStep("set sequential scroll", () => setScrollAlgorithm(ScrollVisualisationMethod.Sequential)); + AddStep("set time range", () => scrollContainers.ForEach(c => c.TimeRange = time_range)); + AddStep("add control points", () => addControlPoints( + new List + { + new MultiplierControlPoint { Velocity = 0.1 } + }, + Time.Current + time_range)); + + // All of the hit objects added below should be immediately visible on screen + AddStep("add hit objects", () => + { + for (int i = 0; i < 20; ++i) + { + addHitObject(Time.Current + time_range * (2 + 0.1 * i)); + } + }); + } + [Test] public void TestOverlappingScrollLifetime() { + setUpHitObjects(); + AddStep("set overlapping scroll", () => setScrollAlgorithm(ScrollVisualisationMethod.Overlapping)); AddStep("set time range", () => scrollContainers.ForEach(c => c.TimeRange = time_range / 2.0)); AddStep("add control points", () => addControlPoints(testControlPoints, Time.Current)); From 4299bd05b4ca268e192a4f4d469f69bed4c6415b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 21 May 2020 21:39:15 +0200 Subject: [PATCH 0853/1117] Add test cases for sequential scroll algorithm --- .../ScrollAlgorithms/SequentialScrollTest.cs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/ScrollAlgorithms/SequentialScrollTest.cs b/osu.Game.Tests/ScrollAlgorithms/SequentialScrollTest.cs index 1f0c069f8d..bd578dcbc4 100644 --- a/osu.Game.Tests/ScrollAlgorithms/SequentialScrollTest.cs +++ b/osu.Game.Tests/ScrollAlgorithms/SequentialScrollTest.cs @@ -29,8 +29,22 @@ namespace osu.Game.Tests.ScrollAlgorithms [Test] public void TestDisplayStartTime() { - // Sequential scroll algorithm approximates the start time - // This should be fixed in the future + // easy cases - time range adjusted for velocity fits within control point duration + Assert.AreEqual(2500, algorithm.GetDisplayStartTime(5000, 0, 2500, 1)); // 5000 - (2500 / 1) + Assert.AreEqual(13750, algorithm.GetDisplayStartTime(15000, 0, 2500, 1)); // 15000 - (2500 / 2) + Assert.AreEqual(20000, algorithm.GetDisplayStartTime(25000, 0, 2500, 1)); // 25000 - (2500 / 0.5) + + // hard case - time range adjusted for velocity exceeds control point duration + + // 1st multiplier point takes 10000 / 2500 = 4 scroll lengths + // 2nd multiplier point takes 10000 / (2500 / 2) = 8 scroll lengths + // 3rd multiplier point takes 2500 / (2500 * 2) = 0.5 scroll lengths up to hitobject start + + // absolute position of the hitobject = 1000 * (4 + 8 + 0.5) = 12500 + // minus one scroll length allowance = 12500 - 1000 = 11500 = 11.5 [scroll lengths] + // therefore the start time lies within the second multiplier point (because 11.5 < 4 + 8) + // its exact time position is = 10000 + 7.5 * (2500 / 2) = 19375 + Assert.AreEqual(19375, algorithm.GetDisplayStartTime(22500, 0, 2500, 1000)); } [Test] From 6f388b731ee97aedebc375370b429539cf8946d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 21 May 2020 21:41:56 +0200 Subject: [PATCH 0854/1117] Fix display start time in sequential scroll algorithm --- .../UI/Scrolling/Algorithms/SequentialScrollAlgorithm.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/UI/Scrolling/Algorithms/SequentialScrollAlgorithm.cs b/osu.Game/Rulesets/UI/Scrolling/Algorithms/SequentialScrollAlgorithm.cs index 41f9ebdb82..0052c877f6 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Algorithms/SequentialScrollAlgorithm.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Algorithms/SequentialScrollAlgorithm.cs @@ -22,8 +22,7 @@ namespace osu.Game.Rulesets.UI.Scrolling.Algorithms public double GetDisplayStartTime(double originTime, float offset, double timeRange, float scrollLength) { - double adjustedTime = TimeAt(-offset, originTime, timeRange, scrollLength); - return adjustedTime - timeRange - 1000; + return TimeAt(-(scrollLength + offset), originTime, timeRange, scrollLength); } public float GetLength(double startTime, double endTime, double timeRange, float scrollLength) From 8a105bdbcfc554f4bd62217782d01632722a047a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 May 2020 11:19:57 +0900 Subject: [PATCH 0855/1117] Remove unused ColumnAt method --- osu.Game.Rulesets.Mania/Edit/IManiaHitObjectComposer.cs | 2 -- osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs | 9 +-------- osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs | 2 +- osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs | 8 -------- 4 files changed, 2 insertions(+), 19 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/IManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/IManiaHitObjectComposer.cs index 48e6b63064..5d9ad21cb7 100644 --- a/osu.Game.Rulesets.Mania/Edit/IManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/IManiaHitObjectComposer.cs @@ -8,8 +8,6 @@ namespace osu.Game.Rulesets.Mania.Edit { public interface IManiaHitObjectComposer { - Column ColumnAt(Vector2 screenSpacePosition); - ManiaPlayfield Playfield { get; } Vector2 ScreenSpacePositionAtTime(double time, Column column = null); diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index 4795bdd8e2..d4cfab840d 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -44,13 +44,6 @@ namespace osu.Game.Rulesets.Mania.Edit inputManager = GetContainingInputManager(); } - /// - /// Retrieves the column that intersects a screen-space position. - /// - /// The screen-space position. - /// The column which intersects with . - public Column ColumnAt(Vector2 screenSpacePosition) => drawableRuleset.GetColumnByPosition(screenSpacePosition); - private DependencyContainer dependencies; protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) @@ -85,7 +78,7 @@ namespace osu.Game.Rulesets.Mania.Edit public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) { - var column = ColumnAt(screenSpacePosition); + var column = Playfield.GetColumnByPosition(screenSpacePosition); if (column == null) return new SnapResult(screenSpacePosition, null); diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs index 83049ff959..4ea71652bc 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Mania.Edit private void performColumnMovement(int lastColumn, MoveSelectionEvent moveEvent) { - var currentColumn = composer.ColumnAt(moveEvent.ScreenSpacePosition); + var currentColumn = composer.Playfield.GetColumnByPosition(moveEvent.ScreenSpacePosition); if (currentColumn == null) return; diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs index f3f843f366..94b5ee9486 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs @@ -23,7 +23,6 @@ using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; -using osuTK; namespace osu.Game.Rulesets.Mania.UI { @@ -108,13 +107,6 @@ namespace osu.Game.Rulesets.Mania.UI private void updateTimeRange() => TimeRange.Value = configTimeRange.Value * speedAdjustmentTrack.AggregateTempo.Value * speedAdjustmentTrack.AggregateFrequency.Value; - /// - /// Retrieves the column that intersects a screen-space position. - /// - /// The screen-space position. - /// The column which intersects with . - public Column GetColumnByPosition(Vector2 screenSpacePosition) => Playfield.GetColumnByPosition(screenSpacePosition); - public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new ManiaPlayfieldAdjustmentContainer(); protected override Playfield CreatePlayfield() => new ManiaPlayfield(Beatmap.Stages); From 9a2889abc54d2c5217bb5adbbd591be7c8dc7d97 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 May 2020 11:32:35 +0900 Subject: [PATCH 0856/1117] Remove remaining left-over test implementations --- .../ManiaPlacementBlueprintTestScene.cs | 3 --- .../ManiaSelectionBlueprintTestScene.cs | 9 +-------- .../TestSceneManiaBeatSnapGrid.cs | 5 ----- 3 files changed, 1 insertion(+), 16 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestScene.cs b/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestScene.cs index 9a50802454..fd18907d96 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestScene.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestScene.cs @@ -15,7 +15,6 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Tests.Visual; -using osuTK; using osuTK.Graphics; namespace osu.Game.Rulesets.Mania.Tests @@ -57,7 +56,5 @@ namespace osu.Game.Rulesets.Mania.Tests protected override void AddHitObject(DrawableHitObject hitObject) => column.Add((DrawableManiaHitObject)hitObject); public ManiaPlayfield Playfield => null; - - public Vector2 ScreenSpacePositionAtTime(double time, Column column = null) => Vector2.Zero; } } diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaSelectionBlueprintTestScene.cs b/osu.Game.Rulesets.Mania.Tests/ManiaSelectionBlueprintTestScene.cs index f7dffbbc1a..35fe596e98 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaSelectionBlueprintTestScene.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaSelectionBlueprintTestScene.cs @@ -7,7 +7,6 @@ using osu.Framework.Timing; using osu.Game.Rulesets.Mania.Edit; using osu.Game.Rulesets.Mania.UI; using osu.Game.Tests.Visual; -using osuTK; using osuTK.Graphics; namespace osu.Game.Rulesets.Mania.Tests @@ -18,11 +17,9 @@ namespace osu.Game.Rulesets.Mania.Tests [Cached(Type = typeof(IAdjustableClock))] private readonly IAdjustableClock clock = new StopwatchClock(); - private readonly Column column; - protected ManiaSelectionBlueprintTestScene() { - Add(column = new Column(0) + Add(new Column(0) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -31,10 +28,6 @@ namespace osu.Game.Rulesets.Mania.Tests }); } - public Column ColumnAt(Vector2 screenSpacePosition) => column; - public ManiaPlayfield Playfield => null; - - public Vector2 ScreenSpacePositionAtTime(double time, Column column = null) => Vector2.Zero; } } diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaBeatSnapGrid.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaBeatSnapGrid.cs index feda3cfb81..ce9546415f 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaBeatSnapGrid.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaBeatSnapGrid.cs @@ -14,7 +14,6 @@ using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Screens.Edit; using osu.Game.Tests.Visual; -using osuTK; namespace osu.Game.Rulesets.Mania.Tests { @@ -66,10 +65,6 @@ namespace osu.Game.Rulesets.Mania.Tests return true; } - public Column ColumnAt(Vector2 screenSpacePosition) => null; - public ManiaPlayfield Playfield { get; } - - public Vector2 ScreenSpacePositionAtTime(double time, Column column = null) => Vector2.Zero; } } From f364d0e8328070f77232472b1db3dcee7d9f4bdf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 May 2020 11:35:26 +0900 Subject: [PATCH 0857/1117] Reduce IManiaHitObjectComposer scope --- .../ManiaPlacementBlueprintTestScene.cs | 5 +---- .../ManiaSelectionBlueprintTestScene.cs | 9 ++------- .../Edit/IManiaHitObjectComposer.cs | 5 +---- .../Edit/ManiaHitObjectComposer.cs | 11 +---------- osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs | 4 ++-- osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs | 8 -------- 6 files changed, 7 insertions(+), 35 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestScene.cs b/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestScene.cs index 547786847b..fd18907d96 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestScene.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestScene.cs @@ -15,7 +15,6 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Tests.Visual; -using osuTK; using osuTK.Graphics; namespace osu.Game.Rulesets.Mania.Tests @@ -56,8 +55,6 @@ namespace osu.Game.Rulesets.Mania.Tests protected override void AddHitObject(DrawableHitObject hitObject) => column.Add((DrawableManiaHitObject)hitObject); - public Column ColumnAt(Vector2 screenSpacePosition) => column; - - public int TotalColumns => 1; + public ManiaPlayfield Playfield => null; } } diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaSelectionBlueprintTestScene.cs b/osu.Game.Rulesets.Mania.Tests/ManiaSelectionBlueprintTestScene.cs index b598893e8c..35fe596e98 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaSelectionBlueprintTestScene.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaSelectionBlueprintTestScene.cs @@ -7,7 +7,6 @@ using osu.Framework.Timing; using osu.Game.Rulesets.Mania.Edit; using osu.Game.Rulesets.Mania.UI; using osu.Game.Tests.Visual; -using osuTK; using osuTK.Graphics; namespace osu.Game.Rulesets.Mania.Tests @@ -18,11 +17,9 @@ namespace osu.Game.Rulesets.Mania.Tests [Cached(Type = typeof(IAdjustableClock))] private readonly IAdjustableClock clock = new StopwatchClock(); - private readonly Column column; - protected ManiaSelectionBlueprintTestScene() { - Add(column = new Column(0) + Add(new Column(0) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -31,8 +28,6 @@ namespace osu.Game.Rulesets.Mania.Tests }); } - public Column ColumnAt(Vector2 screenSpacePosition) => column; - - public int TotalColumns => 1; + public ManiaPlayfield Playfield => null; } } diff --git a/osu.Game.Rulesets.Mania/Edit/IManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/IManiaHitObjectComposer.cs index f64bab1fae..3818d0e15d 100644 --- a/osu.Game.Rulesets.Mania/Edit/IManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/IManiaHitObjectComposer.cs @@ -2,14 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Mania.UI; -using osuTK; namespace osu.Game.Rulesets.Mania.Edit { public interface IManiaHitObjectComposer { - Column ColumnAt(Vector2 screenSpacePosition); - - int TotalColumns { get; } + ManiaPlayfield Playfield { get; } } } diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index cfb04c8e50..8367b4f5e9 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -26,13 +26,6 @@ namespace osu.Game.Rulesets.Mania.Edit { } - /// - /// Retrieves the column that intersects a screen-space position. - /// - /// The screen-space position. - /// The column which intersects with . - public Column ColumnAt(Vector2 screenSpacePosition) => drawableRuleset.GetColumnByPosition(screenSpacePosition); - private DependencyContainer dependencies; protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) @@ -42,11 +35,9 @@ namespace osu.Game.Rulesets.Mania.Edit public IScrollingInfo ScrollingInfo => drawableRuleset.ScrollingInfo; - public int TotalColumns => Playfield.TotalColumns; - public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) { - var column = ColumnAt(screenSpacePosition); + var column = Playfield.GetColumnByPosition(screenSpacePosition); if (column == null) return new SnapResult(screenSpacePosition, null); diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs index 55245198c8..4ea71652bc 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Mania.Edit private void performColumnMovement(int lastColumn, MoveSelectionEvent moveEvent) { - var currentColumn = composer.ColumnAt(moveEvent.ScreenSpacePosition); + var currentColumn = composer.Playfield.GetColumnByPosition(moveEvent.ScreenSpacePosition); if (currentColumn == null) return; @@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Mania.Edit maxColumn = obj.Column; } - columnDelta = Math.Clamp(columnDelta, -minColumn, composer.TotalColumns - 1 - maxColumn); + columnDelta = Math.Clamp(columnDelta, -minColumn, composer.Playfield.TotalColumns - 1 - maxColumn); foreach (var obj in SelectedHitObjects.OfType()) obj.Column += columnDelta; diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs index f3f843f366..94b5ee9486 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs @@ -23,7 +23,6 @@ using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; -using osuTK; namespace osu.Game.Rulesets.Mania.UI { @@ -108,13 +107,6 @@ namespace osu.Game.Rulesets.Mania.UI private void updateTimeRange() => TimeRange.Value = configTimeRange.Value * speedAdjustmentTrack.AggregateTempo.Value * speedAdjustmentTrack.AggregateFrequency.Value; - /// - /// Retrieves the column that intersects a screen-space position. - /// - /// The screen-space position. - /// The column which intersects with . - public Column GetColumnByPosition(Vector2 screenSpacePosition) => Playfield.GetColumnByPosition(screenSpacePosition); - public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new ManiaPlayfieldAdjustmentContainer(); protected override Playfield CreatePlayfield() => new ManiaPlayfield(Beatmap.Stages); From b2667bbb0210d00b9f6502362fc632f93bb9fa92 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 May 2020 11:45:58 +0900 Subject: [PATCH 0858/1117] Move protected implementation down --- .../Edit/ManiaHitObjectComposer.cs | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index 82a55b4965..683e921cbf 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -53,29 +53,6 @@ namespace osu.Game.Rulesets.Mania.Edit public IScrollingInfo ScrollingInfo => drawableRuleset.ScrollingInfo; - protected override void UpdateAfterChildren() - { - base.UpdateAfterChildren(); - - if (BlueprintContainer.CurrentTool is SelectTool) - { - if (EditorBeatmap.SelectedHitObjects.Any()) - { - beatSnapGrid.SelectionTimeRange = (EditorBeatmap.SelectedHitObjects.Min(h => h.StartTime), EditorBeatmap.SelectedHitObjects.Max(h => h.GetEndTime())); - } - else - beatSnapGrid.SelectionTimeRange = null; - } - else - { - var result = SnapScreenSpacePositionToValidTime(inputManager.CurrentState.Mouse.Position); - if (result.Time is double time) - beatSnapGrid.SelectionTimeRange = (time, time); - else - beatSnapGrid.SelectionTimeRange = null; - } - } - public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) { var column = Playfield.GetColumnByPosition(screenSpacePosition); @@ -111,5 +88,28 @@ namespace osu.Game.Rulesets.Mania.Edit new NoteCompositionTool(), new HoldNoteCompositionTool() }; + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + if (BlueprintContainer.CurrentTool is SelectTool) + { + if (EditorBeatmap.SelectedHitObjects.Any()) + { + beatSnapGrid.SelectionTimeRange = (EditorBeatmap.SelectedHitObjects.Min(h => h.StartTime), EditorBeatmap.SelectedHitObjects.Max(h => h.GetEndTime())); + } + else + beatSnapGrid.SelectionTimeRange = null; + } + else + { + var result = SnapScreenSpacePositionToValidTime(inputManager.CurrentState.Mouse.Position); + if (result.Time is double time) + beatSnapGrid.SelectionTimeRange = (time, time); + else + beatSnapGrid.SelectionTimeRange = null; + } + } } } From d529a2aefac633bde0bcdf32e215e0d89af7bff6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 May 2020 12:28:01 +0900 Subject: [PATCH 0859/1117] Remove left-over function --- .../Edit/ManiaBeatSnapGrid.cs | 32 ------------------- 1 file changed, 32 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs index fa8f8a755a..e52cd5774c 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs @@ -11,12 +11,10 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps; using osu.Game.Graphics; -using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Screens.Edit; -using osuTK; using osuTK.Graphics; namespace osu.Game.Rulesets.Mania.Edit @@ -171,36 +169,6 @@ namespace osu.Game.Rulesets.Mania.Edit } } - public (Vector2 position, double time)? GetSnappedPosition(Vector2 position) - { - float minDist = float.PositiveInfinity; - DrawableGridLine minDistLine = null; - - Vector2 minDistLinePosition = Vector2.Zero; - - foreach (var grid in grids) - { - foreach (var line in grid.Objects.OfType()) - { - Vector2 linePos = line.ToSpaceOfOtherDrawable(line.OriginPosition, this); - float d = Vector2.Distance(position, linePos); - - if (d < minDist) - { - minDist = d; - minDistLine = line; - minDistLinePosition = linePos; - } - } - } - - if (minDistLine == null) - return null; - - float noteOffset = (scrollingInfo.Direction.Value == ScrollingDirection.Up ? 1 : -1) * DefaultNotePiece.NOTE_HEIGHT / 2; - return (new Vector2(position.X, minDistLinePosition.Y + noteOffset), minDistLine.HitObject.StartTime); - } - private class DrawableGridLine : DrawableHitObject { [Resolved] From ce35d09e7dac2987f3cb439366eba75e0db0d7a1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 May 2020 12:45:37 +0900 Subject: [PATCH 0860/1117] Fix incorrect alpha application to lines on rewinding --- osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs | 8 ++++++-- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs index e52cd5774c..b5b6c08fca 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs @@ -199,10 +199,14 @@ namespace osu.Game.Rulesets.Mania.Edit : Anchor.BottomLeft; } + protected override void UpdateInitialTransforms() + { + // don't perform any fading – we are handling that ourselves. + } + protected override void UpdateStateTransforms(ArmedState state) { - using (BeginAbsoluteSequence(HitObject.StartTime + 1000)) - this.FadeOut(); + LifetimeEnd = HitObject.StartTime + visible_range; } } } diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index d594909cda..44afb7a227 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -257,7 +257,7 @@ namespace osu.Game.Rulesets.Objects.Drawables } } - if (state.Value != ArmedState.Idle && LifetimeEnd == double.MaxValue || HitObject.HitWindows == null) + if (LifetimeEnd == double.MaxValue && (state.Value != ArmedState.Idle || HitObject.HitWindows == null)) Expire(); // apply any custom state overrides From dd09d7830d108ff1de968035507f66199fc2ab57 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 May 2020 16:37:28 +0900 Subject: [PATCH 0861/1117] Cache and resolve editor clock as EditorClock in all cases --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 14 +++++-------- osu.Game/Rulesets/Edit/PlacementBlueprint.cs | 11 +++++----- .../Edit/Components/PlaybackControl.cs | 11 +++++----- .../Edit/Components/TimeInfoContainer.cs | 5 ++--- .../Timelines/Summary/Parts/MarkerPart.cs | 15 +++++++------ .../Timelines/Summary/SummaryTimeline.cs | 5 ++--- .../Compose/Components/BlueprintContainer.cs | 5 ++--- .../Compose/Components/Timeline/Timeline.cs | 21 +++++++++---------- osu.Game/Screens/Edit/Editor.cs | 3 +-- osu.Game/Screens/Edit/Timing/TimingScreen.cs | 2 +- osu.Game/Tests/Visual/EditorClockTestScene.cs | 3 +-- 11 files changed, 41 insertions(+), 54 deletions(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index fd8af0afd5..1987148aed 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -11,7 +11,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Input; using osu.Framework.Input.Events; using osu.Framework.Logging; -using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Configuration; @@ -38,14 +37,11 @@ namespace osu.Game.Rulesets.Edit protected readonly Ruleset Ruleset; [Resolved] - protected IFrameBasedClock EditorClock { get; private set; } + protected EditorClock EditorClock { get; private set; } [Resolved] protected EditorBeatmap EditorBeatmap { get; private set; } - [Resolved] - private IAdjustableClock adjustableClock { get; set; } - [Resolved] protected IBeatSnapProvider BeatSnapProvider { get; private set; } @@ -68,7 +64,7 @@ namespace osu.Game.Rulesets.Edit } [BackgroundDependencyLoader] - private void load(IFrameBasedClock framedClock) + private void load() { Config = Dependencies.Get().GetConfigFor(Ruleset); @@ -76,7 +72,7 @@ namespace osu.Game.Rulesets.Edit { drawableRulesetWrapper = new DrawableEditRulesetWrapper(CreateDrawableRuleset(Ruleset, EditorBeatmap.PlayableBeatmap)) { - Clock = framedClock, + Clock = EditorClock, ProcessCustomClock = false }; } @@ -221,8 +217,8 @@ namespace osu.Game.Rulesets.Edit { EditorBeatmap.Add(hitObject); - if (adjustableClock.CurrentTime < hitObject.StartTime) - adjustableClock.Seek(hitObject.StartTime); + if (EditorClock.CurrentTime < hitObject.StartTime) + EditorClock.Seek(hitObject.StartTime); } } diff --git a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs index f0b63f8ea5..20584c66e5 100644 --- a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs +++ b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs @@ -6,10 +6,10 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; -using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Objects; +using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Compose; using osuTK; @@ -30,7 +30,8 @@ namespace osu.Game.Rulesets.Edit /// protected readonly HitObject HitObject; - protected IClock EditorClock { get; private set; } + [Resolved] + protected EditorClock EditorClock { get; private set; } private readonly IBindable beatmap = new Bindable(); @@ -49,12 +50,10 @@ namespace osu.Game.Rulesets.Edit } [BackgroundDependencyLoader] - private void load(IBindable beatmap, IAdjustableClock clock) + private void load(IBindable beatmap) { this.beatmap.BindTo(beatmap); - EditorClock = clock; - ApplyDefaultsToHitObject(); } @@ -84,7 +83,7 @@ namespace osu.Game.Rulesets.Edit } [Resolved(canBeNull: true)] - private IFrameBasedClock editorClock { get; set; } + private EditorClock editorClock { get; set; } /// /// Updates the position of this to a new screen-space position. diff --git a/osu.Game/Screens/Edit/Components/PlaybackControl.cs b/osu.Game/Screens/Edit/Components/PlaybackControl.cs index 897c6ec531..59b3d1c565 100644 --- a/osu.Game/Screens/Edit/Components/PlaybackControl.cs +++ b/osu.Game/Screens/Edit/Components/PlaybackControl.cs @@ -13,7 +13,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; -using osu.Framework.Timing; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -26,7 +25,7 @@ namespace osu.Game.Screens.Edit.Components private IconButton playButton; [Resolved] - private IAdjustableClock adjustableClock { get; set; } + private EditorClock editorClock { get; set; } private readonly BindableNumber tempo = new BindableDouble(1); @@ -87,17 +86,17 @@ namespace osu.Game.Screens.Edit.Components private void togglePause() { - if (adjustableClock.IsRunning) - adjustableClock.Stop(); + if (editorClock.IsRunning) + editorClock.Stop(); else - adjustableClock.Start(); + editorClock.Start(); } protected override void Update() { base.Update(); - playButton.Icon = adjustableClock.IsRunning ? FontAwesome.Regular.PauseCircle : FontAwesome.Regular.PlayCircle; + playButton.Icon = editorClock.IsRunning ? FontAwesome.Regular.PauseCircle : FontAwesome.Regular.PlayCircle; } private class PlaybackTabControl : OsuTabControl diff --git a/osu.Game/Screens/Edit/Components/TimeInfoContainer.cs b/osu.Game/Screens/Edit/Components/TimeInfoContainer.cs index 4bf21d240a..c1f54d7938 100644 --- a/osu.Game/Screens/Edit/Components/TimeInfoContainer.cs +++ b/osu.Game/Screens/Edit/Components/TimeInfoContainer.cs @@ -5,7 +5,6 @@ using osu.Framework.Graphics; using osu.Game.Graphics.Sprites; using System; using osu.Framework.Allocation; -using osu.Framework.Timing; using osu.Game.Graphics; namespace osu.Game.Screens.Edit.Components @@ -15,7 +14,7 @@ namespace osu.Game.Screens.Edit.Components private readonly OsuSpriteText trackTimer; [Resolved] - private IAdjustableClock adjustableClock { get; set; } + private EditorClock editorClock { get; set; } public TimeInfoContainer() { @@ -35,7 +34,7 @@ namespace osu.Game.Screens.Edit.Components { base.Update(); - trackTimer.Text = TimeSpan.FromMilliseconds(adjustableClock.CurrentTime).ToString(@"mm\:ss\:fff"); + trackTimer.Text = TimeSpan.FromMilliseconds(editorClock.CurrentTime).ToString(@"mm\:ss\:fff"); } } } diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs index 5d638d7919..82581dfc56 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs @@ -9,7 +9,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Framework.Threading; -using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Graphics; @@ -20,14 +19,14 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts /// public class MarkerPart : TimelinePart { - private readonly Drawable marker; + private Drawable marker; - private readonly IAdjustableClock adjustableClock; + [Resolved] + private EditorClock editorClock { get; set; } - public MarkerPart(IAdjustableClock adjustableClock) + [BackgroundDependencyLoader] + private void load() { - this.adjustableClock = adjustableClock; - Add(marker = new MarkerVisualisation()); } @@ -59,14 +58,14 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts return; float markerPos = Math.Clamp(ToLocalSpace(screenPosition).X, 0, DrawWidth); - adjustableClock.Seek(markerPos / DrawWidth * Beatmap.Value.Track.Length); + editorClock.Seek(markerPos / DrawWidth * editorClock.TrackLength); }); } protected override void Update() { base.Update(); - marker.X = (float)adjustableClock.CurrentTime; + marker.X = (float)editorClock.CurrentTime; } protected override void LoadBeatmap(WorkingBeatmap beatmap) diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs index 20db2cac21..02cd4bccb4 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs @@ -6,7 +6,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Timing; using osu.Game.Graphics; using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts; @@ -18,11 +17,11 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary public class SummaryTimeline : BottomBarContainer { [BackgroundDependencyLoader] - private void load(OsuColour colours, IAdjustableClock adjustableClock) + private void load(OsuColour colours) { Children = new Drawable[] { - new MarkerPart(adjustableClock) { RelativeSizeAxes = Axes.Both }, + new MarkerPart { RelativeSizeAxes = Axes.Both }, new ControlPointPart { Anchor = Anchor.Centre, diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 1e8a35c047..fba7671fca 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -13,7 +13,6 @@ using osu.Framework.Graphics.Primitives; using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; -using osu.Framework.Timing; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; @@ -38,7 +37,7 @@ namespace osu.Game.Screens.Edit.Compose.Components private IEditorChangeHandler changeHandler { get; set; } [Resolved] - private IAdjustableClock adjustableClock { get; set; } + private EditorClock editorClock { get; set; } [Resolved] private EditorBeatmap beatmap { get; set; } @@ -144,7 +143,7 @@ namespace osu.Game.Screens.Edit.Compose.Components if (clickedBlueprint == null) return false; - adjustableClock?.Seek(clickedBlueprint.HitObject.StartTime); + editorClock?.Seek(clickedBlueprint.HitObject.StartTime); return true; } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index ec2b11c0cf..121f3dc213 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -9,7 +9,6 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Audio; using osu.Framework.Input.Events; -using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Rulesets.Edit; @@ -25,7 +24,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline public readonly IBindable Beatmap = new Bindable(); [Resolved] - private IAdjustableClock adjustableClock { get; set; } + private EditorClock editorClock { get; set; } public Timeline() { @@ -101,7 +100,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline Content.Margin = new MarginPadding { Horizontal = DrawWidth / 2 }; // This needs to happen after transforms are updated, but before the scroll position is updated in base.UpdateAfterChildren - if (adjustableClock.IsRunning) + if (editorClock.IsRunning) scrollToTrackTime(); } @@ -111,21 +110,21 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline if (handlingDragInput) seekTrackToCurrent(); - else if (!adjustableClock.IsRunning) + else if (!editorClock.IsRunning) { // The track isn't running. There are two cases we have to be wary of: // 1) The user flick-drags on this timeline: We want the track to follow us // 2) The user changes the track time through some other means (scrolling in the editor or overview timeline): We want to follow the track time // The simplest way to cover both cases is by checking whether the scroll position has changed and the audio hasn't been changed externally - if (Current != lastScrollPosition && adjustableClock.CurrentTime == lastTrackTime) + if (Current != lastScrollPosition && editorClock.CurrentTime == lastTrackTime) seekTrackToCurrent(); else scrollToTrackTime(); } lastScrollPosition = Current; - lastTrackTime = adjustableClock.CurrentTime; + lastTrackTime = editorClock.CurrentTime; } private void seekTrackToCurrent() @@ -133,7 +132,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline if (!track.IsLoaded) return; - adjustableClock.Seek(Current / Content.DrawWidth * track.Length); + editorClock.Seek(Current / Content.DrawWidth * track.Length); } private void scrollToTrackTime() @@ -141,7 +140,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline if (!track.IsLoaded || track.Length == 0) return; - ScrollTo((float)(adjustableClock.CurrentTime / track.Length) * Content.DrawWidth, false); + ScrollTo((float)(editorClock.CurrentTime / track.Length) * Content.DrawWidth, false); } protected override bool OnMouseDown(MouseDownEvent e) @@ -164,15 +163,15 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private void beginUserDrag() { handlingDragInput = true; - trackWasPlaying = adjustableClock.IsRunning; - adjustableClock.Stop(); + trackWasPlaying = editorClock.IsRunning; + editorClock.Stop(); } private void endUserDrag() { handlingDragInput = false; if (trackWasPlaying) - adjustableClock.Start(); + editorClock.Start(); } [Resolved] diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 54e4af94a4..54c5a23c3e 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -83,8 +83,7 @@ namespace osu.Game.Screens.Edit clock = new EditorClock(Beatmap.Value, beatDivisor) { IsCoupled = false }; clock.ChangeSource(sourceClock); - dependencies.CacheAs(clock); - dependencies.CacheAs(clock); + dependencies.CacheAs(clock); // todo: remove caching of this and consume via editorBeatmap? dependencies.Cache(beatDivisor); diff --git a/osu.Game/Screens/Edit/Timing/TimingScreen.cs b/osu.Game/Screens/Edit/Timing/TimingScreen.cs index d9da3ff92d..f3d1ec2cbb 100644 --- a/osu.Game/Screens/Edit/Timing/TimingScreen.cs +++ b/osu.Game/Screens/Edit/Timing/TimingScreen.cs @@ -23,7 +23,7 @@ namespace osu.Game.Screens.Edit.Timing private Bindable selectedGroup = new Bindable(); [Resolved] - private IAdjustableClock clock { get; set; } + private EditorClock clock { get; set; } protected override Drawable CreateMainContent() => new GridContainer { diff --git a/osu.Game/Tests/Visual/EditorClockTestScene.cs b/osu.Game/Tests/Visual/EditorClockTestScene.cs index 830e6ed363..f0ec638fc9 100644 --- a/osu.Game/Tests/Visual/EditorClockTestScene.cs +++ b/osu.Game/Tests/Visual/EditorClockTestScene.cs @@ -30,8 +30,7 @@ namespace osu.Game.Tests.Visual var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); dependencies.Cache(BeatDivisor); - dependencies.CacheAs(Clock); - dependencies.CacheAs(Clock); + dependencies.CacheAs(Clock); return dependencies; } From d18eb663b157d63a2c46816d86f5c3194604bc40 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 May 2020 16:40:52 +0900 Subject: [PATCH 0862/1117] Add tweening seek support to EditorClock --- .../Edit/OsuHitObjectComposer.cs | 3 +- .../Visual/Editing/TimelineTestScene.cs | 13 +- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 2 +- .../Timelines/Summary/Parts/MarkerPart.cs | 2 +- .../Compose/Components/BlueprintContainer.cs | 2 +- osu.Game/Screens/Edit/Editor.cs | 1 + osu.Game/Screens/Edit/EditorClock.cs | 129 ++++++++++++++++-- osu.Game/Screens/Edit/Timing/TimingScreen.cs | 2 +- 8 files changed, 129 insertions(+), 25 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 9ba3e30445..df14b11e8a 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -104,7 +104,8 @@ namespace osu.Game.Rulesets.Osu.Edit var objects = selectedHitObjects.ToList(); if (objects.Count == 0) - return createGrid(h => h.StartTime <= EditorClock.CurrentTime); + // use accurate time value to give more instantaneous feedback to the user. + return createGrid(h => h.StartTime <= EditorClock.CurrentTimeAccurate); double minTime = objects.Min(h => h.StartTime); return createGrid(h => h.StartTime < minTime, objects.Count + 1); diff --git a/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs b/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs index 01ef7e6170..3652d41e67 100644 --- a/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs +++ b/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs @@ -7,7 +7,6 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Edit; @@ -69,7 +68,7 @@ namespace osu.Game.Tests.Visual.Editing private IBindable beatmap { get; set; } [Resolved] - private IAdjustableClock adjustableClock { get; set; } + private EditorClock adjustableClock { get; set; } public AudioVisualiser() { @@ -102,7 +101,7 @@ namespace osu.Game.Tests.Visual.Editing private class StartStopButton : OsuButton { - private IAdjustableClock adjustableClock; + private EditorClock editorClock; private bool started; public StartStopButton() @@ -115,21 +114,21 @@ namespace osu.Game.Tests.Visual.Editing } [BackgroundDependencyLoader] - private void load(IAdjustableClock adjustableClock) + private void load(EditorClock editorClock) { - this.adjustableClock = adjustableClock; + this.editorClock = editorClock; } private void onClick() { if (started) { - adjustableClock.Stop(); + editorClock.Stop(); Text = "Start"; } else { - adjustableClock.Start(); + editorClock.Start(); Text = "Stop"; } diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 1987148aed..edbdd41d81 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -218,7 +218,7 @@ namespace osu.Game.Rulesets.Edit EditorBeatmap.Add(hitObject); if (EditorClock.CurrentTime < hitObject.StartTime) - EditorClock.Seek(hitObject.StartTime); + EditorClock.SeekTo(hitObject.StartTime); } } diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs index 82581dfc56..9e9ac93d23 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs @@ -58,7 +58,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts return; float markerPos = Math.Clamp(ToLocalSpace(screenPosition).X, 0, DrawWidth); - editorClock.Seek(markerPos / DrawWidth * editorClock.TrackLength); + editorClock.SeekTo(markerPos / DrawWidth * editorClock.TrackLength); }); } diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index fba7671fca..cc417bbb10 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -143,7 +143,7 @@ namespace osu.Game.Screens.Edit.Compose.Components if (clickedBlueprint == null) return false; - editorClock?.Seek(clickedBlueprint.HitObject.StartTime); + editorClock?.SeekTo(clickedBlueprint.HitObject.StartTime); return true; } diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 54c5a23c3e..9f61589c36 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -84,6 +84,7 @@ namespace osu.Game.Screens.Edit clock.ChangeSource(sourceClock); dependencies.CacheAs(clock); + AddInternal(clock); // todo: remove caching of this and consume via editorBeatmap? dependencies.Cache(beatDivisor); diff --git a/osu.Game/Screens/Edit/EditorClock.cs b/osu.Game/Screens/Edit/EditorClock.cs index e5e47507f3..321a25170a 100644 --- a/osu.Game/Screens/Edit/EditorClock.cs +++ b/osu.Game/Screens/Edit/EditorClock.cs @@ -3,6 +3,8 @@ using System; using System.Linq; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Transforms; using osu.Framework.Utils; using osu.Framework.Timing; using osu.Game.Beatmaps; @@ -13,7 +15,7 @@ namespace osu.Game.Screens.Edit /// /// A decoupled clock which adds editor-specific functionality, such as snapping to a user-defined beat divisor. /// - public class EditorClock : DecoupleableInterpolatingFramedClock + public class EditorClock : Component, IFrameBasedClock, IAdjustableClock, ISourceChangeableClock { public readonly double TrackLength; @@ -21,12 +23,11 @@ namespace osu.Game.Screens.Edit private readonly BindableBeatDivisor beatDivisor; - public EditorClock(WorkingBeatmap beatmap, BindableBeatDivisor beatDivisor) - { - this.beatDivisor = beatDivisor; + private readonly DecoupleableInterpolatingFramedClock underlyingClock; - ControlPointInfo = beatmap.Beatmap.ControlPointInfo; - TrackLength = beatmap.Track.Length; + public EditorClock(WorkingBeatmap beatmap, BindableBeatDivisor beatDivisor) + : this(beatmap.Beatmap.ControlPointInfo, beatmap.Track.Length, beatDivisor) + { } public EditorClock(ControlPointInfo controlPointInfo, double trackLength, BindableBeatDivisor beatDivisor) @@ -35,6 +36,8 @@ namespace osu.Game.Screens.Edit ControlPointInfo = controlPointInfo; TrackLength = trackLength; + + underlyingClock = new DecoupleableInterpolatingFramedClock(); } /// @@ -79,20 +82,30 @@ namespace osu.Game.Screens.Edit private void seek(int direction, bool snapped, double amount = 1) { + double current = CurrentTime; + + // if a seek transform is active, use its end time instead of the reported current time. + var existingTransform = Transforms.OfType().FirstOrDefault(); + + // but only if the requested direction is in the same direction as the transform. + // this allows quick pivoting rather than resetting the transform for the first opposite direction movement. + if (existingTransform != null && Math.Sign(existingTransform.EndValue - current) == Math.Sign(direction)) + current = existingTransform.EndValue; + if (amount <= 0) throw new ArgumentException("Value should be greater than zero", nameof(amount)); - var timingPoint = ControlPointInfo.TimingPointAt(CurrentTime); + var timingPoint = ControlPointInfo.TimingPointAt(current); - if (direction < 0 && timingPoint.Time == CurrentTime) + if (direction < 0 && timingPoint.Time == current) // When going backwards and we're at the boundary of two timing points, we compute the seek distance with the timing point which we are seeking into - timingPoint = ControlPointInfo.TimingPointAt(CurrentTime - 1); + timingPoint = ControlPointInfo.TimingPointAt(current - 1); double seekAmount = timingPoint.BeatLength / beatDivisor.Value * amount; - double seekTime = CurrentTime + seekAmount * direction; + double seekTime = current + seekAmount * direction; if (!snapped || ControlPointInfo.TimingPoints.Count == 0) { - Seek(seekTime); + SeekTo(seekTime); return; } @@ -110,7 +123,7 @@ namespace osu.Game.Screens.Edit // Due to the rounding above, we may end up on the current beat. This will effectively cause 0 seeking to happen, but we don't want this. // Instead, we'll go to the next beat in the direction when this is the case - if (Precision.AlmostEquals(CurrentTime, seekTime)) + if (Precision.AlmostEquals(current, seekTime)) { closestBeat += direction > 0 ? 1 : -1; seekTime = timingPoint.Time + closestBeat * seekAmount; @@ -125,7 +138,97 @@ namespace osu.Game.Screens.Edit // Ensure the sought point is within the boundaries seekTime = Math.Clamp(seekTime, 0, TrackLength); - Seek(seekTime); + SeekTo(seekTime); + } + + /// + /// The current time of this clock, include any active transform seeks performed via . + /// + public double CurrentTimeAccurate => + Transforms.OfType().FirstOrDefault()?.EndValue ?? CurrentTime; + + public double CurrentTime => underlyingClock.CurrentTime; + + public void Reset() + { + ClearTransforms(); + underlyingClock.Reset(); + } + + public void Start() + { + ClearTransforms(); + underlyingClock.Start(); + } + + public void Stop() + { + underlyingClock.Stop(); + } + + public bool Seek(double position) + { + ClearTransforms(); + return underlyingClock.Seek(position); + } + + public void ResetSpeedAdjustments() => underlyingClock.ResetSpeedAdjustments(); + + double IAdjustableClock.Rate + { + get => underlyingClock.Rate; + set => underlyingClock.Rate = value; + } + + double IClock.Rate => underlyingClock.Rate; + + public bool IsRunning => underlyingClock.IsRunning; + + public void ProcessFrame() => underlyingClock.ProcessFrame(); + + public double ElapsedFrameTime => underlyingClock.ElapsedFrameTime; + + public double FramesPerSecond => underlyingClock.FramesPerSecond; + + public FrameTimeInfo TimeInfo => underlyingClock.TimeInfo; + + public void ChangeSource(IClock source) => underlyingClock.ChangeSource(source); + + public IClock Source => underlyingClock.Source; + + public bool IsCoupled + { + get => underlyingClock.IsCoupled; + set => underlyingClock.IsCoupled = value; + } + + private const double transform_time = 300; + + public void SeekTo(double seekDestination) + { + if (IsRunning) + Seek(seekDestination); + else + transformSeekTo(seekDestination, transform_time, Easing.OutQuint); + } + + private void transformSeekTo(double seek, double duration = 0, Easing easing = Easing.None) + => this.TransformTo(this.PopulateTransform(new TransformSeek(), seek, duration, easing)); + + private double currentTime + { + get => underlyingClock.CurrentTime; + set => underlyingClock.Seek(value); + } + + private class TransformSeek : Transform + { + public override string TargetMember => nameof(currentTime); + + protected override void Apply(EditorClock clock, double time) => + clock.currentTime = Interpolation.ValueAt(time, StartValue, EndValue, StartTime, EndTime, Easing); + + protected override void ReadIntoStartValue(EditorClock clock) => StartValue = clock.currentTime; } } } diff --git a/osu.Game/Screens/Edit/Timing/TimingScreen.cs b/osu.Game/Screens/Edit/Timing/TimingScreen.cs index f3d1ec2cbb..59af88a049 100644 --- a/osu.Game/Screens/Edit/Timing/TimingScreen.cs +++ b/osu.Game/Screens/Edit/Timing/TimingScreen.cs @@ -50,7 +50,7 @@ namespace osu.Game.Screens.Edit.Timing selectedGroup.BindValueChanged(selected => { if (selected.NewValue != null) - clock.Seek(selected.NewValue.Time); + clock.SeekTo(selected.NewValue.Time); }); } From 866db629d6ce60b534e9082652c37c888c3cb42b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 May 2020 18:23:24 +0900 Subject: [PATCH 0863/1117] Fix remaining test failures --- .../Visual/Editing/TestScenePlaybackControl.cs | 7 +++---- osu.Game.Tests/Visual/Editing/TimelineTestScene.cs | 14 +++++--------- osu.Game/Screens/Edit/EditorClock.cs | 5 +++++ .../Tests/Visual/PlacementBlueprintTestScene.cs | 3 ++- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestScenePlaybackControl.cs b/osu.Game.Tests/Visual/Editing/TestScenePlaybackControl.cs index 3af976cae0..6aa884a197 100644 --- a/osu.Game.Tests/Visual/Editing/TestScenePlaybackControl.cs +++ b/osu.Game.Tests/Visual/Editing/TestScenePlaybackControl.cs @@ -4,8 +4,8 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Timing; using osu.Game.Beatmaps; +using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Components; using osuTK; @@ -17,9 +17,8 @@ namespace osu.Game.Tests.Visual.Editing [BackgroundDependencyLoader] private void load() { - var clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; - Dependencies.CacheAs(clock); - Dependencies.CacheAs(clock); + var clock = new EditorClock { IsCoupled = false }; + Dependencies.CacheAs(clock); var playback = new PlaybackControl { diff --git a/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs b/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs index 01ef7e6170..2e7ccc8ad3 100644 --- a/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs +++ b/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs @@ -102,7 +102,9 @@ namespace osu.Game.Tests.Visual.Editing private class StartStopButton : OsuButton { - private IAdjustableClock adjustableClock; + [Resolved] + private EditorClock editorClock { get; set; } + private bool started; public StartStopButton() @@ -114,22 +116,16 @@ namespace osu.Game.Tests.Visual.Editing Action = onClick; } - [BackgroundDependencyLoader] - private void load(IAdjustableClock adjustableClock) - { - this.adjustableClock = adjustableClock; - } - private void onClick() { if (started) { - adjustableClock.Stop(); + editorClock.Stop(); Text = "Start"; } else { - adjustableClock.Start(); + editorClock.Start(); Text = "Stop"; } diff --git a/osu.Game/Screens/Edit/EditorClock.cs b/osu.Game/Screens/Edit/EditorClock.cs index e5e47507f3..d2bb1c8984 100644 --- a/osu.Game/Screens/Edit/EditorClock.cs +++ b/osu.Game/Screens/Edit/EditorClock.cs @@ -37,6 +37,11 @@ namespace osu.Game.Screens.Edit TrackLength = trackLength; } + public EditorClock() + : this(new ControlPointInfo(), 1000, new BindableBeatDivisor()) + { + } + /// /// Seek to the closest snappable beat from a time. /// diff --git a/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs b/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs index feecea473c..c3d74f21aa 100644 --- a/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs +++ b/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs @@ -8,6 +8,7 @@ using osu.Framework.Timing; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Compose; namespace osu.Game.Tests.Visual @@ -32,7 +33,7 @@ namespace osu.Game.Tests.Visual protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); - dependencies.CacheAs(new StopwatchClock()); + dependencies.CacheAs(new EditorClock()); return dependencies; } From 3e0ee310d0e0d7dd529c1e7c8a3e59f4a4e8ec7e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 May 2020 18:30:39 +0900 Subject: [PATCH 0864/1117] Remove now incorrect comment --- osu.Game/Rulesets/Edit/PlacementBlueprint.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs index f0b63f8ea5..e71ccc33a4 100644 --- a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs +++ b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs @@ -64,9 +64,7 @@ namespace osu.Game.Rulesets.Edit /// Whether this call is committing a value for HitObject.StartTime and continuing with further adjustments. protected void BeginPlacement(bool commitStart = false) { - // applies snapping to above time placementHandler.BeginPlacement(HitObject); - PlacementActive |= commitStart; } From af30d1201f0934fc49ee4857e0157611bdc4114c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 May 2020 18:57:28 +0900 Subject: [PATCH 0865/1117] Fix slider path control point blueprint not working correctly --- .../Blueprints/Sliders/Components/PathControlPointPiece.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 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 834bf1892f..c06904c0c2 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -162,8 +162,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components if (ControlPoint == slider.Path.ControlPoints[0]) { // Special handling for the head control point - the position of the slider changes which means the snapped position and time have to be taken into account - var result = snapProvider?.SnapScreenSpacePositionToValidTime(e.MousePosition); - Vector2 movementDelta = (result?.ScreenSpacePosition ?? e.MousePosition) - slider.Position; + var result = snapProvider?.SnapScreenSpacePositionToValidTime(e.ScreenSpaceMousePosition); + + Vector2 movementDelta = Parent.ToLocalSpace(result?.ScreenSpacePosition ?? e.ScreenSpaceMousePosition) - slider.Position; slider.Position += movementDelta; slider.StartTime = result?.Time ?? slider.StartTime; From 5ea33f4c046ffdde090f21c67a9a26772083a24b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 May 2020 19:23:07 +0900 Subject: [PATCH 0866/1117] Fix incorrect rounding in DragBar --- osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs | 4 ++-- .../Compose/Components/Timeline/TimelineHitObjectBlueprint.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index ec2b11c0cf..61ed1743a9 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -181,8 +181,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline [Resolved] private IBeatSnapProvider beatSnapProvider { get; set; } - public SnapResult SnapScreenSpacePositionToValidTime(Vector2 position) => - new SnapResult(position, beatSnapProvider.SnapTime(getTimeFromPosition(Content.ToLocalSpace(position)))); + public SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) => + new SnapResult(screenSpacePosition, beatSnapProvider.SnapTime(getTimeFromPosition(Content.ToLocalSpace(screenSpacePosition)))); private double getTimeFromPosition(Vector2 localPosition) => (localPosition.X / Content.DrawWidth) * track.Length; diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index 03e05b75c5..dd2f7a833e 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -282,7 +282,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline case IHasRepeats repeatHitObject: // find the number of repeats which can fit in the requested time. var lengthOfOneRepeat = repeatHitObject.Duration / (repeatHitObject.RepeatCount + 1); - var proposedCount = Math.Max(0, (int)((time - hitObject.StartTime) / lengthOfOneRepeat) - 1); + var proposedCount = Math.Max(0, (int)Math.Round((time - hitObject.StartTime) / lengthOfOneRepeat) - 1); if (proposedCount == repeatHitObject.RepeatCount) return; From 8b79e142257652c1914d5381f8942f4cf20c1ae6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 May 2020 19:49:49 +0900 Subject: [PATCH 0867/1117] Fix remaining test regressions --- osu.Game.Tests/Visual/Editing/TimelineTestScene.cs | 5 ++--- osu.Game/Screens/Edit/Timing/TimingScreen.cs | 3 +-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs b/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs index 2e7ccc8ad3..fdb8781563 100644 --- a/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs +++ b/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs @@ -7,7 +7,6 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Edit; @@ -69,7 +68,7 @@ namespace osu.Game.Tests.Visual.Editing private IBindable beatmap { get; set; } [Resolved] - private IAdjustableClock adjustableClock { get; set; } + private EditorClock editorClock { get; set; } public AudioVisualiser() { @@ -96,7 +95,7 @@ namespace osu.Game.Tests.Visual.Editing base.Update(); if (beatmap.Value.Track.IsLoaded) - marker.X = (float)(adjustableClock.CurrentTime / beatmap.Value.Track.Length); + marker.X = (float)(editorClock.CurrentTime / beatmap.Value.Track.Length); } } diff --git a/osu.Game/Screens/Edit/Timing/TimingScreen.cs b/osu.Game/Screens/Edit/Timing/TimingScreen.cs index f3d1ec2cbb..f22d7291d9 100644 --- a/osu.Game/Screens/Edit/Timing/TimingScreen.cs +++ b/osu.Game/Screens/Edit/Timing/TimingScreen.cs @@ -7,7 +7,6 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; @@ -62,7 +61,7 @@ namespace osu.Game.Screens.Edit.Timing private IBindableList controlGroups; [Resolved] - private IFrameBasedClock clock { get; set; } + private EditorClock clock { get; set; } [Resolved] protected IBindable Beatmap { get; private set; } From 50059860498d63d2dbe8cd77b5840f2bf628a9da Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 22 May 2020 20:18:47 +0900 Subject: [PATCH 0868/1117] Cleanup test --- .../TestSceneContractedPanelMiddleContent.cs | 25 +++---------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneContractedPanelMiddleContent.cs b/osu.Game.Tests/Visual/Ranking/TestSceneContractedPanelMiddleContent.cs index e1e00e3c2b..972ac26b84 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneContractedPanelMiddleContent.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneContractedPanelMiddleContent.cs @@ -9,16 +9,13 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Testing; using osu.Game.Beatmaps; -using osu.Game.Graphics.Sprites; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; using osu.Game.Scoring; using osu.Game.Screens.Ranking; using osu.Game.Screens.Ranking.Contracted; using osu.Game.Tests.Beatmaps; -using osu.Game.Users; using osuTK; namespace osu.Game.Tests.Visual.Ranking @@ -29,22 +26,9 @@ namespace osu.Game.Tests.Visual.Ranking private RulesetStore rulesetStore { get; set; } [Test] - public void TestMapWithKnownMapper() + public void TestShowPanel() { - var author = new User { Username = "mapper_name" }; - - AddStep("show example score", () => showPanel(createTestBeatmap(author), new TestScoreInfo(new OsuRuleset().RulesetInfo))); - - AddAssert("mapper name present", () => this.ChildrenOfType().Any(spriteText => spriteText.Text == "mapper_name")); - } - - [Test] - public void TestMapWithUnknownMapper() - { - AddStep("show example score", () => showPanel(createTestBeatmap(null), new TestScoreInfo(new OsuRuleset().RulesetInfo))); - - AddAssert("mapped by text not present", () => - this.ChildrenOfType().All(spriteText => !containsAny(spriteText.Text, "mapped", "by"))); + AddStep("show example score", () => showPanel(createTestBeatmap(), new TestScoreInfo(new OsuRuleset().RulesetInfo))); } private void showPanel(WorkingBeatmap workingBeatmap, ScoreInfo score) @@ -52,10 +36,9 @@ namespace osu.Game.Tests.Visual.Ranking Child = new ContractedPanelMiddleContentContainer(workingBeatmap, score); } - private WorkingBeatmap createTestBeatmap(User author) + private WorkingBeatmap createTestBeatmap() { var beatmap = new TestBeatmap(rulesetStore.GetRuleset(0)); - beatmap.Metadata.Author = author; beatmap.Metadata.Title = "Verrrrrrrrrrrrrrrrrrry looooooooooooooooooooooooong beatmap title"; beatmap.Metadata.Artist = "Verrrrrrrrrrrrrrrrrrry looooooooooooooooooooooooong beatmap artist"; @@ -75,7 +58,7 @@ namespace osu.Game.Tests.Visual.Ranking Anchor = Anchor.Centre; Origin = Anchor.Centre; - Size = new Vector2(ScorePanel.CONTRACTED_WIDTH, 700); + Size = new Vector2(ScorePanel.CONTRACTED_WIDTH, 460); Children = new Drawable[] { new Box From 6bcc4c95cc0794260e82a7d70cfe06aa31e7c33f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 22 May 2020 20:19:23 +0900 Subject: [PATCH 0869/1117] Schedule api callback --- osu.Game/Screens/Ranking/ResultsScreen.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index cdceaa939e..af748d8336 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -120,7 +120,7 @@ namespace osu.Game.Screens.Ranking var req = new GetScoresRequest(Score.Beatmap, Score.Ruleset); - req.Success += r => + req.Success += r => Schedule(() => { foreach (var s in r.Scores.Select(s => s.CreateScoreInfo(rulesets))) { @@ -129,7 +129,7 @@ namespace osu.Game.Screens.Ranking panels.AddScore(s); } - }; + }); api.Queue(req); } From 80388feac4f6bbc7f87b617476c142f55511fa78 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 22 May 2020 20:39:02 +0900 Subject: [PATCH 0870/1117] Disable scroll user scroll controls in list --- osu.Game/Screens/Ranking/ResultsScreen.cs | 69 +++++++++++++--------- osu.Game/Screens/Ranking/ScorePanelList.cs | 4 ++ 2 files changed, 46 insertions(+), 27 deletions(-) diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index af748d8336..133bdcca4a 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -59,42 +59,57 @@ namespace osu.Game.Screens.Ranking { FillFlowContainer buttons; - InternalChildren = new[] + InternalChild = new GridContainer { - new ResultsScrollContainer + RelativeSizeAxes = Axes.Both, + Content = new[] { - Child = panels = new ScorePanelList(Score) + new Drawable[] { - RelativeSizeAxes = Axes.Both, - } - }, - bottomPanel = new Container - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - RelativeSizeAxes = Axes.X, - Height = TwoLayerButton.SIZE_EXTENDED.Y, - Alpha = 0, - Children = new Drawable[] + new ResultsScrollContainer + { + Child = panels = new ScorePanelList(Score) + { + RelativeSizeAxes = Axes.Both, + } + } + }, + new[] { - new Box + bottomPanel = new Container { - RelativeSizeAxes = Axes.Both, - Colour = Color4Extensions.FromHex("#333") - }, - buttons = new FillFlowContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - AutoSizeAxes = Axes.Both, - Spacing = new Vector2(5), - Direction = FillDirection.Horizontal, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.X, + Height = TwoLayerButton.SIZE_EXTENDED.Y, + Alpha = 0, Children = new Drawable[] { - new ReplayDownloadButton(Score) { Width = 300 }, + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4Extensions.FromHex("#333") + }, + buttons = new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Spacing = new Vector2(5), + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + new ReplayDownloadButton(Score) { Width = 300 }, + } + } } } } + }, + RowDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.AutoSize) } }; @@ -171,7 +186,7 @@ namespace osu.Game.Screens.Ranking protected override void Update() { base.Update(); - content.Height = Math.Max(768, DrawHeight); + content.Height = Math.Max(768 - TwoLayerButton.SIZE_EXTENDED.Y, DrawHeight); } } } diff --git a/osu.Game/Screens/Ranking/ScorePanelList.cs b/osu.Game/Screens/Ranking/ScorePanelList.cs index ed6d07d078..97a132b9ff 100644 --- a/osu.Game/Screens/Ranking/ScorePanelList.cs +++ b/osu.Game/Screens/Ranking/ScorePanelList.cs @@ -149,6 +149,10 @@ namespace osu.Game.Screens.Ranking base.UpdateAfterChildren(); } + + public override bool HandlePositionalInput => false; + + public override bool HandleNonPositionalInput => false; } } } From 12d65f305f24cdfd8dc0792d94eb4c6c5fa2f020 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 May 2020 22:11:55 +0900 Subject: [PATCH 0871/1117] Simplify and fix incorrect seeking --- osu.Game/Screens/Edit/EditorClock.cs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/osu.Game/Screens/Edit/EditorClock.cs b/osu.Game/Screens/Edit/EditorClock.cs index 214b45f654..dd934c10cd 100644 --- a/osu.Game/Screens/Edit/EditorClock.cs +++ b/osu.Game/Screens/Edit/EditorClock.cs @@ -87,15 +87,7 @@ namespace osu.Game.Screens.Edit private void seek(int direction, bool snapped, double amount = 1) { - double current = CurrentTime; - - // if a seek transform is active, use its end time instead of the reported current time. - var existingTransform = Transforms.OfType().FirstOrDefault(); - - // but only if the requested direction is in the same direction as the transform. - // this allows quick pivoting rather than resetting the transform for the first opposite direction movement. - if (existingTransform != null && Math.Sign(existingTransform.EndValue - current) == Math.Sign(direction)) - current = existingTransform.EndValue; + double current = CurrentTimeAccurate; if (amount <= 0) throw new ArgumentException("Value should be greater than zero", nameof(amount)); From 83f4ba107f6b97185c0487bb75e45516e73aa52e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 May 2020 22:41:06 +0900 Subject: [PATCH 0872/1117] Fix defaults not being applied correctly to blueprints after StartTime is changed --- osu.Game/Rulesets/Edit/PlacementBlueprint.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs index e71ccc33a4..90a114bcdc 100644 --- a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs +++ b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs @@ -34,6 +34,8 @@ namespace osu.Game.Rulesets.Edit private readonly IBindable beatmap = new Bindable(); + private Bindable startTimeBindable; + [Resolved] private IPlacementHandler placementHandler { get; set; } @@ -55,7 +57,8 @@ namespace osu.Game.Rulesets.Edit EditorClock = clock; - ApplyDefaultsToHitObject(); + startTimeBindable = HitObject.StartTimeBindable.GetBoundCopy(); + startTimeBindable.BindValueChanged(_ => ApplyDefaultsToHitObject(), true); } /// From 3d3cc2c15efbf4ac33e1a70cea6ec9dd4ecb8f5b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 22 May 2020 17:26:37 +0300 Subject: [PATCH 0873/1117] Dispose BeatmapOnlineLookupQueue cache download request --- osu.Game/Beatmaps/BeatmapManager.cs | 7 ++++++- .../Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs | 7 ++++++- osu.Game/OsuGameBase.cs | 1 + 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 7aaf0ca08d..b286c054e9 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -34,7 +34,7 @@ namespace osu.Game.Beatmaps /// /// Handles the storage and retrieval of Beatmaps/WorkingBeatmaps. /// - public partial class BeatmapManager : DownloadableArchiveModelManager + public partial class BeatmapManager : DownloadableArchiveModelManager, IDisposable { /// /// Fired when a single difficulty has been hidden. @@ -433,6 +433,11 @@ namespace osu.Game.Beatmaps return endTime - startTime; } + public void Dispose() + { + onlineLookupQueue?.Dispose(); + } + /// /// A dummy WorkingBeatmap for the purpose of retrieving a beatmap for star difficulty calculation. /// diff --git a/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs b/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs index 2c79a664c5..d47d37806e 100644 --- a/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs +++ b/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs @@ -23,7 +23,7 @@ namespace osu.Game.Beatmaps { public partial class BeatmapManager { - private class BeatmapOnlineLookupQueue + private class BeatmapOnlineLookupQueue : IDisposable { private readonly IAPIProvider api; private readonly Storage storage; @@ -180,6 +180,11 @@ namespace osu.Game.Beatmaps return false; } + public void Dispose() + { + cacheDownloadRequest?.Dispose(); + } + [Serializable] [SuppressMessage("ReSharper", "InconsistentNaming")] private class CachedOnlineBeatmapLookup diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index c367c3b636..453587df18 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -337,6 +337,7 @@ namespace osu.Game { base.Dispose(isDisposing); RulesetStore?.Dispose(); + BeatmapManager?.Dispose(); contextFactory.FlushConnections(); } From 554be1c4222eec67b7926c2a4a802b6130aeae35 Mon Sep 17 00:00:00 2001 From: Olle Kelderman Date: Fri, 22 May 2020 19:25:05 +0200 Subject: [PATCH 0874/1117] add the ability to set the size of the Tournament Client to an arbitrary value instead of a fixed 1080p option --- osu.Game.Tournament/Screens/SetupScreen.cs | 79 ++++++++++++++++++---- 1 file changed, 67 insertions(+), 12 deletions(-) diff --git a/osu.Game.Tournament/Screens/SetupScreen.cs b/osu.Game.Tournament/Screens/SetupScreen.cs index c91379b2d6..f9ec29d0c6 100644 --- a/osu.Game.Tournament/Screens/SetupScreen.cs +++ b/osu.Game.Tournament/Screens/SetupScreen.cs @@ -25,7 +25,7 @@ namespace osu.Game.Tournament.Screens private FillFlowContainer fillFlow; private LoginOverlay loginOverlay; - private ActionableInfo resolution; + private ActionableInfoWithNumberBox resolution; [Resolved] private MatchIPCInfo ipc { get; set; } @@ -108,18 +108,22 @@ namespace osu.Game.Tournament.Screens Items = rulesets.AvailableRulesets, Current = LadderInfo.Ruleset, }, - resolution = new ActionableInfo + resolution = new ActionableInfoWithNumberBox { Label = "Stream area resolution", - ButtonText = "Set to 1080p", - Action = () => + ButtonText = "Set size", + Action = i => { - windowSize.Value = new Size((int)(1920 / TournamentSceneManager.STREAM_AREA_WIDTH * TournamentSceneManager.REQUIRED_WIDTH), 1080); + i = Math.Clamp(i, 480, 2160); + windowSize.Value = new Size((int)(i * aspect_ratio / TournamentSceneManager.STREAM_AREA_WIDTH * TournamentSceneManager.REQUIRED_WIDTH), i); + resolution.NumberValue = i; } }, }; } + private const float aspect_ratio = 16f / 9f; + protected override void Update() { base.Update(); @@ -149,7 +153,7 @@ namespace osu.Game.Tournament.Screens private class ActionableInfo : LabelledDrawable { - private OsuButton button; + protected OsuButton Button; public ActionableInfo() : base(true) @@ -158,22 +162,22 @@ namespace osu.Game.Tournament.Screens public string ButtonText { - set => button.Text = value; + set => Button.Text = value; } public string Value { - set => valueText.Text = value; + set => ValueText.Text = value; } public bool Failing { - set => valueText.Colour = value ? Color4.Red : Color4.White; + set => ValueText.Colour = value ? Color4.Red : Color4.White; } public Action Action; - private TournamentSpriteText valueText; + protected TournamentSpriteText ValueText; protected override Drawable CreateComponent() => new Container { @@ -181,12 +185,12 @@ namespace osu.Game.Tournament.Screens RelativeSizeAxes = Axes.X, Children = new Drawable[] { - valueText = new TournamentSpriteText + ValueText = new TournamentSpriteText { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, }, - button = new TriangleButton + Button = new TriangleButton { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, @@ -196,5 +200,56 @@ namespace osu.Game.Tournament.Screens } }; } + + private class ActionableInfoWithNumberBox : ActionableInfo + { + public new Action Action; + + private OsuNumberBox numberBox; + + public int NumberValue + { + get + { + int.TryParse(numberBox.Text, out var val); + return val; + } + set => numberBox.Text = value.ToString(); + } + + protected override Drawable CreateComponent() => new Container + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Children = new Drawable[] + { + ValueText = new TournamentSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + }, + numberBox = new OsuNumberBox + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Width = 100, + Margin = new MarginPadding + { + Right = 110 + } + }, + Button = new TriangleButton + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Size = new Vector2(100, 30), + Action = () => + { + if (numberBox.Text.Length > 0) Action?.Invoke(NumberValue); + } + }, + } + }; + } } } From bc82c2d3b7b6995f77473ea9686f8404e16c3686 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 23 May 2020 10:44:53 +0900 Subject: [PATCH 0875/1117] Move drawable addition above event bindings --- osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 9ba3e30445..10f0855e33 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -40,10 +40,10 @@ namespace osu.Game.Rulesets.Osu.Edit [BackgroundDependencyLoader] private void load() { + LayerBelowRuleset.Add(distanceSnapGridContainer = new Container { RelativeSizeAxes = Axes.Both }); + EditorBeatmap.SelectedHitObjects.CollectionChanged += (_, __) => updateDistanceSnapGrid(); EditorBeatmap.PlacementObject.ValueChanged += _ => updateDistanceSnapGrid(); - - LayerBelowRuleset.Add(distanceSnapGridContainer = new Container { RelativeSizeAxes = Axes.Both }); } protected override ComposeBlueprintContainer CreateBlueprintContainer() => new OsuBlueprintContainer(HitObjects); From a8dbfe279159b3157b573f99a125c1fd4eda9060 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 23 May 2020 10:57:17 +0900 Subject: [PATCH 0876/1117] Fix distance snap grid not disappearing when exiting playfield --- .../Edit/OsuHitObjectComposer.cs | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 10f0855e33..62287574ea 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -86,10 +86,24 @@ namespace osu.Game.Rulesets.Osu.Edit distanceSnapGridContainer.Clear(); distanceSnapGridCache.Invalidate(); - if (BlueprintContainer.CurrentTool is SelectTool && !EditorBeatmap.SelectedHitObjects.Any()) - return; + switch (BlueprintContainer.CurrentTool) + { + case SelectTool _: + if (!EditorBeatmap.SelectedHitObjects.Any()) + return; - if ((distanceSnapGrid = createDistanceSnapGrid(EditorBeatmap.SelectedHitObjects)) != null) + distanceSnapGrid = createDistanceSnapGrid(EditorBeatmap.SelectedHitObjects); + break; + + default: + if (!CursorInPlacementArea) + return; + + distanceSnapGrid = createDistanceSnapGrid(Enumerable.Empty()); + break; + } + + if (distanceSnapGrid != null) { distanceSnapGridContainer.Add(distanceSnapGrid); distanceSnapGridCache.Validate(); From 224a3ff462cbbaf45896578511f2769bc0134dda Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 23 May 2020 11:44:45 +0900 Subject: [PATCH 0877/1117] Add note about gameplay mechanics in osu!lazer --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 59d72247f5..336bf33f7e 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,8 @@ Rhythm is just a *click* away. The future of [osu!](https://osu.ppy.sh) and the This project is under heavy development, but is in a stable state. Users are encouraged to try it out and keep it installed alongside the stable *osu!* client. It will continue to evolve to the point of eventually replacing the existing stable client as an update. +**IMPORTANT:** Gameplay mechanics (and other features which you may have come to know and love) are in a constant state of flux. Game balance and final quality-of-life passses come at the end of development, preceeded by experimentation and changes which may potentially **reduce playability or usability**. This is done in order to allow us to move forward as developers and designers more efficiently. If this offends you, please consider sticking to the stable releases of osu! (found on the website). We are not yet open to heated discussion over game mechanics and will not be using github as a forum for such discussions just yet. + We are accepting bug reports (please report with as much detail as possible and follow the existing issue templates). Feature requests are also welcome, but understand that our focus is on completing the game to feature parity before adding new features. A few resources are available as starting points to getting involved and understanding the project: - Detailed release changelogs are available on the [official osu! site](https://osu.ppy.sh/home/changelog/lazer). From edc46d1dce0e8f82e924de8becdb30a787b71031 Mon Sep 17 00:00:00 2001 From: Berkan Diler Date: Sat, 23 May 2020 16:18:55 +0200 Subject: [PATCH 0878/1117] Fix osu.Game.Benchmarks --- osu.Game.Benchmarks/BenchmarkBeatmapParsing.cs | 6 +++--- osu.Game.Benchmarks/osu.Game.Benchmarks.csproj | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game.Benchmarks/BenchmarkBeatmapParsing.cs b/osu.Game.Benchmarks/BenchmarkBeatmapParsing.cs index 394fd75488..a6b7c8fcdb 100644 --- a/osu.Game.Benchmarks/BenchmarkBeatmapParsing.cs +++ b/osu.Game.Benchmarks/BenchmarkBeatmapParsing.cs @@ -8,7 +8,7 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.Formats; using osu.Game.IO; using osu.Game.IO.Archives; -using osu.Game.Resources; +using osu.Game.Tests.Resources; namespace osu.Game.Benchmarks { @@ -18,8 +18,8 @@ namespace osu.Game.Benchmarks public override void SetUp() { - using (var resources = new DllResourceStore(OsuResources.ResourceAssembly)) - using (var archive = resources.GetStream("Beatmaps/241526 Soleily - Renatus.osz")) + using (var resources = new DllResourceStore(typeof(TestResources).Assembly)) + using (var archive = resources.GetStream($"Resources/Archives/241526 Soleily - Renatus.osz")) using (var reader = new ZipArchiveReader(archive)) reader.GetStream("Soleily - Renatus (Gamu) [Insane].osu").CopyTo(beatmapStream); } diff --git a/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj b/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj index 88fe8f1150..41e726e05c 100644 --- a/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj +++ b/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj @@ -13,6 +13,7 @@ + From de60d509e8ac00b882d091b099f9171dfb4081f3 Mon Sep 17 00:00:00 2001 From: Berkan Diler Date: Sat, 23 May 2020 17:01:34 +0200 Subject: [PATCH 0879/1117] Remove redundant string interpolation to fix CI --- osu.Game.Benchmarks/BenchmarkBeatmapParsing.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Benchmarks/BenchmarkBeatmapParsing.cs b/osu.Game.Benchmarks/BenchmarkBeatmapParsing.cs index a6b7c8fcdb..1d207d04c7 100644 --- a/osu.Game.Benchmarks/BenchmarkBeatmapParsing.cs +++ b/osu.Game.Benchmarks/BenchmarkBeatmapParsing.cs @@ -19,7 +19,7 @@ namespace osu.Game.Benchmarks public override void SetUp() { using (var resources = new DllResourceStore(typeof(TestResources).Assembly)) - using (var archive = resources.GetStream($"Resources/Archives/241526 Soleily - Renatus.osz")) + using (var archive = resources.GetStream("Resources/Archives/241526 Soleily - Renatus.osz")) using (var reader = new ZipArchiveReader(archive)) reader.GetStream("Soleily - Renatus (Gamu) [Insane].osu").CopyTo(beatmapStream); } From c071fe61407440f6035d35f3fef2949036ad402e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 24 May 2020 13:44:11 +0900 Subject: [PATCH 0880/1117] Add the ability to export skins --- osu.Game/Beatmaps/BeatmapManager.cs | 23 --------------- osu.Game/Database/ArchiveModelManager.cs | 28 +++++++++++++++++++ .../Overlays/Settings/Sections/SkinSection.cs | 24 ++++++++++++++++ 3 files changed, 52 insertions(+), 23 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index b286c054e9..f626b45e42 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -27,7 +27,6 @@ using osu.Game.Online.API.Requests; using osu.Game.Rulesets; using osu.Game.Rulesets.Objects; using Decoder = osu.Game.Beatmaps.Formats.Decoder; -using ZipArchive = SharpCompress.Archives.Zip.ZipArchive; namespace osu.Game.Beatmaps { @@ -66,7 +65,6 @@ namespace osu.Game.Beatmaps private readonly AudioManager audioManager; private readonly GameHost host; private readonly BeatmapOnlineLookupQueue onlineLookupQueue; - private readonly Storage exportStorage; public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, AudioManager audioManager, GameHost host = null, WorkingBeatmap defaultBeatmap = null) @@ -83,7 +81,6 @@ namespace osu.Game.Beatmaps beatmaps.BeatmapRestored += b => beatmapRestored.Value = new WeakReference(b); onlineLookupQueue = new BeatmapOnlineLookupQueue(api, storage); - exportStorage = storage.GetStorageForDirectory("exports"); } protected override ArchiveDownloadRequest CreateDownloadRequest(BeatmapSetInfo set, bool minimiseDownloadSize) => @@ -214,26 +211,6 @@ namespace osu.Game.Beatmaps workingCache.Remove(working); } - /// - /// Exports a to an .osz package. - /// - /// The to export. - public void Export(BeatmapSetInfo set) - { - var localSet = QueryBeatmapSet(s => s.ID == set.ID); - - using (var archive = ZipArchive.Create()) - { - foreach (var file in localSet.Files) - archive.AddEntry(file.Filename, Files.Storage.GetStream(file.FileInfo.StoragePath)); - - using (var outputStream = exportStorage.GetStream($"{set}.osz", FileAccess.Write, FileMode.Create)) - archive.SaveTo(outputStream); - - exportStorage.OpenInNativeExplorer(); - } - } - private readonly WeakList workingCache = new WeakList(); /// diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 33b16cbaaf..3db367555f 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -22,6 +22,7 @@ using osu.Game.IO.Archives; using osu.Game.IPC; using osu.Game.Overlays.Notifications; using osu.Game.Utils; +using SharpCompress.Archives.Zip; using SharpCompress.Common; using FileInfo = osu.Game.IO.FileInfo; @@ -82,6 +83,8 @@ namespace osu.Game.Database // ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised) private ArchiveImportIPCChannel ipc; + private readonly Storage exportStorage; + protected ArchiveModelManager(Storage storage, IDatabaseContextFactory contextFactory, MutableDatabaseBackedStoreWithFileIncludes modelStore, IIpcHost importHost = null) { ContextFactory = contextFactory; @@ -90,6 +93,8 @@ namespace osu.Game.Database ModelStore.ItemAdded += item => handleEvent(() => itemAdded.Value = new WeakReference(item)); ModelStore.ItemRemoved += item => handleEvent(() => itemRemoved.Value = new WeakReference(item)); + exportStorage = storage.GetStorageForDirectory("exports"); + Files = new FileStore(contextFactory, storage); if (importHost != null) @@ -369,6 +374,29 @@ namespace osu.Game.Database return item; }, cancellationToken, TaskCreationOptions.HideScheduler, import_scheduler).Unwrap(); + /// + /// Exports an item to an legacy (.zip based) package. + /// + /// The item to export. + public void Export(TModel item) + { + var retrievedItem = ModelStore.ConsumableItems.FirstOrDefault(s => s.ID == item.ID); + + if (retrievedItem == null) + throw new ArgumentException("Specified model count not be found", nameof(item)); + + using (var archive = ZipArchive.Create()) + { + foreach (var file in retrievedItem.Files) + archive.AddEntry(file.Filename, Files.Storage.GetStream(file.FileInfo.StoragePath)); + + using (var outputStream = exportStorage.GetStream($"{item}{HandledExtensions.First()}", FileAccess.Write, FileMode.Create)) + archive.SaveTo(outputStream); + + exportStorage.OpenInNativeExplorer(); + } + } + public void UpdateFile(TModel model, TFileModel file, Stream contents) { using (var usage = ContextFactory.GetForWrite()) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 94080f5592..a89f2c26c8 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -7,6 +7,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; +using osu.Framework.Logging; using osu.Game.Configuration; using osu.Game.Graphics.UserInterface; using osu.Game.Skinning; @@ -34,13 +35,33 @@ namespace osu.Game.Overlays.Settings.Sections private IBindable> managerAdded; private IBindable> managerRemoved; + private SettingsButton exportButton; + + private Bindable currentSkin; + [BackgroundDependencyLoader] private void load(OsuConfigManager config) { FlowContent.Spacing = new Vector2(0, 5); + Children = new Drawable[] { skinDropdown = new SkinSettingsDropdown(), + exportButton = new SettingsButton + { + Text = "Export selected skin", + Action = () => + { + try + { + skins.Export(skins.CurrentSkin.Value.SkinInfo); + } + catch (Exception) + { + Logger.Log("Could not export current skin", level: LogLevel.Error); + } + } + }, new SettingsSlider { LabelText = "Menu cursor size", @@ -81,6 +102,9 @@ namespace osu.Game.Overlays.Settings.Sections skinDropdown.Bindable = dropdownBindable; skinDropdown.Items = skins.GetAllUsableSkins().ToArray(); + currentSkin = skins.CurrentSkin.GetBoundCopy(); + currentSkin.BindValueChanged(skin => exportButton.Enabled.Value = skin.NewValue.SkinInfo.ID > 0); + // Todo: This should not be necessary when OsuConfigManager is databased if (skinDropdown.Items.All(s => s.ID != configBindable.Value)) configBindable.Value = 0; From f277b0c99f2a53f7b7bd92e0f748e1e206fe452c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 24 May 2020 22:30:56 +0900 Subject: [PATCH 0881/1117] Use better formatting for skin display (matching BeatmapMetadata) --- osu.Game/Skinning/SkinInfo.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Skinning/SkinInfo.cs b/osu.Game/Skinning/SkinInfo.cs index 6b9627188e..b9fe44ef3b 100644 --- a/osu.Game/Skinning/SkinInfo.cs +++ b/osu.Game/Skinning/SkinInfo.cs @@ -24,8 +24,6 @@ namespace osu.Game.Skinning public bool DeletePending { get; set; } - public string FullName => $"\"{Name}\" by {Creator}"; - public static SkinInfo Default { get; } = new SkinInfo { Name = "osu!lazer", @@ -34,6 +32,10 @@ namespace osu.Game.Skinning public bool Equals(SkinInfo other) => other != null && ID == other.ID; - public override string ToString() => FullName; + public override string ToString() + { + string author = Creator == null ? string.Empty : $"({Creator})"; + return $"{Name} {author}".Trim(); + } } } From 234fa2844574e81ac520b90974af0350b750f070 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 24 May 2020 22:34:31 +0900 Subject: [PATCH 0882/1117] Ensure export filename is valid --- osu.Game/Database/ArchiveModelManager.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 3db367555f..9fc1bfceb5 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -390,7 +390,7 @@ namespace osu.Game.Database foreach (var file in retrievedItem.Files) archive.AddEntry(file.Filename, Files.Storage.GetStream(file.FileInfo.StoragePath)); - using (var outputStream = exportStorage.GetStream($"{item}{HandledExtensions.First()}", FileAccess.Write, FileMode.Create)) + using (var outputStream = exportStorage.GetStream($"{getValidFilename(item.ToString())}{HandledExtensions.First()}", FileAccess.Write, FileMode.Create)) archive.SaveTo(outputStream); exportStorage.OpenInNativeExplorer(); @@ -738,5 +738,12 @@ namespace osu.Game.Database } #endregion + + private string getValidFilename(string filename) + { + foreach (char c in Path.GetInvalidFileNameChars()) + filename = filename.Replace(c, '_'); + return filename; + } } } From 904d17224f75260783a416170ce6b53ee1edd8c5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 24 May 2020 23:09:38 +0900 Subject: [PATCH 0883/1117] Fix english --- osu.Game/Database/ArchiveModelManager.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 9fc1bfceb5..f21f708f95 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -375,7 +375,7 @@ namespace osu.Game.Database }, cancellationToken, TaskCreationOptions.HideScheduler, import_scheduler).Unwrap(); /// - /// Exports an item to an legacy (.zip based) package. + /// Exports an item to a legacy (.zip based) package. /// /// The item to export. public void Export(TModel item) @@ -383,7 +383,7 @@ namespace osu.Game.Database var retrievedItem = ModelStore.ConsumableItems.FirstOrDefault(s => s.ID == item.ID); if (retrievedItem == null) - throw new ArgumentException("Specified model count not be found", nameof(item)); + throw new ArgumentException("Specified model could not be found", nameof(item)); using (var archive = ZipArchive.Create()) { From 8ab65e4c5d3083682ea2fde406dbfcd229fca359 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 24 May 2020 23:15:24 +0900 Subject: [PATCH 0884/1117] Move implementation into own class --- .../Overlays/Settings/Sections/SkinSection.cs | 53 +++++++++++-------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index a89f2c26c8..b84b9fec37 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -35,10 +35,6 @@ namespace osu.Game.Overlays.Settings.Sections private IBindable> managerAdded; private IBindable> managerRemoved; - private SettingsButton exportButton; - - private Bindable currentSkin; - [BackgroundDependencyLoader] private void load(OsuConfigManager config) { @@ -47,21 +43,7 @@ namespace osu.Game.Overlays.Settings.Sections Children = new Drawable[] { skinDropdown = new SkinSettingsDropdown(), - exportButton = new SettingsButton - { - Text = "Export selected skin", - Action = () => - { - try - { - skins.Export(skins.CurrentSkin.Value.SkinInfo); - } - catch (Exception) - { - Logger.Log("Could not export current skin", level: LogLevel.Error); - } - } - }, + new ExportSkinButton(), new SettingsSlider { LabelText = "Menu cursor size", @@ -102,9 +84,6 @@ namespace osu.Game.Overlays.Settings.Sections skinDropdown.Bindable = dropdownBindable; skinDropdown.Items = skins.GetAllUsableSkins().ToArray(); - currentSkin = skins.CurrentSkin.GetBoundCopy(); - currentSkin.BindValueChanged(skin => exportButton.Enabled.Value = skin.NewValue.SkinInfo.ID > 0); - // Todo: This should not be necessary when OsuConfigManager is databased if (skinDropdown.Items.All(s => s.ID != configBindable.Value)) configBindable.Value = 0; @@ -141,5 +120,35 @@ namespace osu.Game.Overlays.Settings.Sections protected override DropdownMenu CreateMenu() => base.CreateMenu().With(m => m.MaxHeight = 200); } } + + private class ExportSkinButton : SettingsButton + { + [Resolved] + private SkinManager skins { get; set; } + + private Bindable currentSkin; + + [BackgroundDependencyLoader] + private void load() + { + Text = "Export selected skin"; + Action = export; + + currentSkin = skins.CurrentSkin.GetBoundCopy(); + currentSkin.BindValueChanged(skin => Enabled.Value = skin.NewValue.SkinInfo.ID > 0, true); + } + + private void export() + { + try + { + skins.Export(currentSkin.Value.SkinInfo); + } + catch (Exception e) + { + Logger.Log($"Could not export current skin: {e.Message}", level: LogLevel.Error); + } + } + } } } From 1062e07ec12f017da02a903f0ad1189468e8fe53 Mon Sep 17 00:00:00 2001 From: Olle Kelderman Date: Sun, 24 May 2020 22:24:46 +0200 Subject: [PATCH 0885/1117] refactor and implemented feedback: - button text change - renamed ActionableInfoWithNumberBox to ResolutionSelector and moved the clamping logic inside it - also removed the ugly right margin and added the FillFlowContainer --- osu.Game.Tournament/Screens/SetupScreen.cs | 99 +++++++++++----------- 1 file changed, 51 insertions(+), 48 deletions(-) diff --git a/osu.Game.Tournament/Screens/SetupScreen.cs b/osu.Game.Tournament/Screens/SetupScreen.cs index f9ec29d0c6..33eefbe553 100644 --- a/osu.Game.Tournament/Screens/SetupScreen.cs +++ b/osu.Game.Tournament/Screens/SetupScreen.cs @@ -25,7 +25,7 @@ namespace osu.Game.Tournament.Screens private FillFlowContainer fillFlow; private LoginOverlay loginOverlay; - private ActionableInfoWithNumberBox resolution; + private ResolutionSelector resolution; [Resolved] private MatchIPCInfo ipc { get; set; } @@ -108,15 +108,13 @@ namespace osu.Game.Tournament.Screens Items = rulesets.AvailableRulesets, Current = LadderInfo.Ruleset, }, - resolution = new ActionableInfoWithNumberBox + resolution = new ResolutionSelector { Label = "Stream area resolution", - ButtonText = "Set size", + ButtonText = "Set height", Action = i => { - i = Math.Clamp(i, 480, 2160); windowSize.Value = new Size((int)(i * aspect_ratio / TournamentSceneManager.STREAM_AREA_WIDTH * TournamentSceneManager.REQUIRED_WIDTH), i); - resolution.NumberValue = i; } }, }; @@ -167,17 +165,18 @@ namespace osu.Game.Tournament.Screens public string Value { - set => ValueText.Text = value; + set => valueText.Text = value; } public bool Failing { - set => ValueText.Colour = value ? Color4.Red : Color4.White; + set => valueText.Colour = value ? Color4.Red : Color4.White; } public Action Action; - protected TournamentSpriteText ValueText; + private TournamentSpriteText valueText; + protected FillFlowContainer FlowContainer; protected override Drawable CreateComponent() => new Container { @@ -185,71 +184,75 @@ namespace osu.Game.Tournament.Screens RelativeSizeAxes = Axes.X, Children = new Drawable[] { - ValueText = new TournamentSpriteText + valueText = new TournamentSpriteText { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, }, - Button = new TriangleButton + FlowContainer = new FillFlowContainer { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, - Size = new Vector2(100, 30), - Action = () => Action?.Invoke() - }, + AutoSizeAxes = Axes.Both, + Spacing = new Vector2(10, 0), + Children = new Drawable[] + { + Button = new TriangleButton + { + Size = new Vector2(100, 30), + Action = () => Action?.Invoke() + } + } + } } }; } - private class ActionableInfoWithNumberBox : ActionableInfo + private class ResolutionSelector : ActionableInfo { + private const int height_min_allowed_value = 480; + private const int height_max_allowed_value = 2160; // 4k public new Action Action; private OsuNumberBox numberBox; - public int NumberValue + protected override Drawable CreateComponent() { - get + var drawable = base.CreateComponent(); + FlowContainer.Insert(0, numberBox = new OsuNumberBox { - int.TryParse(numberBox.Text, out var val); - return val; - } - set => numberBox.Text = value.ToString(); - } + Width = 100 + }); + FlowContainer.SetLayoutPosition(Button, 1); - protected override Drawable CreateComponent() => new Container - { - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - Children = new Drawable[] + base.Action = () => { - ValueText = new TournamentSpriteText + if (numberBox.Text.Length > 0) { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - }, - numberBox = new OsuNumberBox - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Width = 100, - Margin = new MarginPadding + // box contains text + if (!int.TryParse(numberBox.Text, out var number)) { - Right = 110 + // at this point, the only reason we can arrive here is if the input number was too big to parse into an int + // so clamp to max allowed value + number = height_max_allowed_value; } - }, - Button = new TriangleButton - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Size = new Vector2(100, 30), - Action = () => + else { - if (numberBox.Text.Length > 0) Action?.Invoke(NumberValue); + number = Math.Clamp(number, height_min_allowed_value, height_max_allowed_value); } - }, - } - }; + + // in case number got clamped, reset number in numberBox + numberBox.Text = number.ToString(); + + Action?.Invoke(number); + } + else + { + // TODO: input box was empty, give user feedback? do nothing? + } + }; + return drawable; + } } } } From a174117880874df70d5cd07d210db4117a1d7cee Mon Sep 17 00:00:00 2001 From: Olle Kelderman Date: Mon, 25 May 2020 00:55:10 +0200 Subject: [PATCH 0886/1117] fix flowcontainer order properly and removed todo as its decided to do nothing there for now --- osu.Game.Tournament/Screens/SetupScreen.cs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tournament/Screens/SetupScreen.cs b/osu.Game.Tournament/Screens/SetupScreen.cs index 33eefbe553..bf328987fe 100644 --- a/osu.Game.Tournament/Screens/SetupScreen.cs +++ b/osu.Game.Tournament/Screens/SetupScreen.cs @@ -151,7 +151,7 @@ namespace osu.Game.Tournament.Screens private class ActionableInfo : LabelledDrawable { - protected OsuButton Button; + private OsuButton button; public ActionableInfo() : base(true) @@ -160,7 +160,7 @@ namespace osu.Game.Tournament.Screens public string ButtonText { - set => Button.Text = value; + set => button.Text = value; } public string Value @@ -197,7 +197,7 @@ namespace osu.Game.Tournament.Screens Spacing = new Vector2(10, 0), Children = new Drawable[] { - Button = new TriangleButton + button = new TriangleButton { Size = new Vector2(100, 30), Action = () => Action?.Invoke() @@ -219,11 +219,10 @@ namespace osu.Game.Tournament.Screens protected override Drawable CreateComponent() { var drawable = base.CreateComponent(); - FlowContainer.Insert(0, numberBox = new OsuNumberBox + FlowContainer.Insert(-1, numberBox = new OsuNumberBox { Width = 100 }); - FlowContainer.SetLayoutPosition(Button, 1); base.Action = () => { @@ -246,10 +245,6 @@ namespace osu.Game.Tournament.Screens Action?.Invoke(number); } - else - { - // TODO: input box was empty, give user feedback? do nothing? - } }; return drawable; } From 1977affe7e6fac5e263011ba20768d1401dcafe3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 May 2020 09:27:11 +0900 Subject: [PATCH 0887/1117] Fix OpenInNativeExplorer not working correctly for wrapped storages --- osu.Game/IO/WrappedStorage.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/IO/WrappedStorage.cs b/osu.Game/IO/WrappedStorage.cs index 646faba9eb..cd775df0fd 100644 --- a/osu.Game/IO/WrappedStorage.cs +++ b/osu.Game/IO/WrappedStorage.cs @@ -69,7 +69,9 @@ namespace osu.Game.IO public override void DeleteDatabase(string name) => UnderlyingStorage.DeleteDatabase(MutatePath(name)); - public override void OpenInNativeExplorer() => UnderlyingStorage.OpenInNativeExplorer(); + public override void OpenInNativeExplorer() => UnderlyingStorage.OpenPathInNativeExplorer(subPath); + + public override void OpenPathInNativeExplorer(string path) => UnderlyingStorage.OpenPathInNativeExplorer(MutatePath(path)); public override Storage GetStorageForDirectory(string path) { From 6904d5d2470c9164f593527a1bdc9d59a6d3f3bd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 May 2020 13:12:53 +0900 Subject: [PATCH 0888/1117] Remove unnecessary override --- osu.Game/IO/WrappedStorage.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/IO/WrappedStorage.cs b/osu.Game/IO/WrappedStorage.cs index cd775df0fd..1dd3afbfae 100644 --- a/osu.Game/IO/WrappedStorage.cs +++ b/osu.Game/IO/WrappedStorage.cs @@ -69,8 +69,6 @@ namespace osu.Game.IO public override void DeleteDatabase(string name) => UnderlyingStorage.DeleteDatabase(MutatePath(name)); - public override void OpenInNativeExplorer() => UnderlyingStorage.OpenPathInNativeExplorer(subPath); - public override void OpenPathInNativeExplorer(string path) => UnderlyingStorage.OpenPathInNativeExplorer(MutatePath(path)); public override Storage GetStorageForDirectory(string path) From b44beb413729bb40dc5294c194454ea0707ddf3e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 May 2020 15:40:25 +0900 Subject: [PATCH 0889/1117] Remove double resolution of EditorClock --- osu.Game/Rulesets/Edit/PlacementBlueprint.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs index 1e0507fc0a..e2bb8b5995 100644 --- a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs +++ b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Edit /// protected readonly HitObject HitObject; - [Resolved] + [Resolved(canBeNull: true)] protected EditorClock EditorClock { get; private set; } private readonly IBindable beatmap = new Bindable(); @@ -85,9 +85,6 @@ namespace osu.Game.Rulesets.Edit PlacementActive = false; } - [Resolved(canBeNull: true)] - private EditorClock editorClock { get; set; } - /// /// Updates the position of this to a new screen-space position. /// @@ -95,7 +92,7 @@ namespace osu.Game.Rulesets.Edit public virtual void UpdatePosition(SnapResult snapResult) { if (!PlacementActive) - HitObject.StartTime = snapResult.Time ?? editorClock?.CurrentTime ?? Time.Current; + HitObject.StartTime = snapResult.Time ?? EditorClock?.CurrentTime ?? Time.Current; } /// From cd65fc860b56f7fb2132e547e93ab068e49b308b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 25 May 2020 16:15:55 +0900 Subject: [PATCH 0890/1117] Remove extra default application --- osu.Game/Rulesets/Edit/PlacementBlueprint.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs index e2bb8b5995..3541a78faa 100644 --- a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs +++ b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs @@ -56,8 +56,6 @@ namespace osu.Game.Rulesets.Edit { this.beatmap.BindTo(beatmap); - ApplyDefaultsToHitObject(); - startTimeBindable = HitObject.StartTimeBindable.GetBoundCopy(); startTimeBindable.BindValueChanged(_ => ApplyDefaultsToHitObject(), true); } From 6f4cd6111cbdeb04d4e22f88f1693bd9f05e9dad Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 25 May 2020 16:50:49 +0900 Subject: [PATCH 0891/1117] Drop obsoletion status for now --- osu.Game/Rulesets/Objects/HitObject.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs index 8ff2bdefb3..6f9053d7cb 100644 --- a/osu.Game/Rulesets/Objects/HitObject.cs +++ b/osu.Game/Rulesets/Objects/HitObject.cs @@ -146,7 +146,6 @@ namespace osu.Game.Rulesets.Objects #pragma warning restore 618 } - [Obsolete("Use the overload with cancellation support instead.")] // can be removed 20201115 protected virtual void CreateNestedHitObjects() { } From d09b579aeee7ca9ca75f78e3bc67754882f1800e Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 25 May 2020 09:32:24 +0000 Subject: [PATCH 0892/1117] Bump DiffPlex from 1.6.1 to 1.6.2 Bumps [DiffPlex](https://github.com/mmanela/diffplex) from 1.6.1 to 1.6.2. - [Release notes](https://github.com/mmanela/diffplex/releases) - [Commits](https://github.com/mmanela/diffplex/commits) Signed-off-by: dependabot-preview[bot] --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 010ef8578a..dbaf0697a0 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -19,7 +19,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 88b0c7dd8a..664a629369 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -75,7 +75,7 @@ - + From c4665048dbd036ee6f492eb45f00bde4a38eedd7 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 25 May 2020 09:34:26 +0000 Subject: [PATCH 0893/1117] Bump SharpCompress from 0.25.0 to 0.25.1 Bumps [SharpCompress](https://github.com/adamhathcock/sharpcompress) from 0.25.0 to 0.25.1. - [Release notes](https://github.com/adamhathcock/sharpcompress/releases) - [Commits](https://github.com/adamhathcock/sharpcompress/compare/0.25...0.25.1) Signed-off-by: dependabot-preview[bot] --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index dbaf0697a0..d8feb4df24 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -27,7 +27,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 664a629369..6d74be5f85 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -81,7 +81,7 @@ - + From af5fac471e56a2b819983ac49067e094e6ea8850 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 May 2020 13:54:30 +0900 Subject: [PATCH 0894/1117] Remove unnecessary size propagation in HitObjectComposer --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 37 +++++---------------- 1 file changed, 9 insertions(+), 28 deletions(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 1987148aed..e5479251b5 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -51,8 +51,6 @@ namespace osu.Game.Rulesets.Edit protected readonly Container LayerBelowRuleset = new Container { RelativeSizeAxes = Axes.Both }; - private readonly List layerContainers = new List(); - private InputManager inputManager; private RadioButtonCollection toolboxCollection; @@ -82,17 +80,6 @@ namespace osu.Game.Rulesets.Edit return; } - var layerBelowRuleset = drawableRulesetWrapper.CreatePlayfieldAdjustmentContainer().WithChildren(new Drawable[] - { - LayerBelowRuleset, - new EditorPlayfieldBorder { RelativeSizeAxes = Axes.Both } - }); - - var layerAboveRuleset = drawableRulesetWrapper.CreatePlayfieldAdjustmentContainer().WithChild(BlueprintContainer = CreateBlueprintContainer()); - - layerContainers.Add(layerBelowRuleset); - layerContainers.Add(layerAboveRuleset); - InternalChild = new GridContainer { RelativeSizeAxes = Axes.Both, @@ -116,9 +103,16 @@ namespace osu.Game.Rulesets.Edit RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - layerBelowRuleset, + // layers below playfield + drawableRulesetWrapper.CreatePlayfieldAdjustmentContainer().WithChildren(new Drawable[] + { + LayerBelowRuleset, + new EditorPlayfieldBorder { RelativeSizeAxes = Axes.Both } + }), drawableRulesetWrapper, - layerAboveRuleset + // layers above playfield + drawableRulesetWrapper.CreatePlayfieldAdjustmentContainer() + .WithChild(BlueprintContainer = CreateBlueprintContainer()) } } }, @@ -162,19 +156,6 @@ namespace osu.Game.Rulesets.Edit inputManager = GetContainingInputManager(); } - protected override void UpdateAfterChildren() - { - base.UpdateAfterChildren(); - - layerContainers.ForEach(l => - { - l.Anchor = drawableRulesetWrapper.Playfield.Anchor; - l.Origin = drawableRulesetWrapper.Playfield.Origin; - l.Position = drawableRulesetWrapper.Playfield.Position; - l.Size = drawableRulesetWrapper.Playfield.Size; - }); - } - private void selectionChanged(object sender, NotifyCollectionChangedEventArgs changedArgs) { if (EditorBeatmap.SelectedHitObjects.Any()) From b8130bd3669f447a8179c13ba889207f261c262c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 May 2020 18:26:21 +0900 Subject: [PATCH 0895/1117] Make mania selection blueprint abstract --- .../Edit/Blueprints/ManiaSelectionBlueprint.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs index b8574b804e..0089a9fbee 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs @@ -11,7 +11,7 @@ using osuTK; namespace osu.Game.Rulesets.Mania.Edit.Blueprints { - public class ManiaSelectionBlueprint : OverlaySelectionBlueprint + public abstract class ManiaSelectionBlueprint : OverlaySelectionBlueprint { public new DrawableManiaHitObject DrawableObject => (DrawableManiaHitObject)base.DrawableObject; @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints [Resolved] private IManiaHitObjectComposer composer { get; set; } - public ManiaSelectionBlueprint(DrawableHitObject drawableObject) + protected ManiaSelectionBlueprint(DrawableHitObject drawableObject) : base(drawableObject) { RelativeSizeAxes = Axes.None; From 2c16619ecd9efa009601eefd0fdc3068fc4e4838 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 May 2020 18:26:28 +0900 Subject: [PATCH 0896/1117] Move time to position conversion to ScrollingHitObjectContainer --- osu.Game.Rulesets.Mania/UI/Column.cs | 50 ----------- .../Scrolling/ScrollingHitObjectContainer.cs | 87 +++++++++++++++++++ .../UI/Scrolling/ScrollingPlayfield.cs | 13 +++ 3 files changed, 100 insertions(+), 50 deletions(-) diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index be31954099..511d6c8623 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -17,7 +17,6 @@ using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Skinning; using osuTK; using osu.Game.Rulesets.Mania.Beatmaps; -using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; namespace osu.Game.Rulesets.Mania.UI { @@ -143,54 +142,5 @@ namespace osu.Game.Rulesets.Mania.UI public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) // This probably shouldn't exist as is, but the columns in the stage are separated by a 1px border => DrawRectangle.Inflate(new Vector2(Stage.COLUMN_SPACING / 2, 0)).Contains(ToLocalSpace(screenSpacePos)); - - /// - /// Given a time, return the screen space position within this column. - /// - public Vector2 ScreenSpacePositionAtTime(double time) - { - var pos = ScrollingInfo.Algorithm.PositionAt(time, Time.Current, ScrollingInfo.TimeRange.Value, HitObjectContainer.DrawHeight); - - switch (ScrollingInfo.Direction.Value) - { - case ScrollingDirection.Down: - // We're dealing with screen coordinates in which the position decreases towards the centre of the screen resulting in an increase in start time. - // The scrolling algorithm instead assumes a top anchor meaning an increase in time corresponds to an increase in position, - // so when scrolling downwards the coordinates need to be flipped. - pos = HitObjectContainer.DrawHeight - pos; - - // Blueprints are centred on the mouse position, such that the hitobject position is anchored at the top or bottom of the blueprint depending on the scroll direction. - pos -= DefaultNotePiece.NOTE_HEIGHT / 2; - break; - - case ScrollingDirection.Up: - pos += DefaultNotePiece.NOTE_HEIGHT / 2; - break; - } - - return HitObjectContainer.ToScreenSpace(new Vector2(HitObjectContainer.DrawWidth / 2, pos)); - } - - /// - /// Given a position in screen space, return the time within this column. - /// - public double TimeAtScreenSpacePosition(Vector2 screenSpacePosition) - { - // convert to local space of column so we can snap and fetch correct location. - Vector2 localPosition = HitObjectContainer.ToLocalSpace(screenSpacePosition); - - switch (ScrollingInfo.Direction.Value) - { - case ScrollingDirection.Down: - // as above - localPosition.Y = HitObjectContainer.DrawHeight - localPosition.Y; - break; - } - - // offset for the fact that blueprints are centered, as above. - localPosition.Y -= DefaultNotePiece.NOTE_HEIGHT / 2; - - return ScrollingInfo.Algorithm.TimeAt(localPosition.Y, Time.Current, ScrollingInfo.TimeRange.Value, HitObjectContainer.DrawHeight); - } } } diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index 15e625872d..4ef2c04f23 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics; using osu.Framework.Layout; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; +using osuTK; namespace osu.Game.Rulesets.UI.Scrolling { @@ -78,6 +79,92 @@ namespace osu.Game.Rulesets.UI.Scrolling hitObjectInitialStateCache.Clear(); } + public double TimeAtScreenSpace(Vector2 screenSpacePosition) + { + // convert to local space of column so we can snap and fetch correct location. + Vector2 localPosition = ToLocalSpace(screenSpacePosition); + + float position = 0; + + switch (scrollingInfo.Direction.Value) + { + case ScrollingDirection.Up: + case ScrollingDirection.Down: + position = localPosition.Y; + break; + + case ScrollingDirection.Right: + case ScrollingDirection.Left: + position = localPosition.X; + break; + } + + flipPositionIfRequired(ref position); + + return scrollingInfo.Algorithm.TimeAt(position, Time.Current, scrollingInfo.TimeRange.Value, getLength()); + } + + public Vector2 ScreenSpacePositionAtTime(double time) + { + var pos = scrollingInfo.Algorithm.PositionAt(time, Time.Current, scrollingInfo.TimeRange.Value, getLength()); + + flipPositionIfRequired(ref pos); + + switch (scrollingInfo.Direction.Value) + { + case ScrollingDirection.Up: + case ScrollingDirection.Down: + return ToScreenSpace(new Vector2(getBredth() / 2, pos)); + + default: + return ToScreenSpace(new Vector2(pos, getBredth() / 2)); + } + } + + private float getLength() + { + switch (scrollingInfo.Direction.Value) + { + case ScrollingDirection.Left: + case ScrollingDirection.Right: + return DrawWidth; + + default: + return DrawHeight; + } + } + + private float getBredth() + { + switch (scrollingInfo.Direction.Value) + { + case ScrollingDirection.Up: + case ScrollingDirection.Down: + return DrawWidth; + + default: + return DrawHeight; + } + } + + private void flipPositionIfRequired(ref float position) + { + // We're dealing with screen coordinates in which the position decreases towards the centre of the screen resulting in an increase in start time. + // The scrolling algorithm instead assumes a top anchor meaning an increase in time corresponds to an increase in position, + // so when scrolling downwards the coordinates need to be flipped. + + switch (scrollingInfo.Direction.Value) + { + case ScrollingDirection.Down: + position = DrawHeight - position; + break; + + case ScrollingDirection.Right: + position = DrawWidth - position; + break; + } + } + private void onDefaultsApplied(DrawableHitObject drawableObject) { // The cache may not exist if the hitobject state hasn't been computed yet (e.g. if the hitobject was added + defaults applied in the same frame). diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs index fd143a3687..1ccde9b1e3 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs @@ -4,6 +4,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Rulesets.Objects.Drawables; +using osuTK; namespace osu.Game.Rulesets.UI.Scrolling { @@ -23,6 +24,18 @@ namespace osu.Game.Rulesets.UI.Scrolling Direction.BindTo(ScrollingInfo.Direction); } + /// + /// Given a position in screen space, return the time within this column. + /// + public virtual double TimeAtScreenSpacePosition(Vector2 screenSpacePosition) => + ((ScrollingHitObjectContainer)HitObjectContainer).TimeAtScreenSpace(screenSpacePosition); + + /// + /// Given a time, return the screen space position within this column. + /// + public virtual Vector2 ScreenSpacePositionAtTime(double time) + => ((ScrollingHitObjectContainer)HitObjectContainer).ScreenSpacePositionAtTime(time); + protected sealed override HitObjectContainer CreateHitObjectContainer() => new ScrollingHitObjectContainer(); } } From e7442ec3a287d7887747bb392e5502bb8fcca387 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 May 2020 19:21:53 +0900 Subject: [PATCH 0897/1117] Remove need for ManiaSnapResult --- .../ManiaPlacementBlueprintTestScene.cs | 2 +- .../Blueprints/HoldNotePlacementBlueprint.cs | 4 ++-- .../Blueprints/ManiaPlacementBlueprint.cs | 2 +- .../Edit/Blueprints/NotePlacementBlueprint.cs | 4 ++-- .../Edit/ManiaHitObjectComposer.cs | 19 ++-------------- .../Edit/ManiaSnapResult.cs | 20 ----------------- .../Edit/OsuHitObjectComposer.cs | 2 +- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 22 ++++++++++++++++++- osu.Game/Rulesets/Edit/SnapResult.cs | 6 ++++- 9 files changed, 35 insertions(+), 46 deletions(-) delete mode 100644 osu.Game.Rulesets.Mania/Edit/ManiaSnapResult.cs diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestScene.cs b/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestScene.cs index fd18907d96..1119a66f63 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestScene.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestScene.cs @@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Mania.Tests var time = column.TimeAtScreenSpacePosition(InputManager.CurrentState.Mouse.Position); var pos = column.ScreenSpacePositionAtTime(time); - return new ManiaSnapResult(pos, time, column); + return new SnapResult(pos, time, column); } protected override Container CreateHitObjectContainer() => new ScrollingTestContainer(ScrollingDirection.Down) { RelativeSizeAxes = Axes.Both }; diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs index b757c17a48..cd549ab6aa 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs @@ -78,9 +78,9 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints } else { - if (result is ManiaSnapResult maniaResult) + if (result.Playfield != null) { - headPiece.Width = tailPiece.Width = maniaResult.Column.DrawWidth; + headPiece.Width = tailPiece.Width = result.Playfield.DrawWidth; headPiece.X = tailPiece.X = ToLocalSpace(result.ScreenSpacePosition).X; } diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs index d173da9d9a..27a279e044 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs @@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints base.UpdatePosition(result); if (!PlacementActive) - Column = (result as ManiaSnapResult)?.Column; + Column = result.Playfield as Column; } } } diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/NotePlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/NotePlacementBlueprint.cs index 5f6db2e6dd..684004b558 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/NotePlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/NotePlacementBlueprint.cs @@ -26,9 +26,9 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints { base.UpdatePosition(result); - if (result is ManiaSnapResult maniaResult) + if (result.Playfield != null) { - piece.Width = maniaResult.Column.DrawWidth; + piece.Width = result.Playfield.DrawWidth; piece.Position = ToLocalSpace(result.ScreenSpacePosition); } } diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index 683e921cbf..83bf202674 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -53,23 +53,8 @@ namespace osu.Game.Rulesets.Mania.Edit public IScrollingInfo ScrollingInfo => drawableRuleset.ScrollingInfo; - public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) - { - var column = Playfield.GetColumnByPosition(screenSpacePosition); - - if (column == null) - return new SnapResult(screenSpacePosition, null); - - double targetTime = column.TimeAtScreenSpacePosition(screenSpacePosition); - - // apply beat snapping - targetTime = BeatSnapProvider.SnapTime(targetTime); - - // convert back to screen space - screenSpacePosition = column.ScreenSpacePositionAtTime(targetTime); - - return new ManiaSnapResult(screenSpacePosition, targetTime, column); - } + protected override Playfield PlayfieldAtScreenSpacePosition(Vector2 screenSpacePosition) => + Playfield.GetColumnByPosition(screenSpacePosition); protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) { diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaSnapResult.cs b/osu.Game.Rulesets.Mania/Edit/ManiaSnapResult.cs deleted file mode 100644 index b94f5e51c4..0000000000 --- a/osu.Game.Rulesets.Mania/Edit/ManiaSnapResult.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.Edit; -using osu.Game.Rulesets.Mania.UI; -using osuTK; - -namespace osu.Game.Rulesets.Mania.Edit -{ - public class ManiaSnapResult : SnapResult - { - public readonly Column Column; - - public ManiaSnapResult(Vector2 screenSpacePosition, double time, Column column) - : base(screenSpacePosition, time) - { - Column = column; - } - } -} diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 62287574ea..9e7dc11fdd 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -78,7 +78,7 @@ namespace osu.Game.Rulesets.Osu.Edit (Vector2 pos, double time) = distanceSnapGrid.GetSnappedPosition(distanceSnapGrid.ToLocalSpace(screenSpacePosition)); - return new SnapResult(distanceSnapGrid.ToScreenSpace(pos), time); + return new SnapResult(distanceSnapGrid.ToScreenSpace(pos), time, PlayfieldAtScreenSpacePosition(screenSpacePosition)); } private void updateDistanceSnapGrid() diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 1987148aed..b26284c060 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -19,6 +19,7 @@ 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.Screens.Edit; using osu.Game.Screens.Edit.Components.RadioButtons; using osu.Game.Screens.Edit.Compose; @@ -224,7 +225,26 @@ namespace osu.Game.Rulesets.Edit public void Delete(HitObject hitObject) => EditorBeatmap.Remove(hitObject); - public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, null); + protected virtual Playfield PlayfieldAtScreenSpacePosition(Vector2 screenSpacePosition) => drawableRulesetWrapper.Playfield; + + public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) + { + var playfield = PlayfieldAtScreenSpacePosition(screenSpacePosition); + double? targetTime = null; + + if (playfield is ScrollingPlayfield scrollingPlayfield) + { + targetTime = scrollingPlayfield.TimeAtScreenSpacePosition(screenSpacePosition); + + // apply beat snapping + targetTime = BeatSnapProvider.SnapTime(targetTime.Value); + + // convert back to screen space + screenSpacePosition = scrollingPlayfield.ScreenSpacePositionAtTime(targetTime.Value); + } + + return new SnapResult(screenSpacePosition, targetTime, playfield); + } public override float GetBeatSnapDistanceAt(double referenceTime) { diff --git a/osu.Game/Rulesets/Edit/SnapResult.cs b/osu.Game/Rulesets/Edit/SnapResult.cs index 5d07d7b233..31dd2b9496 100644 --- a/osu.Game/Rulesets/Edit/SnapResult.cs +++ b/osu.Game/Rulesets/Edit/SnapResult.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.Rulesets.UI; using osuTK; namespace osu.Game.Rulesets.Edit @@ -20,10 +21,13 @@ namespace osu.Game.Rulesets.Edit /// public double? Time; - public SnapResult(Vector2 screenSpacePosition, double? time) + public readonly Playfield Playfield; + + public SnapResult(Vector2 screenSpacePosition, double? time, Playfield playfield = null) { ScreenSpacePosition = screenSpacePosition; Time = time; + Playfield = playfield; } } } From 827345ed88f6dd6792ebd1cfc4a16989cd163723 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 May 2020 20:21:06 +0900 Subject: [PATCH 0898/1117] Fix mania offsets --- .../Blueprints/HoldNotePlacementBlueprint.cs | 17 ++++++++++++++ .../Edit/ManiaHitObjectComposer.cs | 23 +++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs index cd549ab6aa..500b26917d 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs @@ -8,6 +8,7 @@ using osu.Framework.Input.Events; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Mania.Edit.Blueprints.Components; using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.UI.Scrolling; using osuTK; using osuTK.Input; @@ -22,6 +23,9 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints [Resolved] private IManiaHitObjectComposer composer { get; set; } + [Resolved] + private IScrollingInfo scrollingInfo { get; set; } + public HoldNotePlacementBlueprint() : base(new HoldNote()) { @@ -43,6 +47,19 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints { headPiece.Y = Parent.ToLocalSpace(Column.ScreenSpacePositionAtTime(HitObject.StartTime)).Y; tailPiece.Y = Parent.ToLocalSpace(Column.ScreenSpacePositionAtTime(HitObject.EndTime)).Y; + + switch (scrollingInfo.Direction.Value) + { + case ScrollingDirection.Down: + headPiece.Y -= headPiece.DrawHeight / 2; + tailPiece.Y -= tailPiece.DrawHeight / 2; + break; + + case ScrollingDirection.Up: + headPiece.Y += headPiece.DrawHeight / 2; + tailPiece.Y += tailPiece.DrawHeight / 2; + break; + } } var topPosition = new Vector2(headPiece.DrawPosition.X, Math.Min(headPiece.DrawPosition.Y, tailPiece.DrawPosition.Y)); diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index 83bf202674..73cbadc97c 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -9,6 +9,7 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Input; +using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; @@ -56,6 +57,28 @@ namespace osu.Game.Rulesets.Mania.Edit protected override Playfield PlayfieldAtScreenSpacePosition(Vector2 screenSpacePosition) => Playfield.GetColumnByPosition(screenSpacePosition); + public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) + { + var result = base.SnapScreenSpacePositionToValidTime(screenSpacePosition); + + switch (ScrollingInfo.Direction.Value) + { + case ScrollingDirection.Down: + result.ScreenSpacePosition -= new Vector2(0, getNoteHeight() / 2); + break; + + case ScrollingDirection.Up: + result.ScreenSpacePosition += new Vector2(0, getNoteHeight() / 2); + break; + } + + return result; + } + + private float getNoteHeight() => + Playfield.GetColumn(0).ToScreenSpace(new Vector2(DefaultNotePiece.NOTE_HEIGHT)).Y - + Playfield.GetColumn(0).ToScreenSpace(Vector2.Zero).Y; + protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) { drawableRuleset = new DrawableManiaEditRuleset(ruleset, beatmap, mods); From 8fc60d12c910dabdb765a93a403ab928d488a506 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 May 2020 22:09:09 +0900 Subject: [PATCH 0899/1117] Add back comments --- .../Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index 4ef2c04f23..544468fd47 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -79,6 +79,9 @@ namespace osu.Game.Rulesets.UI.Scrolling hitObjectInitialStateCache.Clear(); } + /// + /// Given a position in screen space, return the time within this column. + /// public double TimeAtScreenSpace(Vector2 screenSpacePosition) { // convert to local space of column so we can snap and fetch correct location. @@ -104,6 +107,9 @@ namespace osu.Game.Rulesets.UI.Scrolling return scrollingInfo.Algorithm.TimeAt(position, Time.Current, scrollingInfo.TimeRange.Value, getLength()); } + /// + /// Given a time, return the screen space position within this column. + /// public Vector2 ScreenSpacePositionAtTime(double time) { var pos = scrollingInfo.Algorithm.PositionAt(time, Time.Current, scrollingInfo.TimeRange.Value, getLength()); From cf341998c360fb3389dea953344c8bbc49cd11b3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 May 2020 22:55:21 +0900 Subject: [PATCH 0900/1117] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index f0f16d3763..b7d08fb120 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index d8feb4df24..d5017a436f 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -24,7 +24,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 6d74be5f85..19a36f1e1f 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -80,7 +80,7 @@ - + From 719da489221850d009c7a87389e3c25edf9a9bf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 25 May 2020 20:11:00 +0200 Subject: [PATCH 0901/1117] Rename delegate argument --- osu.Game.Tournament/Screens/SetupScreen.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tournament/Screens/SetupScreen.cs b/osu.Game.Tournament/Screens/SetupScreen.cs index bf328987fe..0ecab449ec 100644 --- a/osu.Game.Tournament/Screens/SetupScreen.cs +++ b/osu.Game.Tournament/Screens/SetupScreen.cs @@ -112,9 +112,9 @@ namespace osu.Game.Tournament.Screens { Label = "Stream area resolution", ButtonText = "Set height", - Action = i => + Action = height => { - windowSize.Value = new Size((int)(i * aspect_ratio / TournamentSceneManager.STREAM_AREA_WIDTH * TournamentSceneManager.REQUIRED_WIDTH), i); + windowSize.Value = new Size((int)(height * aspect_ratio / TournamentSceneManager.STREAM_AREA_WIDTH * TournamentSceneManager.REQUIRED_WIDTH), height); } }, }; From ca68d94cf7aa7acec4d638f61934836bd3c2b3c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 25 May 2020 20:18:17 +0200 Subject: [PATCH 0902/1117] Invert if to reduce nesting --- osu.Game.Tournament/Screens/SetupScreen.cs | 34 +++++++++++----------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/osu.Game.Tournament/Screens/SetupScreen.cs b/osu.Game.Tournament/Screens/SetupScreen.cs index 0ecab449ec..f8895f4703 100644 --- a/osu.Game.Tournament/Screens/SetupScreen.cs +++ b/osu.Game.Tournament/Screens/SetupScreen.cs @@ -226,25 +226,25 @@ namespace osu.Game.Tournament.Screens base.Action = () => { - if (numberBox.Text.Length > 0) + if (string.IsNullOrEmpty(numberBox.Text)) + return; + + // box contains text + if (!int.TryParse(numberBox.Text, out var number)) { - // box contains text - if (!int.TryParse(numberBox.Text, out var number)) - { - // at this point, the only reason we can arrive here is if the input number was too big to parse into an int - // so clamp to max allowed value - number = height_max_allowed_value; - } - else - { - number = Math.Clamp(number, height_min_allowed_value, height_max_allowed_value); - } - - // in case number got clamped, reset number in numberBox - numberBox.Text = number.ToString(); - - Action?.Invoke(number); + // at this point, the only reason we can arrive here is if the input number was too big to parse into an int + // so clamp to max allowed value + number = height_max_allowed_value; } + else + { + number = Math.Clamp(number, height_min_allowed_value, height_max_allowed_value); + } + + // in case number got clamped, reset number in numberBox + numberBox.Text = number.ToString(); + + Action?.Invoke(number); }; return drawable; } From 748f7fcd8b914ff7e84d0f29463142b18a6b244d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 25 May 2020 20:20:26 +0200 Subject: [PATCH 0903/1117] Rename constants --- osu.Game.Tournament/Screens/SetupScreen.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tournament/Screens/SetupScreen.cs b/osu.Game.Tournament/Screens/SetupScreen.cs index f8895f4703..e1594de69e 100644 --- a/osu.Game.Tournament/Screens/SetupScreen.cs +++ b/osu.Game.Tournament/Screens/SetupScreen.cs @@ -210,8 +210,8 @@ namespace osu.Game.Tournament.Screens private class ResolutionSelector : ActionableInfo { - private const int height_min_allowed_value = 480; - private const int height_max_allowed_value = 2160; // 4k + private const int minimum_window_height = 480; + private const int maximum_window_height = 2160; // 4k public new Action Action; private OsuNumberBox numberBox; @@ -234,11 +234,11 @@ namespace osu.Game.Tournament.Screens { // at this point, the only reason we can arrive here is if the input number was too big to parse into an int // so clamp to max allowed value - number = height_max_allowed_value; + number = maximum_window_height; } else { - number = Math.Clamp(number, height_min_allowed_value, height_max_allowed_value); + number = Math.Clamp(number, minimum_window_height, maximum_window_height); } // in case number got clamped, reset number in numberBox From d69111c665079d495906232e24a49c640c7fe6e0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 May 2020 10:17:34 +0900 Subject: [PATCH 0904/1117] Fix spelling of breadth --- .../Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index 544468fd47..9b84b67241 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -120,10 +120,10 @@ namespace osu.Game.Rulesets.UI.Scrolling { case ScrollingDirection.Up: case ScrollingDirection.Down: - return ToScreenSpace(new Vector2(getBredth() / 2, pos)); + return ToScreenSpace(new Vector2(getBreadth() / 2, pos)); default: - return ToScreenSpace(new Vector2(pos, getBredth() / 2)); + return ToScreenSpace(new Vector2(pos, getBreadth() / 2)); } } @@ -140,7 +140,7 @@ namespace osu.Game.Rulesets.UI.Scrolling } } - private float getBredth() + private float getBreadth() { switch (scrollingInfo.Direction.Value) { From 417e31d77f87b66087e00c8918c83bef0fbfcbf3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 May 2020 10:17:56 +0900 Subject: [PATCH 0905/1117] Rename function for consistency --- osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs | 2 +- osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index 9b84b67241..c817d84d5c 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -82,7 +82,7 @@ namespace osu.Game.Rulesets.UI.Scrolling /// /// Given a position in screen space, return the time within this column. /// - public double TimeAtScreenSpace(Vector2 screenSpacePosition) + public double TimeAtScreenSpacePosition(Vector2 screenSpacePosition) { // convert to local space of column so we can snap and fetch correct location. Vector2 localPosition = ToLocalSpace(screenSpacePosition); diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs index 1ccde9b1e3..9dac3f4de1 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.UI.Scrolling /// Given a position in screen space, return the time within this column. /// public virtual double TimeAtScreenSpacePosition(Vector2 screenSpacePosition) => - ((ScrollingHitObjectContainer)HitObjectContainer).TimeAtScreenSpace(screenSpacePosition); + ((ScrollingHitObjectContainer)HitObjectContainer).TimeAtScreenSpacePosition(screenSpacePosition); /// /// Given a time, return the screen space position within this column. From 13bd6be8a3e2bf97ab9fb0bf19c7cf732ddcd5cb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 May 2020 11:29:56 +0900 Subject: [PATCH 0906/1117] Convert wait steps into until steps --- .../Background/TestSceneUserDimBackgrounds.cs | 53 ++++++------------- 1 file changed, 17 insertions(+), 36 deletions(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs index 76d0c7a50f..edba462bc2 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs @@ -79,11 +79,9 @@ namespace osu.Game.Tests.Visual.Background InputManager.MoveMouseTo(playerLoader.ScreenPos); InputManager.MoveMouseTo(playerLoader.VisualSettingsPos); }); - waitForDim(); - AddAssert("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied()); + AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied()); AddStep("Stop background preview", () => InputManager.MoveMouseTo(playerLoader.ScreenPos)); - waitForDim(); - AddAssert("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && playerLoader.IsBlurCorrect()); + AddUntilStep("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && playerLoader.IsBlurCorrect()); } /// @@ -97,8 +95,7 @@ namespace osu.Game.Tests.Visual.Background performFullSetup(); AddStep("Trigger hover event", () => playerLoader.TriggerOnHover()); AddAssert("Background retained from song select", () => songSelect.IsBackgroundCurrent()); - waitForDim(); - AddAssert("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied()); + AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied()); } /// @@ -114,15 +111,13 @@ namespace osu.Game.Tests.Visual.Background player.ReplacesBackground.Value = true; player.StoryboardEnabled.Value = true; }); - waitForDim(); - AddAssert("Background is invisible, storyboard is visible", () => songSelect.IsBackgroundInvisible() && player.IsStoryboardVisible); + AddUntilStep("Background is invisible, storyboard is visible", () => songSelect.IsBackgroundInvisible() && player.IsStoryboardVisible); AddStep("Disable Storyboard", () => { player.ReplacesBackground.Value = false; player.StoryboardEnabled.Value = false; }); - waitForDim(); - AddAssert("Background is visible, storyboard is invisible", () => songSelect.IsBackgroundVisible() && !player.IsStoryboardVisible); + AddUntilStep("Background is visible, storyboard is invisible", () => songSelect.IsBackgroundVisible() && !player.IsStoryboardVisible); } /// @@ -134,8 +129,7 @@ namespace osu.Game.Tests.Visual.Background performFullSetup(); createFakeStoryboard(); AddStep("Exit to song select", () => player.Exit()); - waitForDim(); - AddAssert("Background is visible", () => songSelect.IsBackgroundVisible()); + AddUntilStep("Background is visible", () => songSelect.IsBackgroundVisible()); } /// @@ -145,14 +139,11 @@ namespace osu.Game.Tests.Visual.Background public void DisableUserDimBackgroundTest() { performFullSetup(); - waitForDim(); - AddAssert("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied()); + AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied()); AddStep("Enable user dim", () => songSelect.DimEnabled.Value = false); - waitForDim(); - AddAssert("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && songSelect.IsUserBlurDisabled()); + AddUntilStep("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && songSelect.IsUserBlurDisabled()); AddStep("Disable user dim", () => songSelect.DimEnabled.Value = true); - waitForDim(); - AddAssert("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied()); + AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied()); } /// @@ -170,11 +161,9 @@ namespace osu.Game.Tests.Visual.Background }); AddStep("Enable user dim", () => player.DimmableStoryboard.EnableUserDim.Value = true); AddStep("Set dim level to 1", () => songSelect.DimLevel.Value = 1f); - waitForDim(); - AddAssert("Storyboard is invisible", () => !player.IsStoryboardVisible); + AddUntilStep("Storyboard is invisible", () => !player.IsStoryboardVisible); AddStep("Disable user dim", () => player.DimmableStoryboard.EnableUserDim.Value = false); - waitForDim(); - AddAssert("Storyboard is visible", () => player.IsStoryboardVisible); + AddUntilStep("Storyboard is visible", () => player.IsStoryboardVisible); } /// @@ -185,11 +174,9 @@ namespace osu.Game.Tests.Visual.Background { performFullSetup(true); AddStep("Pause", () => player.Pause()); - waitForDim(); - AddAssert("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied()); + AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied()); AddStep("Unpause", () => player.Resume()); - waitForDim(); - AddAssert("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied()); + AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied()); } /// @@ -203,8 +190,7 @@ namespace osu.Game.Tests.Visual.Background AddStep("Transition to Results", () => player.Push(results = new FadeAccessibleResults(new ScoreInfo { User = new User { Username = "osu!" } }))); AddUntilStep("Wait for results is current", () => results.IsCurrentScreen()); - waitForDim(); - AddAssert("Screen is undimmed, original background retained", () => + AddUntilStep("Screen is undimmed, original background retained", () => songSelect.IsBackgroundUndimmed() && songSelect.IsBackgroundCurrent() && results.IsBlurCorrect()); } @@ -216,8 +202,7 @@ namespace osu.Game.Tests.Visual.Background { performFullSetup(); AddStep("Exit to song select", () => player.Exit()); - waitForDim(); - AddAssert("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && songSelect.IsBlurCorrect()); + AddUntilStep("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && songSelect.IsBlurCorrect()); } /// @@ -229,15 +214,11 @@ namespace osu.Game.Tests.Visual.Background performFullSetup(); AddStep("Move mouse to Visual Settings", () => InputManager.MoveMouseTo(playerLoader.VisualSettingsPos)); AddStep("Resume PlayerLoader", () => player.Restart()); - waitForDim(); - AddAssert("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied()); + AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied()); AddStep("Move mouse to center of screen", () => InputManager.MoveMouseTo(playerLoader.ScreenPos)); - waitForDim(); - AddAssert("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && playerLoader.IsBlurCorrect()); + AddUntilStep("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && playerLoader.IsBlurCorrect()); } - private void waitForDim() => AddWaitStep("Wait for dim", 5); - private void createFakeStoryboard() => AddStep("Create storyboard", () => { player.StoryboardEnabled.Value = false; From 2bf066d72c6099c47bd967a11424b35c13004500 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 May 2020 11:30:36 +0900 Subject: [PATCH 0907/1117] Rename tests to match convention --- .../Background/TestSceneUserDimBackgrounds.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs index edba462bc2..d601f40afe 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs @@ -68,7 +68,7 @@ namespace osu.Game.Tests.Visual.Background /// Check if properly triggers the visual settings preview when a user hovers over the visual settings panel. /// [Test] - public void PlayerLoaderSettingsHoverTest() + public void TestPlayerLoaderSettingsHover() { setupUserSettings(); AddStep("Start player loader", () => songSelect.Push(playerLoader = new TestPlayerLoader(player = new LoadBlockingTestPlayer { BlockLoad = true }))); @@ -90,7 +90,7 @@ namespace osu.Game.Tests.Visual.Background /// We need to check that in this scenario, the dim and blur is still properly applied after entering player. /// [Test] - public void PlayerLoaderTransitionTest() + public void TestPlayerLoaderTransition() { performFullSetup(); AddStep("Trigger hover event", () => playerLoader.TriggerOnHover()); @@ -102,7 +102,7 @@ namespace osu.Game.Tests.Visual.Background /// Make sure the background is fully invisible (Alpha == 0) when the background should be disabled by the storyboard. /// [Test] - public void StoryboardBackgroundVisibilityTest() + public void TestStoryboardBackgroundVisibility() { performFullSetup(); createFakeStoryboard(); @@ -124,7 +124,7 @@ namespace osu.Game.Tests.Visual.Background /// When exiting player, the screen that it suspends/exits to needs to have a fully visible (Alpha == 1) background. /// [Test] - public void StoryboardTransitionTest() + public void TestStoryboardTransition() { performFullSetup(); createFakeStoryboard(); @@ -136,7 +136,7 @@ namespace osu.Game.Tests.Visual.Background /// Ensure is properly accepting user-defined visual changes for a background. /// [Test] - public void DisableUserDimBackgroundTest() + public void TestDisableUserDimBackground() { performFullSetup(); AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied()); @@ -150,7 +150,7 @@ namespace osu.Game.Tests.Visual.Background /// Ensure is properly accepting user-defined visual changes for a storyboard. /// [Test] - public void DisableUserDimStoryboardTest() + public void TestDisableUserDimStoryboard() { performFullSetup(); createFakeStoryboard(); @@ -170,7 +170,7 @@ namespace osu.Game.Tests.Visual.Background /// Check if the visual settings container retains dim and blur when pausing /// [Test] - public void PauseTest() + public void TestPause() { performFullSetup(true); AddStep("Pause", () => player.Pause()); @@ -183,7 +183,7 @@ namespace osu.Game.Tests.Visual.Background /// Check if the visual settings container removes user dim when suspending for /// [Test] - public void TransitionTest() + public void TestTransition() { performFullSetup(); FadeAccessibleResults results = null; @@ -198,7 +198,7 @@ namespace osu.Game.Tests.Visual.Background /// Check if background gets undimmed and unblurred when leaving for /// [Test] - public void TransitionOutTest() + public void TestTransitionOut() { performFullSetup(); AddStep("Exit to song select", () => player.Exit()); @@ -209,7 +209,7 @@ namespace osu.Game.Tests.Visual.Background /// Check if hovering on the visual settings dialogue after resuming from player still previews the background dim. /// [Test] - public void ResumeFromPlayerTest() + public void TestResumeFromPlayer() { performFullSetup(); AddStep("Move mouse to Visual Settings", () => InputManager.MoveMouseTo(playerLoader.VisualSettingsPos)); From d041de63ce3f312e22a1cb7a23cdc5a292c33da8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 May 2020 13:00:32 +0900 Subject: [PATCH 0908/1117] Allow SelectionHandler to provide custom context menu items without local hover check --- .../Compose/Components/SelectionHandler.cs | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index 764eae1056..9ecda9fdb8 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -244,14 +244,21 @@ namespace osu.Game.Screens.Edit.Compose.Components #region Context Menu - public virtual MenuItem[] ContextMenuItems + public MenuItem[] ContextMenuItems { get { if (!selectedBlueprints.Any(b => b.IsHovered)) return Array.Empty(); - var items = new List + var items = new List(); + + items.AddRange(GetContextMenuItemsForSelection(selectedBlueprints)); + + if (selectedBlueprints.Count == 1) + items.AddRange(selectedBlueprints[0].ContextMenuItems); + + items.AddRange(new[] { new OsuMenuItem("Sound") { @@ -263,15 +270,20 @@ namespace osu.Game.Screens.Edit.Compose.Components } }, new OsuMenuItem("Delete", MenuItemType.Destructive, deleteSelected), - }; - - if (selectedBlueprints.Count == 1) - items.AddRange(selectedBlueprints[0].ContextMenuItems); + }); return items.ToArray(); } } + /// + /// Provide context menu items relevant to current selection. Calling base is not required. + /// + /// The current selection. + /// The relevant menu items. + protected virtual IEnumerable GetContextMenuItemsForSelection(IEnumerable selection) + => Enumerable.Empty(); + private MenuItem createHitSampleMenuItem(string name, string sampleName) { return new TernaryStateMenuItem(name, MenuItemType.Standard, setHitSampleState) From aaf5596f9c0f619fc9eb63ab046f21a03885bb45 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 26 May 2020 15:54:07 +0900 Subject: [PATCH 0909/1117] Cleanup test --- .../TestSceneContractedPanelMiddleContent.cs | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneContractedPanelMiddleContent.cs b/osu.Game.Tests/Visual/Ranking/TestSceneContractedPanelMiddleContent.cs index 972ac26b84..76cfe75b59 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneContractedPanelMiddleContent.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneContractedPanelMiddleContent.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.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -15,7 +14,6 @@ using osu.Game.Rulesets.Osu; using osu.Game.Scoring; using osu.Game.Screens.Ranking; using osu.Game.Screens.Ranking.Contracted; -using osu.Game.Tests.Beatmaps; using osuTK; namespace osu.Game.Tests.Visual.Ranking @@ -28,7 +26,7 @@ namespace osu.Game.Tests.Visual.Ranking [Test] public void TestShowPanel() { - AddStep("show example score", () => showPanel(createTestBeatmap(), new TestScoreInfo(new OsuRuleset().RulesetInfo))); + AddStep("show example score", () => showPanel(CreateWorkingBeatmap(CreateBeatmap(new OsuRuleset().RulesetInfo)), new TestScoreInfo(new OsuRuleset().RulesetInfo))); } private void showPanel(WorkingBeatmap workingBeatmap, ScoreInfo score) @@ -36,17 +34,6 @@ namespace osu.Game.Tests.Visual.Ranking Child = new ContractedPanelMiddleContentContainer(workingBeatmap, score); } - private WorkingBeatmap createTestBeatmap() - { - var beatmap = new TestBeatmap(rulesetStore.GetRuleset(0)); - beatmap.Metadata.Title = "Verrrrrrrrrrrrrrrrrrry looooooooooooooooooooooooong beatmap title"; - beatmap.Metadata.Artist = "Verrrrrrrrrrrrrrrrrrry looooooooooooooooooooooooong beatmap artist"; - - return new TestWorkingBeatmap(beatmap); - } - - private bool containsAny(string text, params string[] stringsToMatch) => stringsToMatch.Any(text.Contains); - private class ContractedPanelMiddleContentContainer : Container { [Cached] From 1768cbd1319ff8a6ab1da72846c2ec4c0a2ab65d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 26 May 2020 15:56:56 +0900 Subject: [PATCH 0910/1117] Format score same as expanded panel --- .../Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs index a263a03a77..8cd0e7025e 100644 --- a/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs @@ -160,7 +160,7 @@ namespace osu.Game.Screens.Ranking.Contracted { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Text = score.TotalScore.ToString(), + Text = score.TotalScore.ToString("N0"), Font = OsuFont.GetFont(size: 20, weight: FontWeight.Medium, fixedWidth: true), Spacing = new Vector2(-1, 0) }, From 906a317a3d35151baae2339a77671fbd82f34231 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 26 May 2020 16:26:53 +0900 Subject: [PATCH 0911/1117] Reduce casting --- osu.Game/Screens/Ranking/ScorePanelList.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Ranking/ScorePanelList.cs b/osu.Game/Screens/Ranking/ScorePanelList.cs index 97a132b9ff..df2c66203b 100644 --- a/osu.Game/Screens/Ranking/ScorePanelList.cs +++ b/osu.Game/Screens/Ranking/ScorePanelList.cs @@ -118,11 +118,11 @@ namespace osu.Game.Screens.Ranking { public override IEnumerable FlowingChildren => applySorting(AliveInternalChildren); - public int GetPanelIndex(ScoreInfo score) => applySorting(Children).OfType().TakeWhile(s => s.Score != score).Count(); + public int GetPanelIndex(ScoreInfo score) => applySorting(Children).TakeWhile(s => s.Score != score).Count(); - private IEnumerable applySorting(IEnumerable drawables) => drawables.OfType() - .OrderByDescending(s => s.Score.TotalScore) - .ThenBy(s => s.Score.OnlineScoreID); + private IEnumerable applySorting(IEnumerable drawables) => drawables.OfType() + .OrderByDescending(s => s.Score.TotalScore) + .ThenBy(s => s.Score.OnlineScoreID); } private class Scroll : OsuScrollContainer From a1ece4f308d51317b5d2c6d25df1555a434f9f00 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 26 May 2020 16:26:58 +0900 Subject: [PATCH 0912/1117] Add expansion/contraction test --- .../Visual/Ranking/TestSceneScorePanel.cs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneScorePanel.cs b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanel.cs index fdb77c14a3..250fdc5ebd 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneScorePanel.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanel.cs @@ -12,6 +12,8 @@ namespace osu.Game.Tests.Visual.Ranking { public class TestSceneScorePanel : OsuTestScene { + private ScorePanel panel; + [Test] public void TestDRank() { @@ -84,9 +86,24 @@ namespace osu.Game.Tests.Visual.Ranking addPanelStep(score, PanelState.Contracted); } + [Test] + public void TestExpandAndContract() + { + var score = new TestScoreInfo(new OsuRuleset().RulesetInfo) { Accuracy = 0.925, Rank = ScoreRank.A }; + + addPanelStep(score, PanelState.Contracted); + AddWaitStep("wait for transition", 10); + + AddStep("expand panel", () => panel.State = PanelState.Expanded); + AddWaitStep("wait for transition", 10); + + AddStep("contract panel", () => panel.State = PanelState.Contracted); + AddWaitStep("wait for transition", 10); + } + private void addPanelStep(ScoreInfo score, PanelState state = PanelState.Expanded) => AddStep("add panel", () => { - Child = new ScorePanel(score) + Child = panel = new ScorePanel(score) { Anchor = Anchor.Centre, Origin = Anchor.Centre, From c86a003ef94eccc990c6e13f7d0ea7159de03ec6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 26 May 2020 16:27:41 +0900 Subject: [PATCH 0913/1117] Adjust transition for smaller sizes --- osu.Game/Screens/Ranking/ScorePanel.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Screens/Ranking/ScorePanel.cs b/osu.Game/Screens/Ranking/ScorePanel.cs index 2933bbddd1..a99b48e8f0 100644 --- a/osu.Game/Screens/Ranking/ScorePanel.cs +++ b/osu.Game/Screens/Ranking/ScorePanel.cs @@ -102,12 +102,14 @@ namespace osu.Game.Screens.Ranking { Anchor = Anchor.Centre, Origin = Anchor.Centre, + Size = new Vector2(40), Children = new Drawable[] { topLayerContainer = new Container { Name = "Top layer", RelativeSizeAxes = Axes.X, + Alpha = 0, Height = 120, Children = new Drawable[] { @@ -214,6 +216,8 @@ namespace osu.Game.Screens.Ranking // If the top layer was already expanded, then we don't need to wait for the resize and can instead transform immediately. This looks better when changing the panel state. using (BeginDelayedSequence(topLayerExpanded ? 0 : resize_duration + top_layer_expand_delay, true)) { + topLayerContainer.FadeIn(); + switch (state) { case PanelState.Expanded: From de0b6ec9f1941c64e19ab486748484351871074b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 26 May 2020 17:00:41 +0900 Subject: [PATCH 0914/1117] Create abstract implementation --- osu.Game/Screens/Play/Player.cs | 2 +- osu.Game/Screens/Play/ReplayPlayer.cs | 2 +- osu.Game/Screens/Ranking/ResultsScreen.cs | 27 ++++++++-------- osu.Game/Screens/Ranking/SoloResultsScreen.cs | 32 +++++++++++++++++++ osu.Game/Screens/Select/PlaySongSelect.cs | 2 +- 5 files changed, 49 insertions(+), 16 deletions(-) create mode 100644 osu.Game/Screens/Ranking/SoloResultsScreen.cs diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 3d4b20bec4..36198bcc65 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -479,7 +479,7 @@ namespace osu.Game.Screens.Play protected override bool OnScroll(ScrollEvent e) => mouseWheelDisabled.Value && !GameplayClockContainer.IsPaused.Value; - protected virtual ResultsScreen CreateResults(ScoreInfo score) => new ResultsScreen(score); + protected virtual ResultsScreen CreateResults(ScoreInfo score) => new SoloResultsScreen(score); #region Fail Logic diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs index f0c76163f1..b443603128 100644 --- a/osu.Game/Screens/Play/ReplayPlayer.cs +++ b/osu.Game/Screens/Play/ReplayPlayer.cs @@ -30,7 +30,7 @@ namespace osu.Game.Screens.Play this.Push(CreateResults(DrawableRuleset.ReplayScore.ScoreInfo)); } - protected override ResultsScreen CreateResults(ScoreInfo score) => new ResultsScreen(score, false); + protected override ResultsScreen CreateResults(ScoreInfo score) => new SoloResultsScreen(score, false); protected override ScoreInfo CreateScore() => score.ScoreInfo; } diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index 133bdcca4a..8db9cdc547 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -2,7 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Linq; +using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -12,8 +12,6 @@ using osu.Framework.Screens; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; -using osu.Game.Online.API.Requests; -using osu.Game.Rulesets; using osu.Game.Scoring; using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Play; @@ -21,7 +19,7 @@ using osuTK; namespace osu.Game.Screens.Ranking { - public class ResultsScreen : OsuScreen + public abstract class ResultsScreen : OsuScreen { protected const float BACKGROUND_BLUR = 20; @@ -38,9 +36,6 @@ namespace osu.Game.Screens.Ranking [Resolved] private IAPIProvider api { get; set; } - [Resolved] - private RulesetStore rulesets { get; set; } - public readonly ScoreInfo Score; private readonly bool allowRetry; @@ -133,22 +128,28 @@ namespace osu.Game.Screens.Ranking { base.LoadComplete(); - var req = new GetScoresRequest(Score.Beatmap, Score.Ruleset); - - req.Success += r => Schedule(() => + var req = FetchScores(scores => Schedule(() => { - foreach (var s in r.Scores.Select(s => s.CreateScoreInfo(rulesets))) + foreach (var s in scores) { if (s.OnlineScoreID == Score.OnlineScoreID) continue; panels.AddScore(s); } - }); + })); - api.Queue(req); + if (req != null) + api.Queue(req); } + /// + /// Performs a fetch/refresh of scores to be displayed. + /// + /// A callback which should be called when fetching is completed. Scheduling is not required. + /// An responsible for the fetch operation. This will be queued and performed automatically. + protected virtual APIRequest FetchScores(Action> scoresCallback) => null; + public override void OnEntering(IScreen last) { base.OnEntering(last); diff --git a/osu.Game/Screens/Ranking/SoloResultsScreen.cs b/osu.Game/Screens/Ranking/SoloResultsScreen.cs new file mode 100644 index 0000000000..2b00748ed8 --- /dev/null +++ b/osu.Game/Screens/Ranking/SoloResultsScreen.cs @@ -0,0 +1,32 @@ +// 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; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Rulesets; +using osu.Game.Scoring; + +namespace osu.Game.Screens.Ranking +{ + public class SoloResultsScreen : ResultsScreen + { + [Resolved] + private RulesetStore rulesets { get; set; } + + public SoloResultsScreen(ScoreInfo score, bool allowRetry = true) + : base(score, allowRetry) + { + } + + protected override APIRequest FetchScores(Action> scoresCallback) + { + var req = new GetScoresRequest(Score.Beatmap, Score.Ruleset); + req.Success += r => scoresCallback?.Invoke(r.Scores.Select(s => s.CreateScoreInfo(rulesets))); + return req; + } + } +} diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index 21ddc5685d..0a4c0e2085 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -37,7 +37,7 @@ namespace osu.Game.Screens.Select } protected void PresentScore(ScoreInfo score) => - FinaliseSelection(score.Beatmap, score.Ruleset, () => this.Push(new ResultsScreen(score))); + FinaliseSelection(score.Beatmap, score.Ruleset, () => this.Push(new SoloResultsScreen(score))); protected override BeatmapDetailArea CreateBeatmapDetailArea() => new PlayBeatmapDetailArea(); From 7e1e26de2a3b3bc8e249ba75b417e4d5955d7c42 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 May 2020 17:00:55 +0900 Subject: [PATCH 0915/1117] Allow HandleMovement by default --- .../Edit/Compose/Components/SelectionHandler.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index 9ecda9fdb8..7ab6340e07 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -74,9 +74,16 @@ namespace osu.Game.Screens.Edit.Compose.Components /// /// Handles the selected s being moved. /// + /// + /// Just returning true is enough to allow updates to take place. + /// Custom implementation is only required if other attributes are to be considered, like changing columns. + /// /// The move event. - /// Whether any s were moved. - public virtual bool HandleMovement(MoveSelectionEvent moveEvent) => false; + /// + /// Whether any s could be moved. + /// Returning true will also propagate StartTime changes provided by the closest . + /// + public virtual bool HandleMovement(MoveSelectionEvent moveEvent) => true; public bool OnPressed(PlatformAction action) { From c07a33b24fc02d33a6dd15da10605d16bb958005 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 26 May 2020 17:31:50 +0900 Subject: [PATCH 0916/1117] Fix ctor accessibility --- osu.Game/Screens/Ranking/ResultsScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index 8db9cdc547..97b56d22eb 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -43,7 +43,7 @@ namespace osu.Game.Screens.Ranking private Drawable bottomPanel; private ScorePanelList panels; - public ResultsScreen(ScoreInfo score, bool allowRetry = true) + protected ResultsScreen(ScoreInfo score, bool allowRetry = true) { Score = score; this.allowRetry = allowRetry; From 6b5b2152991f3ba617b42f274fee646f37753ab7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 May 2020 17:44:47 +0900 Subject: [PATCH 0917/1117] Split out IHasPath from IHasCurve to better define hitobjects --- .../Beatmaps/CatchBeatmapConverter.cs | 2 +- .../Objects/JuiceStream.cs | 2 +- .../Legacy/DistanceObjectPatternGenerator.cs | 2 +- .../Beatmaps/OsuBeatmapConverter.cs | 2 +- osu.Game.Rulesets.Osu/Objects/Slider.cs | 2 +- .../Beatmaps/TaikoBeatmapConverter.cs | 2 +- osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs | 10 +---- .../Formats/LegacyBeatmapDecoderTest.cs | 2 +- .../Beatmaps/Formats/OsuJsonDecoderTest.cs | 2 +- .../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 39 +++++++++++-------- .../Rulesets/Objects/Legacy/ConvertSlider.cs | 2 +- osu.Game/Rulesets/Objects/Types/IHasPath.cs | 13 +++++++ .../{IHasCurve.cs => IHasPathWithRepeats.cs} | 20 +++++----- 13 files changed, 57 insertions(+), 43 deletions(-) create mode 100644 osu.Game/Rulesets/Objects/Types/IHasPath.cs rename osu.Game/Rulesets/Objects/Types/{IHasCurve.cs => IHasPathWithRepeats.cs} (77%) diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs index 90a6e609f0..27a9b63e9a 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps switch (obj) { - case IHasCurve curveData: + case IHasPathWithRepeats curveData: return new JuiceStream { StartTime = obj.StartTime, diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs index d32595c2e1..24090e233a 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs @@ -14,7 +14,7 @@ using osu.Game.Rulesets.Objects.Types; namespace osu.Game.Rulesets.Catch.Objects { - public class JuiceStream : CatchHitObject, IHasCurve + public class JuiceStream : CatchHitObject, IHasPathWithRepeats { /// /// Positional distance that results in a duration of one second, before any speed adjustments. diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index d8d5b67c0e..1bd796511b 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -474,7 +474,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy /// private IList sampleInfoListAt(double time) { - if (!(HitObject is IHasCurve curveData)) + if (!(HitObject is IHasPathWithRepeats curveData)) return HitObject.Samples; double segmentTime = (EndTime - HitObject.StartTime) / spanCount; diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs index 147d74c929..060a3919bd 100644 --- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs +++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs @@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps switch (original) { - case IHasCurve curveData: + case IHasPathWithRepeats curveData: return new Slider { StartTime = original.StartTime, diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 6ba0e1c6aa..713d1a61f8 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -17,7 +17,7 @@ using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Objects { - public class Slider : OsuHitObject, IHasCurve + public class Slider : OsuHitObject, IHasPathWithRepeats { public double EndTime { diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index d324441285..1a47be2282 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -114,7 +114,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps if (!isForCurrentRuleset && tickSpacing > 0 && osuDuration < 2 * speedAdjustedBeatLength) { - List> allSamples = obj is IHasCurve curveData ? curveData.NodeSamples : new List>(new[] { samples }); + List> allSamples = obj is IHasPathWithRepeats curveData ? curveData.NodeSamples : new List>(new[] { samples }); int i = 0; diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs index 7b11bce520..5f52160be1 100644 --- a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs @@ -3,9 +3,7 @@ using osu.Game.Rulesets.Objects.Types; using System; -using System.Collections.Generic; using System.Threading; -using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Judgements; @@ -17,7 +15,7 @@ using osuTK; namespace osu.Game.Rulesets.Taiko.Objects { - public class DrumRoll : TaikoHitObject, IHasCurve + public class DrumRoll : TaikoHitObject, IHasPath { /// /// Drum roll distance that results in a duration of 1 speed-adjusted beat length. @@ -115,11 +113,7 @@ namespace osu.Game.Rulesets.Taiko.Objects double IHasDistance.Distance => Duration * Velocity; - int IHasRepeats.RepeatCount { get => 0; set { } } - - List> IHasRepeats.NodeSamples => new List>(); - - SliderPath IHasCurve.Path + SliderPath IHasPath.Path => new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(1) }, ((IHasDistance)this).Distance / TaikoBeatmapConverter.LEGACY_VELOCITY_MULTIPLIER); #endregion diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index acb30a6277..dab923d75b 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -365,7 +365,7 @@ namespace osu.Game.Tests.Beatmaps.Formats { var hitObjects = decoder.Decode(stream).HitObjects; - var curveData = hitObjects[0] as IHasCurve; + var curveData = hitObjects[0] as IHasPathWithRepeats; var positionData = hitObjects[0] as IHasPosition; Assert.IsNotNull(positionData); diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs index b034e66616..b4c78ce273 100644 --- a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs @@ -95,7 +95,7 @@ namespace osu.Game.Tests.Beatmaps.Formats { var beatmap = decodeAsJson(normal); - var curveData = beatmap.HitObjects[0] as IHasCurve; + var curveData = beatmap.HitObjects[0] as IHasPathWithRepeats; var positionData = beatmap.HitObjects[0] as IHasPosition; Assert.IsNotNull(positionData); diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 7727f25967..d7e83fa471 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -233,9 +233,9 @@ namespace osu.Game.Beatmaps.Formats writer.Write(FormattableString.Invariant($"{(int)getObjectType(hitObject)},")); writer.Write(FormattableString.Invariant($"{(int)toLegacyHitSoundType(hitObject.Samples)},")); - if (hitObject is IHasCurve curveData) + if (hitObject is IHasPathWithRepeats curveData) { - addCurveData(writer, curveData, position); + addPathData(writer, curveData, position); writer.Write(getSampleBank(hitObject.Samples, zeroBanks: true)); } else @@ -263,7 +263,7 @@ namespace osu.Game.Beatmaps.Formats switch (hitObject) { - case IHasCurve _: + case IHasPath _: type |= LegacyHitObjectType.Slider; break; @@ -282,13 +282,13 @@ namespace osu.Game.Beatmaps.Formats return type; } - private void addCurveData(TextWriter writer, IHasCurve curveData, Vector2 position) + private void addPathData(TextWriter writer, IHasPath pathData, Vector2 position) { PathType? lastType = null; - for (int i = 0; i < curveData.Path.ControlPoints.Count; i++) + for (int i = 0; i < pathData.Path.ControlPoints.Count; i++) { - PathControlPoint point = curveData.Path.ControlPoints[i]; + PathControlPoint point = pathData.Path.ControlPoints[i]; if (point.Type.Value != null) { @@ -325,23 +325,28 @@ namespace osu.Game.Beatmaps.Formats if (i != 0) { writer.Write(FormattableString.Invariant($"{position.X + point.Position.Value.X}:{position.Y + point.Position.Value.Y}")); - writer.Write(i != curveData.Path.ControlPoints.Count - 1 ? "|" : ","); + writer.Write(i != pathData.Path.ControlPoints.Count - 1 ? "|" : ","); } } - writer.Write(FormattableString.Invariant($"{curveData.RepeatCount + 1},")); - writer.Write(FormattableString.Invariant($"{curveData.Path.Distance},")); + var curveData = pathData as IHasPathWithRepeats; - for (int i = 0; i < curveData.NodeSamples.Count; i++) - { - writer.Write(FormattableString.Invariant($"{(int)toLegacyHitSoundType(curveData.NodeSamples[i])}")); - writer.Write(i != curveData.NodeSamples.Count - 1 ? "|" : ","); - } + writer.Write(FormattableString.Invariant($"{(curveData?.RepeatCount ?? 0) + 1},")); + writer.Write(FormattableString.Invariant($"{pathData.Path.Distance},")); - for (int i = 0; i < curveData.NodeSamples.Count; i++) + if (curveData != null) { - writer.Write(getSampleBank(curveData.NodeSamples[i], true)); - writer.Write(i != curveData.NodeSamples.Count - 1 ? "|" : ","); + for (int i = 0; i < curveData.NodeSamples.Count; i++) + { + writer.Write(FormattableString.Invariant($"{(int)toLegacyHitSoundType(curveData.NodeSamples[i])}")); + writer.Write(i != curveData.NodeSamples.Count - 1 ? "|" : ","); + } + + for (int i = 0; i < curveData.NodeSamples.Count; i++) + { + writer.Write(getSampleBank(curveData.NodeSamples[i], true)); + writer.Write(i != curveData.NodeSamples.Count - 1 ? "|" : ","); + } } } diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs index 924182b265..73192dc42e 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs @@ -9,7 +9,7 @@ using osu.Game.Beatmaps.ControlPoints; namespace osu.Game.Rulesets.Objects.Legacy { - internal abstract class ConvertSlider : ConvertHitObject, IHasCurve, IHasLegacyLastTickOffset + internal abstract class ConvertSlider : ConvertHitObject, IHasPathWithRepeats, IHasLegacyLastTickOffset { /// /// Scoring distance with a speed-adjusted beat length of 1 second. diff --git a/osu.Game/Rulesets/Objects/Types/IHasPath.cs b/osu.Game/Rulesets/Objects/Types/IHasPath.cs new file mode 100644 index 0000000000..567c24a4a2 --- /dev/null +++ b/osu.Game/Rulesets/Objects/Types/IHasPath.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.Rulesets.Objects.Types +{ + public interface IHasPath : IHasDistance + { + /// + /// The curve. + /// + SliderPath Path { get; } + } +} diff --git a/osu.Game/Rulesets/Objects/Types/IHasCurve.cs b/osu.Game/Rulesets/Objects/Types/IHasPathWithRepeats.cs similarity index 77% rename from osu.Game/Rulesets/Objects/Types/IHasCurve.cs rename to osu.Game/Rulesets/Objects/Types/IHasPathWithRepeats.cs index e98a888bd7..fba0fd7aff 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasCurve.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasPathWithRepeats.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 osuTK; namespace osu.Game.Rulesets.Objects.Types @@ -8,15 +9,16 @@ namespace osu.Game.Rulesets.Objects.Types /// /// A HitObject that has a curve. /// - public interface IHasCurve : IHasDistance, IHasRepeats + public interface IHasPathWithRepeats : IHasPath, IHasRepeats { - /// - /// The curve. - /// - SliderPath Path { get; } } - public static class HasCurveExtensions + [Obsolete("Use IHasPathWithRepeats instead.")] // can be removed 20201126 + public interface IHasCurve : IHasPathWithRepeats + { + } + + public static class HasPathWithRepeatsExtensions { /// /// Computes the position on the curve relative to how much of the has been completed. @@ -24,7 +26,7 @@ namespace osu.Game.Rulesets.Objects.Types /// The curve. /// [0, 1] where 0 is the start time of the and 1 is the end time of the . /// The position on the curve. - public static Vector2 CurvePositionAt(this IHasCurve obj, double progress) + public static Vector2 CurvePositionAt(this IHasPathWithRepeats obj, double progress) => obj.Path.PositionAt(obj.ProgressAt(progress)); /// @@ -33,7 +35,7 @@ namespace osu.Game.Rulesets.Objects.Types /// The curve. /// [0, 1] where 0 is the start time of the and 1 is the end time of the . /// [0, 1] where 0 is the beginning of the curve and 1 is the end of the curve. - public static double ProgressAt(this IHasCurve obj, double progress) + public static double ProgressAt(this IHasPathWithRepeats obj, double progress) { double p = progress * obj.SpanCount() % 1; if (obj.SpanAt(progress) % 2 == 1) @@ -47,7 +49,7 @@ namespace osu.Game.Rulesets.Objects.Types /// The curve. /// [0, 1] where 0 is the beginning of the curve and 1 is the end of the curve. /// [0, SpanCount) where 0 is the first run. - public static int SpanAt(this IHasCurve obj, double progress) + public static int SpanAt(this IHasPathWithRepeats obj, double progress) => (int)(progress * obj.SpanCount()); } } From b8e0a6f12725e464f179f03e53dd9f20625cd635 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 May 2020 12:37:44 +0900 Subject: [PATCH 0918/1117] Move sett from EndTime to Duration --- osu.Game.Rulesets.Catch/Objects/JuiceStream.cs | 10 +++++----- osu.Game.Rulesets.Osu/Objects/Slider.cs | 8 ++++---- .../Objects/Drawables/DrawableSwell.cs | 2 +- .../Gameplay/TestSceneDrawableScrollingRuleset.cs | 6 +++--- .../Objects/Legacy/Catch/ConvertHitObjectParser.cs | 6 +++--- .../Objects/Legacy/Catch/ConvertSpinner.cs | 4 ++-- .../Objects/Legacy/ConvertHitObjectParser.cs | 14 +++++++------- osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs | 6 +++--- .../Objects/Legacy/Mania/ConvertHitObjectParser.cs | 8 ++++---- .../Rulesets/Objects/Legacy/Mania/ConvertHold.cs | 4 ++-- .../Objects/Legacy/Mania/ConvertSpinner.cs | 4 ++-- .../Objects/Legacy/Osu/ConvertHitObjectParser.cs | 6 +++--- .../Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs | 4 ++-- .../Objects/Legacy/Taiko/ConvertHitObjectParser.cs | 6 +++--- .../Objects/Legacy/Taiko/ConvertSpinner.cs | 4 ++-- osu.Game/Rulesets/Objects/Types/IHasEndTime.cs | 6 +++--- .../Timeline/TimelineHitObjectBlueprint.cs | 2 +- 17 files changed, 50 insertions(+), 50 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs index d32595c2e1..7c8d57e689 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs @@ -115,15 +115,15 @@ namespace osu.Game.Rulesets.Catch.Objects } } - public double EndTime + public float EndX => X + this.CurvePositionAt(1).X / CatchPlayfield.BASE_WIDTH; + + public double Duration { - get => StartTime + this.SpanCount() * Path.Distance / Velocity; + get => this.SpanCount() * Path.Distance / Velocity; set => throw new System.NotSupportedException($"Adjust via {nameof(RepeatCount)} instead"); // can be implemented if/when needed. } - public float EndX => X + this.CurvePositionAt(1).X / CatchPlayfield.BASE_WIDTH; - - public double Duration => EndTime - StartTime; + public double EndTime => StartTime + Duration; private readonly SliderPath path = new SliderPath(); diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 6ba0e1c6aa..984a9bf956 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -19,14 +19,14 @@ namespace osu.Game.Rulesets.Osu.Objects { public class Slider : OsuHitObject, IHasCurve { - public double EndTime + public double EndTime => StartTime + this.SpanCount() * Path.Distance / Velocity; + + public double Duration { - get => StartTime + this.SpanCount() * Path.Distance / Velocity; + get => EndTime - StartTime; set => throw new System.NotSupportedException($"Adjust via {nameof(RepeatCount)} instead"); // can be implemented if/when needed. } - public double Duration => EndTime - StartTime; - private readonly Cached endPositionCache = new Cached(); public override Vector2 EndPosition => endPositionCache.IsValid ? endPositionCache.Value : endPositionCache.Value = Position + this.CurvePositionAt(1); diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs index 32f7acadc8..7294587b10 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs @@ -237,7 +237,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables case ArmedState.Miss: case ArmedState.Hit: - using (BeginAbsoluteSequence(Time.Current, true)) + using (BeginDelayedSequence(HitObject.Duration, true)) { this.FadeOut(transition_duration, Easing.Out); bodyContainer.ScaleTo(1.4f, transition_duration); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs index b25b81c9af..08fd849fa6 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs @@ -281,7 +281,7 @@ namespace osu.Game.Tests.Visual.Gameplay yield return new TestHitObject { StartTime = original.StartTime, - EndTime = (original as IHasEndTime)?.EndTime ?? (original.StartTime + 100) + Duration = (original as IHasEndTime)?.Duration ?? 100 }; } } @@ -292,9 +292,9 @@ namespace osu.Game.Tests.Visual.Gameplay private class TestHitObject : ConvertHitObject, IHasEndTime { - public double EndTime { get; set; } + public double EndTime => StartTime + Duration; - public double Duration => EndTime - StartTime; + public double Duration { get; set; } } private class DrawableTestHitObject : DrawableHitObject diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs index 43e8d01297..c10c8dc30f 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs @@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch }; } - protected override HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double endTime) + 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 @@ -65,11 +65,11 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch return new ConvertSpinner { - EndTime = endTime + Duration = duration }; } - protected override HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double endTime) + protected override HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double duration) { return null; } diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs index 9de311c9d7..4b0270064a 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs @@ -10,9 +10,9 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch /// internal sealed class ConvertSpinner : ConvertHitObject, IHasEndTime, IHasXPosition, IHasCombo { - public double EndTime { get; set; } + public double EndTime => StartTime + Duration; - public double Duration => EndTime - StartTime; + public double Duration { get; set; } public float X => 256; // Required for CatchBeatmapConverter diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 9a60a0a75c..d8d90fddfa 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -189,9 +189,9 @@ namespace osu.Game.Rulesets.Objects.Legacy } else if (type.HasFlag(LegacyHitObjectType.Spinner)) { - double endTime = Math.Max(startTime, Parsing.ParseDouble(split[5]) + Offset); + double duration = Math.Max(0, Parsing.ParseDouble(split[5]) + Offset - startTime); - result = CreateSpinner(new Vector2(512, 384) / 2, combo, comboOffset, endTime); + result = CreateSpinner(new Vector2(512, 384) / 2, combo, comboOffset, duration); if (split.Length > 6) readCustomSampleBanks(split[6], bankInfo); @@ -209,7 +209,7 @@ namespace osu.Game.Rulesets.Objects.Legacy readCustomSampleBanks(string.Join(":", ss.Skip(1)), bankInfo); } - result = CreateHold(pos, combo, comboOffset, endTime + Offset); + result = CreateHold(pos, combo, comboOffset, endTime + Offset - startTime); } if (result == null) @@ -321,9 +321,9 @@ namespace osu.Game.Rulesets.Objects.Legacy /// The position of the hit object. /// Whether the hit object creates a new combo. /// When starting a new combo, the offset of the new combo relative to the current one. - /// The spinner end time. + /// The spinner duration. /// The hit object. - protected abstract HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double endTime); + protected abstract HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double duration); /// /// Creates a legacy Hold-type hit object. @@ -331,8 +331,8 @@ namespace osu.Game.Rulesets.Objects.Legacy /// The position of the hit object. /// Whether the hit object creates a new combo. /// When starting a new combo, the offset of the new combo relative to the current one. - /// The hold end time. - protected abstract HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double endTime); + /// The hold end time. + protected abstract HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double duration); private List convertSoundType(LegacyHitSoundType type, SampleBankInfo bankInfo) { diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs index 924182b265..0a3c1b40fd 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs @@ -26,13 +26,13 @@ namespace osu.Game.Rulesets.Objects.Legacy public List> NodeSamples { get; set; } public int RepeatCount { get; set; } - public double EndTime + public double Duration { - get => StartTime + this.SpanCount() * Distance / Velocity; + get => this.SpanCount() * Distance / Velocity; set => throw new System.NotSupportedException($"Adjust via {nameof(RepeatCount)} instead"); // can be implemented if/when needed. } - public double Duration => EndTime - StartTime; + public double EndTime => StartTime + Duration; public double Velocity = 1; diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs index f94c4aaa75..bc64518f40 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs @@ -37,21 +37,21 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania }; } - protected override HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double endTime) + protected override HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double duration) { return new ConvertSpinner { X = position.X, - EndTime = endTime + Duration = duration }; } - protected override HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double endTime) + protected override HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double duration) { return new ConvertHold { X = position.X, - EndTime = endTime + Duration = duration }; } } diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHold.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHold.cs index 1d92d638dd..dcb66163e4 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHold.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHold.cs @@ -9,8 +9,8 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania { public float X { get; set; } - public double EndTime { get; set; } + public double Duration { get; set; } - public double Duration => EndTime - StartTime; + public double EndTime => StartTime + Duration; } } diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSpinner.cs index 7dc13e27cd..b731f7c8d8 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSpinner.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSpinner.cs @@ -10,9 +10,9 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania /// internal sealed class ConvertSpinner : ConvertHitObject, IHasEndTime, IHasXPosition { - public double EndTime { get; set; } + public double Duration { get; set; } - public double Duration => EndTime - StartTime; + public double EndTime => StartTime + Duration; public float X { get; set; } } diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs index b95ec703b6..75ecab0b8f 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs @@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu }; } - protected override HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double endTime) + 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 @@ -66,11 +66,11 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu return new ConvertSpinner { Position = position, - EndTime = endTime + Duration = duration }; } - protected override HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double endTime) + protected override HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double duration) { return null; } diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs index 8b21aab411..a231237077 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs @@ -11,9 +11,9 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu /// internal sealed class ConvertSpinner : ConvertHitObject, IHasEndTime, IHasPosition, IHasCombo { - public double EndTime { get; set; } + public double Duration { get; set; } - public double Duration => EndTime - StartTime; + public double EndTime => StartTime + Duration; public Vector2 Position { get; set; } diff --git a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs index db65a61c90..13e3e84c6a 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs @@ -33,15 +33,15 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko }; } - protected override HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double endTime) + protected override HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double duration) { return new ConvertSpinner { - EndTime = endTime + Duration = duration }; } - protected override HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double endTime) + protected override HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double duration) { return null; } diff --git a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSpinner.cs index 8e28487f2f..0976106ec4 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSpinner.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSpinner.cs @@ -10,8 +10,8 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko /// internal sealed class ConvertSpinner : ConvertHitObject, IHasEndTime { - public double EndTime { get; set; } + public double Duration { get; set; } - public double Duration => EndTime - StartTime; + public double EndTime => StartTime + Duration; } } diff --git a/osu.Game/Rulesets/Objects/Types/IHasEndTime.cs b/osu.Game/Rulesets/Objects/Types/IHasEndTime.cs index bc7103c60d..5eb551e15c 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasEndTime.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasEndTime.cs @@ -13,12 +13,12 @@ namespace osu.Game.Rulesets.Objects.Types /// /// The time at which the HitObject ends. /// - [JsonIgnore] - double EndTime { get; set; } + double EndTime { get; } /// /// The duration of the HitObject. /// - double Duration { get; } + [JsonIgnore] + double Duration { get; set; } } } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index dd2f7a833e..d6fc17f358 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -296,7 +296,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline if (endTimeHitObject.EndTime == snappedTime) return; - endTimeHitObject.EndTime = snappedTime; + endTimeHitObject.Duration = snappedTime - hitObject.StartTime; break; } From cbd563e80b3b82aecf9b84e474197d9c12993b16 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 May 2020 12:38:39 +0900 Subject: [PATCH 0919/1117] Rename to IHasDuration --- .../Beatmaps/CatchBeatmapConverter.cs | 2 +- .../Objects/BananaShower.cs | 2 +- .../TestSceneNotes.cs | 2 +- .../Beatmaps/ManiaBeatmapConverter.cs | 6 ++--- .../Legacy/EndTimeObjectPatternGenerator.cs | 2 +- osu.Game.Rulesets.Mania/Objects/HoldNote.cs | 2 +- .../Beatmaps/OsuBeatmapConverter.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs | 2 +- osu.Game.Rulesets.Osu/Objects/Spinner.cs | 2 +- .../Beatmaps/TaikoBeatmapConverter.cs | 2 +- osu.Game.Rulesets.Taiko/Objects/Swell.cs | 2 +- .../TestSceneDrawableScrollingRuleset.cs | 4 ++-- .../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 6 ++--- osu.Game/Beatmaps/WorkingBeatmap.cs | 2 +- osu.Game/Rulesets/Objects/HitObject.cs | 4 ++-- .../Objects/Legacy/Catch/ConvertSpinner.cs | 2 +- .../Objects/Legacy/Mania/ConvertHold.cs | 2 +- .../Objects/Legacy/Mania/ConvertSpinner.cs | 2 +- .../Objects/Legacy/Osu/ConvertSpinner.cs | 2 +- .../Objects/Legacy/Taiko/ConvertSpinner.cs | 2 +- .../Rulesets/Objects/Types/IHasDistance.cs | 2 +- .../Rulesets/Objects/Types/IHasDuration.cs | 24 +++++++++++++++++++ .../Rulesets/Objects/Types/IHasEndTime.cs | 20 ++++------------ .../Rulesets/Objects/Types/IHasRepeats.cs | 2 +- .../Scrolling/ScrollingHitObjectContainer.cs | 2 +- .../Timeline/TimelineHitObjectBlueprint.cs | 4 ++-- 27 files changed, 60 insertions(+), 48 deletions(-) create mode 100644 osu.Game/Rulesets/Objects/Types/IHasDuration.cs diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs index 90a6e609f0..0b370cf911 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs @@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps LegacyLastTickOffset = (obj as IHasLegacyLastTickOffset)?.LegacyLastTickOffset ?? 0 }.Yield(); - case IHasEndTime endTime: + case IHasDuration endTime: return new BananaShower { StartTime = obj.StartTime, diff --git a/osu.Game.Rulesets.Catch/Objects/BananaShower.cs b/osu.Game.Rulesets.Catch/Objects/BananaShower.cs index 3a0b5ace53..04a995c77e 100644 --- a/osu.Game.Rulesets.Catch/Objects/BananaShower.cs +++ b/osu.Game.Rulesets.Catch/Objects/BananaShower.cs @@ -7,7 +7,7 @@ using osu.Game.Rulesets.Objects.Types; namespace osu.Game.Rulesets.Catch.Objects { - public class BananaShower : CatchHitObject, IHasEndTime + public class BananaShower : CatchHitObject, IHasDuration { public override FruitVisualRepresentation VisualRepresentation => FruitVisualRepresentation.Banana; diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneNotes.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneNotes.cs index ea6a1e2e6a..dd5fd93710 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneNotes.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneNotes.cs @@ -156,7 +156,7 @@ namespace osu.Game.Rulesets.Mania.Tests foreach (var obj in content.OfType()) { - if (!(obj.HitObject is IHasEndTime endTime)) + if (!(obj.HitObject is IHasDuration endTime)) continue; foreach (var nested in obj.NestedHitObjects) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index 1c8116754f..32abf5e7f9 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -54,7 +54,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps } else { - float percentSliderOrSpinner = (float)beatmap.HitObjects.Count(h => h is IHasEndTime) / beatmap.HitObjects.Count; + float percentSliderOrSpinner = (float)beatmap.HitObjects.Count(h => h is IHasDuration) / beatmap.HitObjects.Count; if (percentSliderOrSpinner < 0.2) TargetColumns = 7; else if (percentSliderOrSpinner < 0.3 || roundedCircleSize >= 5) @@ -175,7 +175,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps break; } - case IHasEndTime endTimeData: + case IHasDuration endTimeData: { conversion = new EndTimeObjectPatternGenerator(Random, original, beatmap, originalBeatmap); @@ -231,7 +231,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps var pattern = new Pattern(); - if (HitObject is IHasEndTime endTimeData) + if (HitObject is IHasDuration endTimeData) { pattern.Add(new HoldNote { diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs index 907bed0d65..d5286a3779 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, IBeatmap originalBeatmap) : base(random, hitObject, beatmap, new Pattern(), originalBeatmap) { - endTime = (HitObject as IHasEndTime)?.EndTime ?? 0; + endTime = (HitObject as IHasDuration)?.EndTime ?? 0; } public override IEnumerable Generate() diff --git a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs index e6f722a8a9..a100c9a58e 100644 --- a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mania.Objects /// /// Represents a hit object which requires pressing, holding, and releasing a key. /// - public class HoldNote : ManiaHitObject, IHasEndTime + public class HoldNote : ManiaHitObject, IHasDuration { public double EndTime { diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs index 147d74c929..3490d96b61 100644 --- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs +++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs @@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps TickDistanceMultiplier = beatmap.BeatmapInfo.BeatmapVersion < 8 ? 1f / beatmap.ControlPointInfo.DifficultyPointAt(original.StartTime).SpeedMultiplier : 1 }.Yield(); - case IHasEndTime endTimeData: + case IHasDuration endTimeData: return new Spinner { StartTime = original.StartTime, diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs index 7b1941b7f9..5d191119b9 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs @@ -70,7 +70,7 @@ namespace osu.Game.Rulesets.Osu.Mods break; // already hit or beyond the hittable end time. - if (h.IsHit || (h.HitObject is IHasEndTime hasEnd && time > hasEnd.EndTime)) + if (h.IsHit || (h.HitObject is IHasDuration hasEnd && time > hasEnd.EndTime)) continue; switch (h) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs b/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs index 297a0fea79..3cad52faeb 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs @@ -61,7 +61,7 @@ namespace osu.Game.Rulesets.Osu.Mods } // Keep wiggling sliders and spinners for their duration - if (!(osuObject is IHasEndTime endTime)) + if (!(osuObject is IHasDuration endTime)) return; amountWiggles = (int)(endTime.Duration / wiggle_duration); diff --git a/osu.Game.Rulesets.Osu/Objects/Spinner.cs b/osu.Game.Rulesets.Osu/Objects/Spinner.cs index 0b8d03d118..418375c090 100644 --- a/osu.Game.Rulesets.Osu/Objects/Spinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Spinner.cs @@ -11,7 +11,7 @@ using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Objects { - public class Spinner : OsuHitObject, IHasEndTime + public class Spinner : OsuHitObject, IHasDuration { public double EndTime { diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index d324441285..a4ec7211ca 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -150,7 +150,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps break; } - case IHasEndTime endTimeData: + case IHasDuration endTimeData: { double hitMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, 3, 5, 7.5) * swell_hit_multiplier; diff --git a/osu.Game.Rulesets.Taiko/Objects/Swell.cs b/osu.Game.Rulesets.Taiko/Objects/Swell.cs index 390f8d1f3b..8a63a89951 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Swell.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Swell.cs @@ -10,7 +10,7 @@ using osu.Game.Rulesets.Taiko.Judgements; namespace osu.Game.Rulesets.Taiko.Objects { - public class Swell : TaikoHitObject, IHasEndTime + public class Swell : TaikoHitObject, IHasDuration { public double EndTime { diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs index 08fd849fa6..bd7e894cf8 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs @@ -281,7 +281,7 @@ namespace osu.Game.Tests.Visual.Gameplay yield return new TestHitObject { StartTime = original.StartTime, - Duration = (original as IHasEndTime)?.Duration ?? 100 + Duration = (original as IHasDuration)?.Duration ?? 100 }; } } @@ -290,7 +290,7 @@ namespace osu.Game.Tests.Visual.Gameplay #region HitObject - private class TestHitObject : ConvertHitObject, IHasEndTime + private class TestHitObject : ConvertHitObject, IHasDuration { public double EndTime => StartTime + Duration; diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 7727f25967..a8982238cf 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -240,7 +240,7 @@ namespace osu.Game.Beatmaps.Formats } else { - if (hitObject is IHasEndTime) + if (hitObject is IHasDuration) addEndTimeData(writer, hitObject); writer.Write(getSampleBank(hitObject.Samples)); @@ -267,7 +267,7 @@ namespace osu.Game.Beatmaps.Formats type |= LegacyHitObjectType.Slider; break; - case IHasEndTime _: + case IHasDuration _: if (beatmap.BeatmapInfo.RulesetID == 3) type |= LegacyHitObjectType.Hold; else @@ -347,7 +347,7 @@ namespace osu.Game.Beatmaps.Formats private void addEndTimeData(TextWriter writer, HitObject hitObject) { - var endTimeData = (IHasEndTime)hitObject; + var endTimeData = (IHasDuration)hitObject; var type = getObjectType(hitObject); char suffix = ','; diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 8126311cbd..ac399e37c4 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -63,7 +63,7 @@ namespace osu.Game.Beatmaps length = emptyLength; break; - case IHasEndTime endTime: + case IHasDuration endTime: length = endTime.EndTime + excess_length; break; diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs index 6f9053d7cb..e2cc98813a 100644 --- a/osu.Game/Rulesets/Objects/HitObject.cs +++ b/osu.Game/Rulesets/Objects/HitObject.cs @@ -175,10 +175,10 @@ namespace osu.Game.Rulesets.Objects /// Returns the end time of this object. /// /// - /// This returns the where available, falling back to otherwise. + /// This returns the where available, falling back to otherwise. /// /// The object. /// The end time of this object. - public static double GetEndTime(this HitObject hitObject) => (hitObject as IHasEndTime)?.EndTime ?? hitObject.StartTime; + public static double GetEndTime(this HitObject hitObject) => (hitObject as IHasDuration)?.EndTime ?? hitObject.StartTime; } } diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs index 4b0270064a..014494ec54 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs @@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch /// /// Legacy osu!catch Spinner-type, used for parsing Beatmaps. /// - internal sealed class ConvertSpinner : ConvertHitObject, IHasEndTime, IHasXPosition, IHasCombo + internal sealed class ConvertSpinner : ConvertHitObject, IHasDuration, IHasXPosition, IHasCombo { public double EndTime => StartTime + Duration; diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHold.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHold.cs index dcb66163e4..2fa4766c1d 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHold.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHold.cs @@ -5,7 +5,7 @@ using osu.Game.Rulesets.Objects.Types; namespace osu.Game.Rulesets.Objects.Legacy.Mania { - internal sealed class ConvertHold : ConvertHitObject, IHasXPosition, IHasEndTime + internal sealed class ConvertHold : ConvertHitObject, IHasXPosition, IHasDuration { public float X { get; set; } diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSpinner.cs index b731f7c8d8..c05aaceb9c 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSpinner.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSpinner.cs @@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania /// /// Legacy osu!mania Spinner-type, used for parsing Beatmaps. /// - internal sealed class ConvertSpinner : ConvertHitObject, IHasEndTime, IHasXPosition + internal sealed class ConvertSpinner : ConvertHitObject, IHasDuration, IHasXPosition { public double Duration { get; set; } diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs index a231237077..e9e5ca8c94 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, IHasEndTime, IHasPosition, IHasCombo + internal sealed class ConvertSpinner : ConvertHitObject, IHasDuration, IHasPosition, IHasCombo { public double Duration { get; set; } diff --git a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSpinner.cs index 0976106ec4..1d5ecb1ef3 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSpinner.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSpinner.cs @@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko /// /// Legacy osu!taiko Spinner-type, used for parsing Beatmaps. /// - internal sealed class ConvertSpinner : ConvertHitObject, IHasEndTime + internal sealed class ConvertSpinner : ConvertHitObject, IHasDuration { public double Duration { get; set; } diff --git a/osu.Game/Rulesets/Objects/Types/IHasDistance.cs b/osu.Game/Rulesets/Objects/Types/IHasDistance.cs index e7f552115e..b497ca5da3 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasDistance.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasDistance.cs @@ -6,7 +6,7 @@ namespace osu.Game.Rulesets.Objects.Types /// /// A HitObject that has a positional length. /// - public interface IHasDistance : IHasEndTime + public interface IHasDistance : IHasDuration { /// /// The positional length of the HitObject. diff --git a/osu.Game/Rulesets/Objects/Types/IHasDuration.cs b/osu.Game/Rulesets/Objects/Types/IHasDuration.cs new file mode 100644 index 0000000000..2433f9597e --- /dev/null +++ b/osu.Game/Rulesets/Objects/Types/IHasDuration.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 Newtonsoft.Json; + +namespace osu.Game.Rulesets.Objects.Types +{ + /// + /// A HitObject that ends at a different time than its start time. + /// + public interface IHasDuration + { + /// + /// The time at which the HitObject ends. + /// + double EndTime { get; } + + /// + /// The duration of the HitObject. + /// + [JsonIgnore] + double Duration { get; set; } + } +} diff --git a/osu.Game/Rulesets/Objects/Types/IHasEndTime.cs b/osu.Game/Rulesets/Objects/Types/IHasEndTime.cs index 5eb551e15c..7395223c7e 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasEndTime.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasEndTime.cs @@ -1,24 +1,12 @@ -// 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 Newtonsoft.Json; +using System; namespace osu.Game.Rulesets.Objects.Types { - /// - /// A HitObject that ends at a different time than its start time. - /// - public interface IHasEndTime + [Obsolete("Use IHasDuration instead.")] // can be removed 20201126 + public interface IHasEndTime : IHasDuration { - /// - /// The time at which the HitObject ends. - /// - double EndTime { get; } - - /// - /// The duration of the HitObject. - /// - [JsonIgnore] - double Duration { get; set; } } } diff --git a/osu.Game/Rulesets/Objects/Types/IHasRepeats.cs b/osu.Game/Rulesets/Objects/Types/IHasRepeats.cs index 256b1f3963..7a3fb16196 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasRepeats.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasRepeats.cs @@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Objects.Types /// /// A HitObject that spans some length. /// - public interface IHasRepeats : IHasEndTime + public interface IHasRepeats : IHasDuration { /// /// The amount of times the HitObject repeats. diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index c817d84d5c..0dc3324559 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -270,7 +270,7 @@ namespace osu.Game.Rulesets.UI.Scrolling // Cant use AddOnce() since the delegate is re-constructed every invocation private void computeInitialStateRecursive(DrawableHitObject hitObject) => hitObject.Schedule(() => { - if (hitObject.HitObject is IHasEndTime e) + if (hitObject.HitObject is IHasDuration e) { switch (direction.Value) { diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index d6fc17f358..b95b3842b3 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -72,7 +72,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline shadowComponents.Add(circle); - if (hitObject is IHasEndTime) + if (hitObject is IHasDuration) { DragBar dragBarUnderlay; Container extensionBar; @@ -290,7 +290,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline repeatHitObject.RepeatCount = proposedCount; break; - case IHasEndTime endTimeHitObject: + case IHasDuration endTimeHitObject: var snappedTime = Math.Max(hitObject.StartTime, beatSnapProvider.SnapTime(time)); if (endTimeHitObject.EndTime == snappedTime) From f5c974dd897b0a7006b944e0103e4158d3d7667a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 May 2020 13:40:16 +0900 Subject: [PATCH 0920/1117] Hide non-alive selection blueprints by default --- .../Edit/Blueprints/OsuSelectionBlueprint.cs | 2 ++ osu.Game/Rulesets/Edit/OverlaySelectionBlueprint.cs | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs index b0e13808a5..8dd550bb96 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs @@ -12,6 +12,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints { protected new T HitObject => (T)DrawableObject.HitObject; + protected override bool AlwaysShowWhenSelected => true; + protected OsuSelectionBlueprint(DrawableHitObject drawableObject) : base(drawableObject) { diff --git a/osu.Game/Rulesets/Edit/OverlaySelectionBlueprint.cs b/osu.Game/Rulesets/Edit/OverlaySelectionBlueprint.cs index 8202d3a1d1..75200e3027 100644 --- a/osu.Game/Rulesets/Edit/OverlaySelectionBlueprint.cs +++ b/osu.Game/Rulesets/Edit/OverlaySelectionBlueprint.cs @@ -15,7 +15,12 @@ namespace osu.Game.Rulesets.Edit /// public readonly DrawableHitObject DrawableObject; - protected override bool ShouldBeAlive => (DrawableObject.IsAlive && DrawableObject.IsPresent) || State == SelectionState.Selected; + /// + /// Whether the blueprint should be shown even when the is not alive. + /// + protected virtual bool AlwaysShowWhenSelected => false; + + protected override bool ShouldBeAlive => (DrawableObject.IsAlive && DrawableObject.IsPresent) || (AlwaysShowWhenSelected && State == SelectionState.Selected); protected OverlaySelectionBlueprint(DrawableHitObject drawableObject) : base(drawableObject.HitObject) From f989f1aa0034d6283ee3d49169316a80713c0a55 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 May 2020 16:08:47 +0900 Subject: [PATCH 0921/1117] Change event flow to avoid firing store delete events on update --- osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs | 4 ++-- osu.Game/Database/ArchiveModelManager.cs | 8 ++++---- osu.Game/Database/IModelManager.cs | 2 +- osu.Game/Database/MutableDatabaseBackedStore.cs | 16 +++++++++++----- osu.Game/Online/DownloadTrackingComposite.cs | 8 ++++---- osu.Game/OsuGameBase.cs | 2 +- osu.Game/Overlays/MusicController.cs | 12 ++++++------ .../Overlays/Settings/Sections/SkinSection.cs | 10 +++++----- .../Multi/Match/Components/ReadyButton.cs | 8 ++++---- osu.Game/Screens/Multi/Match/MatchSubScreen.cs | 8 ++++---- osu.Game/Screens/Select/BeatmapCarousel.cs | 8 ++++---- osu.Game/Screens/Select/Carousel/TopLocalRank.cs | 6 +++--- 12 files changed, 49 insertions(+), 43 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 43fab186aa..5eb11a3264 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -156,7 +156,7 @@ namespace osu.Game.Tests.Beatmaps.IO var manager = osu.Dependencies.Get(); // ReSharper disable once AccessToModifiedClosure - manager.ItemAdded.BindValueChanged(_ => Interlocked.Increment(ref itemAddRemoveFireCount)); + manager.ItemUpdated.BindValueChanged(_ => Interlocked.Increment(ref itemAddRemoveFireCount)); manager.ItemRemoved.BindValueChanged(_ => Interlocked.Increment(ref itemAddRemoveFireCount)); var imported = await LoadOszIntoOsu(osu); @@ -166,7 +166,7 @@ namespace osu.Game.Tests.Beatmaps.IO imported.Hash += "-changed"; manager.Update(imported); - Assert.AreEqual(0, itemAddRemoveFireCount -= 2); + Assert.AreEqual(0, itemAddRemoveFireCount -= 1); checkBeatmapSetCount(osu, 1); checkBeatmapCount(osu, 12); diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index f21f708f95..ae55a7b14a 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -55,12 +55,12 @@ namespace osu.Game.Database public Action PostNotification { protected get; set; } /// - /// Fired when a new becomes available in the database. + /// Fired when a new or updated becomes available in the database. /// This is not guaranteed to run on the update thread. /// - public IBindable> ItemAdded => itemAdded; + public IBindable> ItemUpdated => itemUpdated; - private readonly Bindable> itemAdded = new Bindable>(); + private readonly Bindable> itemUpdated = new Bindable>(); /// /// Fired when a is removed from the database. @@ -90,7 +90,7 @@ namespace osu.Game.Database ContextFactory = contextFactory; ModelStore = modelStore; - ModelStore.ItemAdded += item => handleEvent(() => itemAdded.Value = new WeakReference(item)); + ModelStore.ItemUpdated += item => handleEvent(() => itemUpdated.Value = new WeakReference(item)); ModelStore.ItemRemoved += item => handleEvent(() => itemRemoved.Value = new WeakReference(item)); exportStorage = storage.GetStorageForDirectory("exports"); diff --git a/osu.Game/Database/IModelManager.cs b/osu.Game/Database/IModelManager.cs index 852b385798..7f7e5565f1 100644 --- a/osu.Game/Database/IModelManager.cs +++ b/osu.Game/Database/IModelManager.cs @@ -13,7 +13,7 @@ namespace osu.Game.Database public interface IModelManager where TModel : class { - IBindable> ItemAdded { get; } + IBindable> ItemUpdated { get; } IBindable> ItemRemoved { get; } } diff --git a/osu.Game/Database/MutableDatabaseBackedStore.cs b/osu.Game/Database/MutableDatabaseBackedStore.cs index 4ca1eef989..c9d0c4bc41 100644 --- a/osu.Game/Database/MutableDatabaseBackedStore.cs +++ b/osu.Game/Database/MutableDatabaseBackedStore.cs @@ -16,7 +16,14 @@ namespace osu.Game.Database public abstract class MutableDatabaseBackedStore : DatabaseBackedStore where T : class, IHasPrimaryKey, ISoftDelete { - public event Action ItemAdded; + /// + /// Fired when an item was added or updated. + /// + public event Action ItemUpdated; + + /// + /// Fired when an item was removed. + /// public event Action ItemRemoved; protected MutableDatabaseBackedStore(IDatabaseContextFactory contextFactory, Storage storage = null) @@ -41,7 +48,7 @@ namespace osu.Game.Database context.Attach(item); } - ItemAdded?.Invoke(item); + ItemUpdated?.Invoke(item); } /// @@ -53,8 +60,7 @@ namespace osu.Game.Database using (var usage = ContextFactory.GetForWrite()) usage.Context.Update(item); - ItemRemoved?.Invoke(item); - ItemAdded?.Invoke(item); + ItemUpdated?.Invoke(item); } /// @@ -91,7 +97,7 @@ namespace osu.Game.Database item.DeletePending = false; } - ItemAdded?.Invoke(item); + ItemUpdated?.Invoke(item); return true; } diff --git a/osu.Game/Online/DownloadTrackingComposite.cs b/osu.Game/Online/DownloadTrackingComposite.cs index 47de7d75ed..5d9cf612bb 100644 --- a/osu.Game/Online/DownloadTrackingComposite.cs +++ b/osu.Game/Online/DownloadTrackingComposite.cs @@ -34,7 +34,7 @@ namespace osu.Game.Online Model.Value = model; } - private IBindable> managerAdded; + private IBindable> managedUpdated; private IBindable> managerRemoved; private IBindable>> managerDownloadBegan; private IBindable>> managerDownloadFailed; @@ -56,8 +56,8 @@ namespace osu.Game.Online managerDownloadBegan.BindValueChanged(downloadBegan); managerDownloadFailed = manager.DownloadFailed.GetBoundCopy(); managerDownloadFailed.BindValueChanged(downloadFailed); - managerAdded = manager.ItemAdded.GetBoundCopy(); - managerAdded.BindValueChanged(itemAdded); + managedUpdated = manager.ItemUpdated.GetBoundCopy(); + managedUpdated.BindValueChanged(itemUpdated); managerRemoved = manager.ItemRemoved.GetBoundCopy(); managerRemoved.BindValueChanged(itemRemoved); } @@ -128,7 +128,7 @@ namespace osu.Game.Online private void onRequestFailure(Exception e) => Schedule(() => attachDownload(null)); - private void itemAdded(ValueChangedEvent> weakItem) + private void itemUpdated(ValueChangedEvent> weakItem) { if (weakItem.NewValue.TryGetTarget(out var item)) setDownloadStateFromManager(item, DownloadState.LocallyAvailable); diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 453587df18..e7446975b9 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -192,7 +192,7 @@ namespace osu.Game ScoreManager.Delete(getBeatmapScores(item), true); }); - BeatmapManager.ItemAdded.BindValueChanged(i => + BeatmapManager.ItemUpdated.BindValueChanged(i => { if (i.NewValue.TryGetTarget(out var item)) ScoreManager.Undelete(getBeatmapScores(item), true); diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 35f3cb0e25..92cf490be2 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -60,14 +60,14 @@ namespace osu.Game.Overlays [Resolved(canBeNull: true)] private OnScreenDisplay onScreenDisplay { get; set; } - private IBindable> managerAdded; + private IBindable> managerUpdated; private IBindable> managerRemoved; [BackgroundDependencyLoader] private void load() { - managerAdded = beatmaps.ItemAdded.GetBoundCopy(); - managerAdded.BindValueChanged(beatmapAdded); + managerUpdated = beatmaps.ItemUpdated.GetBoundCopy(); + managerUpdated.BindValueChanged(beatmapUpdated); managerRemoved = beatmaps.ItemRemoved.GetBoundCopy(); managerRemoved.BindValueChanged(beatmapRemoved); @@ -98,14 +98,14 @@ namespace osu.Game.Overlays /// public bool IsPlaying => current?.Track.IsRunning ?? false; - private void beatmapAdded(ValueChangedEvent> weakSet) + private void beatmapUpdated(ValueChangedEvent> weakSet) { if (weakSet.NewValue.TryGetTarget(out var set)) { Schedule(() => { - if (!beatmapSets.Contains(set)) - beatmapSets.Add(set); + beatmapSets.Remove(set); + beatmapSets.Add(set); }); } } diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index b84b9fec37..04390a1193 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -32,7 +32,7 @@ namespace osu.Game.Overlays.Settings.Sections [Resolved] private SkinManager skins { get; set; } - private IBindable> managerAdded; + private IBindable> managerUpdated; private IBindable> managerRemoved; [BackgroundDependencyLoader] @@ -73,8 +73,8 @@ namespace osu.Game.Overlays.Settings.Sections }, }; - managerAdded = skins.ItemAdded.GetBoundCopy(); - managerAdded.BindValueChanged(itemAdded); + managerUpdated = skins.ItemUpdated.GetBoundCopy(); + managerUpdated.BindValueChanged(itemUpdated); managerRemoved = skins.ItemRemoved.GetBoundCopy(); managerRemoved.BindValueChanged(itemRemoved); @@ -92,10 +92,10 @@ namespace osu.Game.Overlays.Settings.Sections dropdownBindable.BindValueChanged(skin => configBindable.Value = skin.NewValue.ID); } - private void itemAdded(ValueChangedEvent> weakItem) + private void itemUpdated(ValueChangedEvent> weakItem) { if (weakItem.NewValue.TryGetTarget(out var item)) - Schedule(() => skinDropdown.Items = skinDropdown.Items.Append(item).ToArray()); + Schedule(() => skinDropdown.Items = skinDropdown.Items.Where(i => !i.Equals(item)).Append(item).ToArray()); } private void itemRemoved(ValueChangedEvent> weakItem) diff --git a/osu.Game/Screens/Multi/Match/Components/ReadyButton.cs b/osu.Game/Screens/Multi/Match/Components/ReadyButton.cs index 4420b2d58a..e1f86fcc97 100644 --- a/osu.Game/Screens/Multi/Match/Components/ReadyButton.cs +++ b/osu.Game/Screens/Multi/Match/Components/ReadyButton.cs @@ -32,14 +32,14 @@ namespace osu.Game.Screens.Multi.Match.Components Text = "Start"; } - private IBindable> managerAdded; + private IBindable> managerUpdated; private IBindable> managerRemoved; [BackgroundDependencyLoader] private void load(OsuColour colours) { - managerAdded = beatmaps.ItemAdded.GetBoundCopy(); - managerAdded.BindValueChanged(beatmapAdded); + managerUpdated = beatmaps.ItemUpdated.GetBoundCopy(); + managerUpdated.BindValueChanged(beatmapUpdated); managerRemoved = beatmaps.ItemRemoved.GetBoundCopy(); managerRemoved.BindValueChanged(beatmapRemoved); @@ -61,7 +61,7 @@ namespace osu.Game.Screens.Multi.Match.Components hasBeatmap = beatmaps.QueryBeatmap(b => b.OnlineBeatmapID == beatmapId) != null; } - private void beatmapAdded(ValueChangedEvent> weakSet) + private void beatmapUpdated(ValueChangedEvent> weakSet) { if (weakSet.NewValue.TryGetTarget(out var set)) { diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs index caa547ac72..e1d72d9600 100644 --- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs @@ -50,7 +50,7 @@ namespace osu.Game.Screens.Multi.Match private LeaderboardChatDisplay leaderboardChatDisplay; private MatchSettingsOverlay settingsOverlay; - private IBindable> managerAdded; + private IBindable> managerUpdated; public MatchSubScreen(Room room) { @@ -183,8 +183,8 @@ namespace osu.Game.Screens.Multi.Match SelectedItem.BindValueChanged(_ => Scheduler.AddOnce(selectedItemChanged)); SelectedItem.Value = playlist.FirstOrDefault(); - managerAdded = beatmapManager.ItemAdded.GetBoundCopy(); - managerAdded.BindValueChanged(beatmapAdded); + managerUpdated = beatmapManager.ItemUpdated.GetBoundCopy(); + managerUpdated.BindValueChanged(beatmapUpdated); } public override bool OnExiting(IScreen next) @@ -217,7 +217,7 @@ namespace osu.Game.Screens.Multi.Match Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap); } - private void beatmapAdded(ValueChangedEvent> weakSet) + private void beatmapUpdated(ValueChangedEvent> weakSet) { Schedule(() => { diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index f23e1b1ef2..2d714d1794 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -131,7 +131,7 @@ namespace osu.Game.Screens.Select private CarouselRoot root; - private IBindable> itemAdded; + private IBindable> itemUpdated; private IBindable> itemRemoved; private IBindable> itemHidden; private IBindable> itemRestored; @@ -166,8 +166,8 @@ namespace osu.Game.Screens.Select RightClickScrollingEnabled.ValueChanged += enabled => scroll.RightMouseScrollbar = enabled.NewValue; RightClickScrollingEnabled.TriggerChange(); - itemAdded = beatmaps.ItemAdded.GetBoundCopy(); - itemAdded.BindValueChanged(beatmapAdded); + itemUpdated = beatmaps.ItemUpdated.GetBoundCopy(); + itemUpdated.BindValueChanged(beatmapUpdated); itemRemoved = beatmaps.ItemRemoved.GetBoundCopy(); itemRemoved.BindValueChanged(beatmapRemoved); itemHidden = beatmaps.BeatmapHidden.GetBoundCopy(); @@ -582,7 +582,7 @@ namespace osu.Game.Screens.Select RemoveBeatmapSet(item); } - private void beatmapAdded(ValueChangedEvent> weakItem) + private void beatmapUpdated(ValueChangedEvent> weakItem) { if (weakItem.NewValue.TryGetTarget(out var item)) UpdateBeatmapSet(item); diff --git a/osu.Game/Screens/Select/Carousel/TopLocalRank.cs b/osu.Game/Screens/Select/Carousel/TopLocalRank.cs index aed25787b0..3ad57c1cb0 100644 --- a/osu.Game/Screens/Select/Carousel/TopLocalRank.cs +++ b/osu.Game/Screens/Select/Carousel/TopLocalRank.cs @@ -28,7 +28,7 @@ namespace osu.Game.Screens.Select.Carousel [Resolved] private IAPIProvider api { get; set; } - private IBindable> itemAdded; + private IBindable> itemUpdated; private IBindable> itemRemoved; public TopLocalRank(BeatmapInfo beatmap) @@ -40,8 +40,8 @@ namespace osu.Game.Screens.Select.Carousel [BackgroundDependencyLoader] private void load() { - itemAdded = scores.ItemAdded.GetBoundCopy(); - itemAdded.BindValueChanged(scoreChanged); + itemUpdated = scores.ItemUpdated.GetBoundCopy(); + itemUpdated.BindValueChanged(scoreChanged); itemRemoved = scores.ItemRemoved.GetBoundCopy(); itemRemoved.BindValueChanged(scoreChanged); From 9a060cfb3acfac28816eb68f245e365153a9af46 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 May 2020 20:44:15 +0900 Subject: [PATCH 0922/1117] Allow drag selections to occur from outside the playfield --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 1 + osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 3453bfbf63..99759bde3d 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -102,6 +102,7 @@ namespace osu.Game.Rulesets.Edit { Name = "Content", RelativeSizeAxes = Axes.Both, + Masking = true, Children = new Drawable[] { // layers below playfield diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index cc417bbb10..d07cffff0c 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -44,6 +44,8 @@ namespace osu.Game.Screens.Edit.Compose.Components private readonly BindableList selectedHitObjects = new BindableList(); + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; + [Resolved(canBeNull: true)] private IPositionSnapProvider snapProvider { get; set; } From 919ff92d15775e63bbb74083a3ce05b61ac53707 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 May 2020 22:56:12 +0900 Subject: [PATCH 0923/1117] Remove unused resolved composer --- .../Edit/Blueprints/HoldNotePlacementBlueprint.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs index 500b26917d..b5ec1e1a2a 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs @@ -20,9 +20,6 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints private readonly EditNotePiece headPiece; private readonly EditNotePiece tailPiece; - [Resolved] - private IManiaHitObjectComposer composer { get; set; } - [Resolved] private IScrollingInfo scrollingInfo { get; set; } From 6be5917eb0c232d5ad34843736fc7c4781c36d02 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 May 2020 23:15:16 +0900 Subject: [PATCH 0924/1117] Remove necessity for custom mania interface caching --- .../ManiaPlacementBlueprintTestScene.cs | 4 +- .../ManiaSelectionBlueprintTestScene.cs | 4 +- .../TestSceneManiaBeatSnapGrid.cs | 60 ++++++++++++++++++- .../Blueprints/ManiaSelectionBlueprint.cs | 3 - .../Edit/IManiaHitObjectComposer.cs | 12 ---- .../Edit/ManiaBeatSnapGrid.cs | 6 +- .../Edit/ManiaHitObjectComposer.cs | 5 +- .../Edit/ManiaSelectionHandler.cs | 9 ++- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 6 +- 9 files changed, 76 insertions(+), 33 deletions(-) delete mode 100644 osu.Game.Rulesets.Mania/Edit/IManiaHitObjectComposer.cs diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestScene.cs b/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestScene.cs index 1119a66f63..0fe4a3c669 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestScene.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestScene.cs @@ -8,7 +8,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Timing; using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Mania.Edit; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Mods; @@ -19,8 +18,7 @@ using osuTK.Graphics; namespace osu.Game.Rulesets.Mania.Tests { - [Cached(Type = typeof(IManiaHitObjectComposer))] - public abstract class ManiaPlacementBlueprintTestScene : PlacementBlueprintTestScene, IManiaHitObjectComposer + public abstract class ManiaPlacementBlueprintTestScene : PlacementBlueprintTestScene { private readonly Column column; diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaSelectionBlueprintTestScene.cs b/osu.Game.Rulesets.Mania.Tests/ManiaSelectionBlueprintTestScene.cs index 35fe596e98..149f6582ab 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaSelectionBlueprintTestScene.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaSelectionBlueprintTestScene.cs @@ -4,15 +4,13 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Timing; -using osu.Game.Rulesets.Mania.Edit; using osu.Game.Rulesets.Mania.UI; using osu.Game.Tests.Visual; using osuTK.Graphics; namespace osu.Game.Rulesets.Mania.Tests { - [Cached(Type = typeof(IManiaHitObjectComposer))] - public abstract class ManiaSelectionBlueprintTestScene : SelectionBlueprintTestScene, IManiaHitObjectComposer + public abstract class ManiaSelectionBlueprintTestScene : SelectionBlueprintTestScene { [Cached(Type = typeof(IAdjustableClock))] private readonly IAdjustableClock clock = new StopwatchClock(); diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaBeatSnapGrid.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaBeatSnapGrid.cs index ce9546415f..639be0bc11 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaBeatSnapGrid.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaBeatSnapGrid.cs @@ -2,23 +2,27 @@ // 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.Graphics; using osu.Framework.Input.Events; using osu.Framework.Timing; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Configuration; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Edit; using osu.Game.Rulesets.Mania.UI; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Screens.Edit; using osu.Game.Tests.Visual; +using osuTK; namespace osu.Game.Rulesets.Mania.Tests { - [Cached(typeof(IManiaHitObjectComposer))] - public class TestSceneManiaBeatSnapGrid : EditorClockTestScene, IManiaHitObjectComposer + public class TestSceneManiaBeatSnapGrid : EditorClockTestScene { [Cached(typeof(IScrollingInfo))] private ScrollingTestContainer.TestScrollingInfo scrollingInfo = new ScrollingTestContainer.TestScrollingInfo(); @@ -50,7 +54,10 @@ namespace osu.Game.Rulesets.Mania.Tests { Clock = new FramedClock(new StopwatchClock()) }, - beatSnapGrid = new ManiaBeatSnapGrid() + new TestHitObjectComposer(Playfield) + { + Child = beatSnapGrid = new ManiaBeatSnapGrid() + } }; } @@ -67,4 +74,51 @@ namespace osu.Game.Rulesets.Mania.Tests public ManiaPlayfield Playfield { get; } } + + public class TestHitObjectComposer : HitObjectComposer + { + public override Playfield Playfield { get; } + public override IEnumerable HitObjects => Enumerable.Empty(); + public override bool CursorInPlacementArea => false; + + public TestHitObjectComposer(Playfield playfield) + { + Playfield = playfield; + } + + public Drawable Child + { + set => InternalChild = value; + } + + public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) + { + throw new System.NotImplementedException(); + } + + public override float GetBeatSnapDistanceAt(double referenceTime) + { + throw new System.NotImplementedException(); + } + + public override float DurationToDistance(double referenceTime, double duration) + { + throw new System.NotImplementedException(); + } + + public override double DistanceToDuration(double referenceTime, float distance) + { + throw new System.NotImplementedException(); + } + + public override double GetSnappedDurationFromDistance(double referenceTime, float distance) + { + throw new System.NotImplementedException(); + } + + public override float GetSnappedDistanceFromDistance(double referenceTime, float distance) + { + throw new System.NotImplementedException(); + } + } } diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs index 0089a9fbee..384f49d9b2 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs @@ -18,9 +18,6 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints [Resolved] private IScrollingInfo scrollingInfo { get; set; } - [Resolved] - private IManiaHitObjectComposer composer { get; set; } - protected ManiaSelectionBlueprint(DrawableHitObject drawableObject) : base(drawableObject) { diff --git a/osu.Game.Rulesets.Mania/Edit/IManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/IManiaHitObjectComposer.cs deleted file mode 100644 index 3818d0e15d..0000000000 --- a/osu.Game.Rulesets.Mania/Edit/IManiaHitObjectComposer.cs +++ /dev/null @@ -1,12 +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.Mania.UI; - -namespace osu.Game.Rulesets.Mania.Edit -{ - public interface IManiaHitObjectComposer - { - ManiaPlayfield Playfield { get; } - } -} diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs index b5b6c08fca..2028cae9a5 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs @@ -11,6 +11,8 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps; using osu.Game.Graphics; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI.Scrolling; @@ -63,9 +65,9 @@ namespace osu.Game.Rulesets.Mania.Edit private (double start, double end)? selectionTimeRange; [BackgroundDependencyLoader] - private void load(IManiaHitObjectComposer composer) + private void load(HitObjectComposer composer) { - foreach (var stage in composer.Playfield.Stages) + foreach (var stage in ((ManiaPlayfield)composer.Playfield).Stages) { foreach (var column in stage.Columns) { diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index 73cbadc97c..10d344242c 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -20,8 +20,7 @@ using osuTK; namespace osu.Game.Rulesets.Mania.Edit { - [Cached(Type = typeof(IManiaHitObjectComposer))] - public class ManiaHitObjectComposer : HitObjectComposer, IManiaHitObjectComposer + public class ManiaHitObjectComposer : HitObjectComposer { private DrawableManiaEditRuleset drawableRuleset; private ManiaBeatSnapGrid beatSnapGrid; @@ -50,7 +49,7 @@ namespace osu.Game.Rulesets.Mania.Edit protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); - public ManiaPlayfield Playfield => ((ManiaPlayfield)drawableRuleset.Playfield); + public new ManiaPlayfield Playfield => ((ManiaPlayfield)drawableRuleset.Playfield); public IScrollingInfo ScrollingInfo => drawableRuleset.ScrollingInfo; diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs index 4ea71652bc..65f40d7d0a 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs @@ -4,6 +4,7 @@ using System; using System.Linq; using osu.Framework.Allocation; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Mania.Edit.Blueprints; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.UI.Scrolling; @@ -17,7 +18,7 @@ namespace osu.Game.Rulesets.Mania.Edit private IScrollingInfo scrollingInfo { get; set; } [Resolved] - private IManiaHitObjectComposer composer { get; set; } + private HitObjectComposer composer { get; set; } public override bool HandleMovement(MoveSelectionEvent moveEvent) { @@ -31,7 +32,9 @@ namespace osu.Game.Rulesets.Mania.Edit private void performColumnMovement(int lastColumn, MoveSelectionEvent moveEvent) { - var currentColumn = composer.Playfield.GetColumnByPosition(moveEvent.ScreenSpacePosition); + var maniaPlayfield = ((ManiaHitObjectComposer)composer).Playfield; + + var currentColumn = maniaPlayfield.GetColumnByPosition(moveEvent.ScreenSpacePosition); if (currentColumn == null) return; @@ -50,7 +53,7 @@ namespace osu.Game.Rulesets.Mania.Edit maxColumn = obj.Column; } - columnDelta = Math.Clamp(columnDelta, -minColumn, composer.Playfield.TotalColumns - 1 - maxColumn); + columnDelta = Math.Clamp(columnDelta, -minColumn, maniaPlayfield.TotalColumns - 1 - maxColumn); foreach (var obj in SelectedHitObjects.OfType()) obj.Column += columnDelta; diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 3453bfbf63..0a2df64dde 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -48,6 +48,8 @@ namespace osu.Game.Rulesets.Edit protected ComposeBlueprintContainer BlueprintContainer { get; private set; } + public override Playfield Playfield => drawableRulesetWrapper.Playfield; + private DrawableEditRulesetWrapper drawableRulesetWrapper; protected readonly Container LayerBelowRuleset = new Container { RelativeSizeAxes = Axes.Both }; @@ -260,11 +262,13 @@ namespace osu.Game.Rulesets.Edit [Cached(typeof(IPositionSnapProvider))] public abstract class HitObjectComposer : CompositeDrawable, IPositionSnapProvider { - internal HitObjectComposer() + protected HitObjectComposer() { RelativeSizeAxes = Axes.Both; } + public abstract Playfield Playfield { get; } + /// /// All the s. /// From e4de20f0a5b6721519d6fa1baa4b2b2daf9222e6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 May 2020 11:54:27 +0900 Subject: [PATCH 0925/1117] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index b7d08fb120..7ea1f3140b 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index d5017a436f..3d2a4f3081 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -24,7 +24,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 19a36f1e1f..8a7f75b515 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -80,7 +80,7 @@ - + From 912c999f4051a259c58c2dc8ec265193bb8f8bd6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 May 2020 19:05:35 +0900 Subject: [PATCH 0926/1117] Fix minor typo in OsuGameBase --- osu.Game/OsuGameBase.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index e7446975b9..5e44562144 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -229,8 +229,8 @@ namespace osu.Game FileStore.Cleanup(); - if (API is APIAccess apiAcces) - AddInternal(apiAcces); + if (API is APIAccess apiAccess) + AddInternal(apiAccess); AddInternal(RulesetConfigCache); GlobalActionContainer globalBinding; From a55ce261307b560ab303db9cbcf183e6c50bca43 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 28 May 2020 20:46:17 +0900 Subject: [PATCH 0927/1117] Allow null score --- .../Visual/Ranking/TestSceneScorePanelList.cs | 38 ++++++++++++++++++- osu.Game/Screens/Ranking/ScorePanelList.cs | 7 +++- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs index b32b3afbda..a204d2bcbc 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs @@ -9,10 +9,11 @@ using osu.Framework.Utils; using osu.Game.Rulesets.Osu; using osu.Game.Scoring; using osu.Game.Screens.Ranking; +using osuTK.Input; namespace osu.Game.Tests.Visual.Ranking { - public class TestSceneScorePanelList : OsuTestScene + public class TestSceneScorePanelList : OsuManualInputManagerTestScene { private ScoreInfo initialScore; private ScorePanelList list; @@ -72,6 +73,41 @@ namespace osu.Game.Tests.Visual.Ranking assertPanelCentred(); } + [Test] + public void TestNullScore() + { + AddStep("create panel with null score", () => + { + Child = list = new ScorePanelList(null) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }; + }); + + AddStep("add many panels", () => + { + for (int i = 0; i < 20; i++) + list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo) { TotalScore = initialScore.TotalScore - i - 1 }); + + for (int i = 0; i < 20; i++) + list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo) { TotalScore = initialScore.TotalScore + i + 1 }); + }); + + AddWaitStep("wait for panel animation", 5); + + AddAssert("no panel selected", () => list.ChildrenOfType().All(p => p.State != PanelState.Expanded)); + + AddStep("expand second panel", () => + { + var expandedPanel = list.ChildrenOfType().OrderBy(p => p.DrawPosition.X).ElementAt(1); + InputManager.MoveMouseTo(expandedPanel); + InputManager.Click(MouseButton.Left); + }); + + assertPanelCentred(); + } + private void assertPanelCentred() => AddUntilStep("expanded panel centred", () => { var expandedPanel = list.ChildrenOfType().Single(p => p.State == PanelState.Expanded); diff --git a/osu.Game/Screens/Ranking/ScorePanelList.cs b/osu.Game/Screens/Ranking/ScorePanelList.cs index df2c66203b..a30d911f04 100644 --- a/osu.Game/Screens/Ranking/ScorePanelList.cs +++ b/osu.Game/Screens/Ranking/ScorePanelList.cs @@ -45,8 +45,11 @@ namespace osu.Game.Screens.Ranking } }; - AddScore(initialScore); - presentScore(initialScore); + if (initialScore != null) + { + AddScore(initialScore); + presentScore(initialScore); + } } /// From 666cbd0f40f2cf59a78d644caa3c1cb6a53b9588 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 28 May 2020 21:08:47 +0900 Subject: [PATCH 0928/1117] Allow selected score to be programmatically changed --- .../Visual/Ranking/TestSceneScorePanelList.cs | 128 ++++++++++++------ osu.Game/Screens/Ranking/ResultsScreen.cs | 3 +- osu.Game/Screens/Ranking/ScorePanelList.cs | 51 ++++--- 3 files changed, 120 insertions(+), 62 deletions(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs index a204d2bcbc..588cddea7d 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.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.Graphics; @@ -9,58 +10,121 @@ using osu.Framework.Utils; using osu.Game.Rulesets.Osu; using osu.Game.Scoring; using osu.Game.Screens.Ranking; -using osuTK.Input; namespace osu.Game.Tests.Visual.Ranking { public class TestSceneScorePanelList : OsuManualInputManagerTestScene { - private ScoreInfo initialScore; private ScorePanelList list; - [SetUp] - public void Setup() => Schedule(() => + [Test] + public void TestEmptyList() { - Child = list = new ScorePanelList(initialScore = new TestScoreInfo(new OsuRuleset().RulesetInfo)) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }; - }); + createListStep(() => new ScorePanelList()); + } [Test] - public void TestSingleScore() + public void TestEmptyListWithSelectedScore() { + createListStep(() => new ScorePanelList + { + SelectedScore = { Value = new TestScoreInfo(new OsuRuleset().RulesetInfo) } + }); + } + + [Test] + public void TestAddPanelAfterSelectingScore() + { + var score = new TestScoreInfo(new OsuRuleset().RulesetInfo); + + createListStep(() => new ScorePanelList + { + SelectedScore = { Value = score } + }); + + AddStep("add panel", () => list.AddScore(score)); + + assertScoreState(score, true); + assertPanelCentred(); + } + + [Test] + public void TestAddPanelBeforeSelectingScore() + { + var score = new TestScoreInfo(new OsuRuleset().RulesetInfo); + + createListStep(() => new ScorePanelList()); + + AddStep("add panel", () => list.AddScore(score)); + + assertScoreState(score, false); + assertPanelCentred(); + + AddStep("select score", () => list.SelectedScore.Value = score); + + assertScoreState(score, true); assertPanelCentred(); } [Test] public void TestAddManyScoresAfter() { - AddStep("add scores", () => + var initialScore = new TestScoreInfo(new OsuRuleset().RulesetInfo); + + createListStep(() => new ScorePanelList()); + + AddStep("add initial panel and select", () => + { + list.AddScore(initialScore); + list.SelectedScore.Value = initialScore; + }); + + AddStep("add many scores", () => { for (int i = 0; i < 20; i++) list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo) { TotalScore = initialScore.TotalScore - i - 1 }); }); + assertScoreState(initialScore, true); assertPanelCentred(); } [Test] public void TestAddManyScoresBefore() { + var initialScore = new TestScoreInfo(new OsuRuleset().RulesetInfo); + + createListStep(() => new ScorePanelList()); + + AddStep("add initial panel and select", () => + { + list.AddScore(initialScore); + list.SelectedScore.Value = initialScore; + }); + AddStep("add scores", () => { for (int i = 0; i < 20; i++) list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo) { TotalScore = initialScore.TotalScore + i + 1 }); }); + assertScoreState(initialScore, true); assertPanelCentred(); } [Test] public void TestAddManyPanelsOnBothSides() { + var initialScore = new TestScoreInfo(new OsuRuleset().RulesetInfo); + + createListStep(() => new ScorePanelList()); + + AddStep("add initial panel and select", () => + { + list.AddScore(initialScore); + list.SelectedScore.Value = initialScore; + }); + AddStep("add scores after", () => { for (int i = 0; i < 20; i++) @@ -70,42 +134,19 @@ namespace osu.Game.Tests.Visual.Ranking list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo) { TotalScore = initialScore.TotalScore + i + 1 }); }); + assertScoreState(initialScore, true); assertPanelCentred(); } - [Test] - public void TestNullScore() + private void createListStep(Func creationFunc) { - AddStep("create panel with null score", () => + AddStep("create list", () => Child = list = creationFunc().With(d => { - Child = list = new ScorePanelList(null) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }; - }); + d.Anchor = Anchor.Centre; + d.Origin = Anchor.Centre; + })); - AddStep("add many panels", () => - { - for (int i = 0; i < 20; i++) - list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo) { TotalScore = initialScore.TotalScore - i - 1 }); - - for (int i = 0; i < 20; i++) - list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo) { TotalScore = initialScore.TotalScore + i + 1 }); - }); - - AddWaitStep("wait for panel animation", 5); - - AddAssert("no panel selected", () => list.ChildrenOfType().All(p => p.State != PanelState.Expanded)); - - AddStep("expand second panel", () => - { - var expandedPanel = list.ChildrenOfType().OrderBy(p => p.DrawPosition.X).ElementAt(1); - InputManager.MoveMouseTo(expandedPanel); - InputManager.Click(MouseButton.Left); - }); - - assertPanelCentred(); + AddUntilStep("wait for load", () => list.IsLoaded); } private void assertPanelCentred() => AddUntilStep("expanded panel centred", () => @@ -113,5 +154,8 @@ namespace osu.Game.Tests.Visual.Ranking var expandedPanel = list.ChildrenOfType().Single(p => p.State == PanelState.Expanded); return Precision.AlmostEquals(expandedPanel.ScreenSpaceDrawQuad.Centre.X, list.ScreenSpaceDrawQuad.Centre.X, 1); }); + + private void assertScoreState(ScoreInfo score, bool expanded) + => AddUntilStep($"correct score expanded = {expanded}", () => (list.ChildrenOfType().Single(p => p.Score == score).State == PanelState.Expanded) == expanded); } } diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index 97b56d22eb..a4d1a3c26b 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -63,9 +63,10 @@ namespace osu.Game.Screens.Ranking { new ResultsScrollContainer { - Child = panels = new ScorePanelList(Score) + Child = panels = new ScorePanelList { RelativeSizeAxes = Axes.Both, + SelectedScore = { Value = Score } } } }, diff --git a/osu.Game/Screens/Ranking/ScorePanelList.cs b/osu.Game/Screens/Ranking/ScorePanelList.cs index a30d911f04..89f5a47ee8 100644 --- a/osu.Game/Screens/Ranking/ScorePanelList.cs +++ b/osu.Game/Screens/Ranking/ScorePanelList.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.Containers; @@ -23,12 +24,13 @@ namespace osu.Game.Screens.Ranking /// private const float expanded_panel_spacing = 15; + public readonly Bindable SelectedScore = new Bindable(); + private readonly Flow flow; private readonly Scroll scroll; - private ScorePanel expandedPanel; - public ScorePanelList(ScoreInfo initialScore) + public ScorePanelList() { RelativeSizeAxes = Axes.Both; @@ -44,12 +46,13 @@ namespace osu.Game.Screens.Ranking AutoSizeAxes = Axes.Both, } }; + } - if (initialScore != null) - { - AddScore(initialScore); - presentScore(initialScore); - } + protected override void LoadComplete() + { + base.LoadComplete(); + + SelectedScore.BindValueChanged(selectedScoreChanged, true); } /// @@ -67,19 +70,24 @@ namespace osu.Game.Screens.Ranking p.StateChanged += s => { if (s == PanelState.Expanded) - presentScore(score); + SelectedScore.Value = p.Score; }; })); - // We want the scroll position to remain relative to the expanded panel. When a new panel is added after the expanded panel, nothing needs to be done. - // But when a panel is added before the expanded panel, we need to offset the scroll position by the width of the new panel. - if (expandedPanel != null && flow.GetPanelIndex(score) < flow.GetPanelIndex(expandedPanel.Score)) + if (SelectedScore.Value == score) + selectedScoreChanged(new ValueChangedEvent(SelectedScore.Value, SelectedScore.Value)); + else { - // A somewhat hacky property is used here because we need to: - // 1) Scroll after the scroll container's visible range is updated. - // 2) Scroll before the scroll container's scroll position is updated. - // Without this, we would have a 1-frame positioning error which looks very jarring. - scroll.InstantScrollTarget = (scroll.InstantScrollTarget ?? scroll.Target) + ScorePanel.CONTRACTED_WIDTH + panel_spacing; + // We want the scroll position to remain relative to the expanded panel. When a new panel is added after the expanded panel, nothing needs to be done. + // But when a panel is added before the expanded panel, we need to offset the scroll position by the width of the new panel. + if (expandedPanel != null && flow.GetPanelIndex(score) < flow.GetPanelIndex(expandedPanel.Score)) + { + // A somewhat hacky property is used here because we need to: + // 1) Scroll after the scroll container's visible range is updated. + // 2) Scroll before the scroll container's scroll position is updated. + // Without this, we would have a 1-frame positioning error which looks very jarring. + scroll.InstantScrollTarget = (scroll.InstantScrollTarget ?? scroll.Target) + ScorePanel.CONTRACTED_WIDTH + panel_spacing; + } } } @@ -87,17 +95,22 @@ namespace osu.Game.Screens.Ranking /// Brings a to the centre of the screen and expands it. /// /// The to present. - private void presentScore(ScoreInfo score) + private void selectedScoreChanged(ValueChangedEvent score) { // Contract the old panel. - foreach (var p in flow.Where(p => p.Score != score)) + foreach (var p in flow.Where(p => p.Score != score.OldValue)) { p.State = PanelState.Contracted; p.Margin = new MarginPadding(); } + // Find the panel corresponding to the new score. + expandedPanel = flow.SingleOrDefault(p => p.Score == score.NewValue); + + if (expandedPanel == null) + return; + // Expand the new panel. - expandedPanel = flow.Single(p => p.Score == score); expandedPanel.State = PanelState.Expanded; expandedPanel.Margin = new MarginPadding { Horizontal = expanded_panel_spacing }; From ad99d854682af9e1404ff4a4e6f1d38e41a10ab1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 28 May 2020 21:29:16 +0900 Subject: [PATCH 0929/1117] Resolve several positioning errors --- .../Visual/Ranking/TestSceneScorePanelList.cs | 69 ++++++++++++++++--- osu.Game/Screens/Ranking/ScorePanelList.cs | 15 +++- 2 files changed, 71 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs index 588cddea7d..e65dcb19b1 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs @@ -10,6 +10,7 @@ using osu.Framework.Utils; using osu.Game.Rulesets.Osu; using osu.Game.Scoring; using osu.Game.Screens.Ranking; +using osuTK.Input; namespace osu.Game.Tests.Visual.Ranking { @@ -45,7 +46,7 @@ namespace osu.Game.Tests.Visual.Ranking AddStep("add panel", () => list.AddScore(score)); assertScoreState(score, true); - assertPanelCentred(); + assertExpandedPanelCentred(); } [Test] @@ -58,16 +59,30 @@ namespace osu.Game.Tests.Visual.Ranking AddStep("add panel", () => list.AddScore(score)); assertScoreState(score, false); - assertPanelCentred(); + assertFirstPanelCentred(); AddStep("select score", () => list.SelectedScore.Value = score); assertScoreState(score, true); - assertPanelCentred(); + assertExpandedPanelCentred(); } [Test] - public void TestAddManyScoresAfter() + public void TestAddManyNonExpandedPanels() + { + createListStep(() => new ScorePanelList()); + + AddStep("add many scores", () => + { + for (int i = 0; i < 20; i++) + list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo)); + }); + + assertFirstPanelCentred(); + } + + [Test] + public void TestAddManyScoresAfterExpandedPanel() { var initialScore = new TestScoreInfo(new OsuRuleset().RulesetInfo); @@ -86,11 +101,11 @@ namespace osu.Game.Tests.Visual.Ranking }); assertScoreState(initialScore, true); - assertPanelCentred(); + assertExpandedPanelCentred(); } [Test] - public void TestAddManyScoresBefore() + public void TestAddManyScoresBeforeExpandedPanel() { var initialScore = new TestScoreInfo(new OsuRuleset().RulesetInfo); @@ -109,11 +124,11 @@ namespace osu.Game.Tests.Visual.Ranking }); assertScoreState(initialScore, true); - assertPanelCentred(); + assertExpandedPanelCentred(); } [Test] - public void TestAddManyPanelsOnBothSides() + public void TestAddManyPanelsOnBothSidesOfExpandedPanel() { var initialScore = new TestScoreInfo(new OsuRuleset().RulesetInfo); @@ -135,7 +150,36 @@ namespace osu.Game.Tests.Visual.Ranking }); assertScoreState(initialScore, true); - assertPanelCentred(); + assertExpandedPanelCentred(); + } + + [Test] + public void TestSelectMultipleScores() + { + var firstScore = new TestScoreInfo(new OsuRuleset().RulesetInfo); + var secondScore = new TestScoreInfo(new OsuRuleset().RulesetInfo); + + createListStep(() => new ScorePanelList()); + + AddStep("add scores and select first", () => + { + list.AddScore(firstScore); + list.AddScore(secondScore); + list.SelectedScore.Value = firstScore; + }); + + assertScoreState(firstScore, true); + assertScoreState(secondScore, false); + + AddStep("select second score", () => + { + InputManager.MoveMouseTo(list.ChildrenOfType().Single(p => p.Score == secondScore)); + InputManager.Click(MouseButton.Left); + }); + + assertScoreState(firstScore, false); + assertScoreState(secondScore, true); + assertExpandedPanelCentred(); } private void createListStep(Func creationFunc) @@ -149,13 +193,16 @@ namespace osu.Game.Tests.Visual.Ranking AddUntilStep("wait for load", () => list.IsLoaded); } - private void assertPanelCentred() => AddUntilStep("expanded panel centred", () => + private void assertExpandedPanelCentred() => AddUntilStep("expanded panel centred", () => { var expandedPanel = list.ChildrenOfType().Single(p => p.State == PanelState.Expanded); return Precision.AlmostEquals(expandedPanel.ScreenSpaceDrawQuad.Centre.X, list.ScreenSpaceDrawQuad.Centre.X, 1); }); + private void assertFirstPanelCentred() + => AddUntilStep("first panel centred", () => Precision.AlmostEquals(list.ChildrenOfType().First().ScreenSpaceDrawQuad.Centre.X, list.ScreenSpaceDrawQuad.Centre.X, 1)); + private void assertScoreState(ScoreInfo score, bool expanded) - => AddUntilStep($"correct score expanded = {expanded}", () => (list.ChildrenOfType().Single(p => p.Score == score).State == PanelState.Expanded) == expanded); + => AddUntilStep($"score expanded = {expanded}", () => (list.ChildrenOfType().Single(p => p.Score == score).State == PanelState.Expanded) == expanded); } } diff --git a/osu.Game/Screens/Ranking/ScorePanelList.cs b/osu.Game/Screens/Ranking/ScorePanelList.cs index 89f5a47ee8..18db3f2af4 100644 --- a/osu.Game/Screens/Ranking/ScorePanelList.cs +++ b/osu.Game/Screens/Ranking/ScorePanelList.cs @@ -98,7 +98,7 @@ namespace osu.Game.Screens.Ranking private void selectedScoreChanged(ValueChangedEvent score) { // Contract the old panel. - foreach (var p in flow.Where(p => p.Score != score.OldValue)) + foreach (var p in flow.Where(p => p.Score == score.OldValue)) { p.State = PanelState.Contracted; p.Margin = new MarginPadding(); @@ -126,8 +126,19 @@ namespace osu.Game.Screens.Ranking { base.Update(); + float offset = DrawWidth / 2f; + // Add padding to both sides such that the centre of an expanded panel on either side is in the middle of the screen. - flow.Padding = new MarginPadding { Horizontal = DrawWidth / 2f - ScorePanel.EXPANDED_WIDTH / 2f - expanded_panel_spacing }; + + if (SelectedScore.Value != null) + { + // The expanded panel has extra padding applied to it, so it needs to be included into the offset. + offset -= ScorePanel.EXPANDED_WIDTH / 2f + expanded_panel_spacing; + } + else + offset -= ScorePanel.CONTRACTED_WIDTH / 2f; + + flow.Padding = new MarginPadding { Horizontal = offset }; } private class Flow : FillFlowContainer From 47d5974f04c77d1a4e1b59beeb2bb85892fbba33 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 28 May 2020 21:40:01 +0900 Subject: [PATCH 0930/1117] Improve results screen behaviour when changing selected score --- .../Screens/Ranking/ReplayDownloadButton.cs | 3 ++ osu.Game/Screens/Ranking/ResultsScreen.cs | 28 +++++++++++-------- osu.Game/Screens/Ranking/SoloResultsScreen.cs | 2 +- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/osu.Game/Screens/Ranking/ReplayDownloadButton.cs b/osu.Game/Screens/Ranking/ReplayDownloadButton.cs index a36c86eafc..347fcb5f6e 100644 --- a/osu.Game/Screens/Ranking/ReplayDownloadButton.cs +++ b/osu.Game/Screens/Ranking/ReplayDownloadButton.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.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; @@ -13,6 +14,8 @@ namespace osu.Game.Screens.Ranking { public class ReplayDownloadButton : DownloadTrackingComposite { + public Bindable Score => Model; + private DownloadButton button; private ShakeContainer shakeContainer; diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index a4d1a3c26b..fbb9b95478 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -30,16 +31,17 @@ namespace osu.Game.Screens.Ranking protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap.Value); + public readonly Bindable SelectedScore = new Bindable(); + + public readonly ScoreInfo Score; + private readonly bool allowRetry; + [Resolved(CanBeNull = true)] private Player player { get; set; } [Resolved] private IAPIProvider api { get; set; } - public readonly ScoreInfo Score; - - private readonly bool allowRetry; - private Drawable bottomPanel; private ScorePanelList panels; @@ -47,6 +49,8 @@ namespace osu.Game.Screens.Ranking { Score = score; this.allowRetry = allowRetry; + + SelectedScore.Value = score; } [BackgroundDependencyLoader] @@ -66,7 +70,7 @@ namespace osu.Game.Screens.Ranking Child = panels = new ScorePanelList { RelativeSizeAxes = Axes.Both, - SelectedScore = { Value = Score } + SelectedScore = { BindTarget = SelectedScore } } } }, @@ -95,7 +99,11 @@ namespace osu.Game.Screens.Ranking Direction = FillDirection.Horizontal, Children = new Drawable[] { - new ReplayDownloadButton(Score) { Width = 300 }, + new ReplayDownloadButton(null) + { + Score = { BindTarget = SelectedScore }, + Width = 300 + }, } } } @@ -109,6 +117,9 @@ namespace osu.Game.Screens.Ranking } }; + if (Score != null) + panels.AddScore(Score); + if (player != null && allowRetry) { buttons.Add(new RetryButton { Width = 300 }); @@ -132,12 +143,7 @@ namespace osu.Game.Screens.Ranking var req = FetchScores(scores => Schedule(() => { foreach (var s in scores) - { - if (s.OnlineScoreID == Score.OnlineScoreID) - continue; - panels.AddScore(s); - } })); if (req != null) diff --git a/osu.Game/Screens/Ranking/SoloResultsScreen.cs b/osu.Game/Screens/Ranking/SoloResultsScreen.cs index 2b00748ed8..3ae723683a 100644 --- a/osu.Game/Screens/Ranking/SoloResultsScreen.cs +++ b/osu.Game/Screens/Ranking/SoloResultsScreen.cs @@ -25,7 +25,7 @@ namespace osu.Game.Screens.Ranking protected override APIRequest FetchScores(Action> scoresCallback) { var req = new GetScoresRequest(Score.Beatmap, Score.Ruleset); - req.Success += r => scoresCallback?.Invoke(r.Scores.Select(s => s.CreateScoreInfo(rulesets))); + req.Success += r => scoresCallback?.Invoke(r.Scores.Where(s => s.OnlineScoreID != Score.OnlineScoreID).Select(s => s.CreateScoreInfo(rulesets))); return req; } } From 013461377e89730ee381499d816c3e9bd3c4887d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 28 May 2020 21:46:02 +0900 Subject: [PATCH 0931/1117] Fix potential nullref --- .../Gameplay/TestSceneReplayDownloadButton.cs | 17 +++++++++++++++++ .../Screens/Ranking/ReplayDownloadButton.cs | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs index a35437a286..1809332bce 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs @@ -28,6 +28,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep(@"locally available state", () => downloadButton.SetDownloadState(DownloadState.LocallyAvailable)); AddStep(@"not downloaded state", () => downloadButton.SetDownloadState(DownloadState.NotDownloaded)); createButton(false); + createButtonNoScore(); } private void createButton(bool withReplay) @@ -40,6 +41,22 @@ namespace osu.Game.Tests.Visual.Gameplay Origin = Anchor.Centre, }; }); + + AddUntilStep("wait for load", () => downloadButton.IsLoaded); + } + + private void createButtonNoScore() + { + AddStep("create button with null score", () => + { + Child = downloadButton = new TestReplayDownloadButton(null) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }; + }); + + AddUntilStep("wait for load", () => downloadButton.IsLoaded); } private ScoreInfo getScoreInfo(bool replayAvailable) diff --git a/osu.Game/Screens/Ranking/ReplayDownloadButton.cs b/osu.Game/Screens/Ranking/ReplayDownloadButton.cs index 347fcb5f6e..9d4e3af230 100644 --- a/osu.Game/Screens/Ranking/ReplayDownloadButton.cs +++ b/osu.Game/Screens/Ranking/ReplayDownloadButton.cs @@ -26,7 +26,7 @@ namespace osu.Game.Screens.Ranking if (State.Value == DownloadState.LocallyAvailable) return ReplayAvailability.Local; - if (!string.IsNullOrEmpty(Model.Value.Hash)) + if (!string.IsNullOrEmpty(Model.Value?.Hash)) return ReplayAvailability.Online; return ReplayAvailability.NotAvailable; From ee591829899d2fd64938bd9ac406e88ad72b9082 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 26 May 2020 18:12:19 +0900 Subject: [PATCH 0932/1117] Implement initial structure for room scores --- .../Requests/GetRoomPlaylistScoresRequest.cs | 21 +++++ osu.Game/Online/API/RoomScore.cs | 79 +++++++++++++++++++ .../Screens/Multi/Match/MatchSubScreen.cs | 16 ++++ .../Screens/Multi/Play/TimeshiftPlayer.cs | 8 ++ .../Multi/Ranking/TimeshiftResultsScreen.cs | 34 ++++++++ 5 files changed, 158 insertions(+) create mode 100644 osu.Game/Online/API/Requests/GetRoomPlaylistScoresRequest.cs create mode 100644 osu.Game/Online/API/RoomScore.cs create mode 100644 osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs diff --git a/osu.Game/Online/API/Requests/GetRoomPlaylistScoresRequest.cs b/osu.Game/Online/API/Requests/GetRoomPlaylistScoresRequest.cs new file mode 100644 index 0000000000..dd7f80fd46 --- /dev/null +++ b/osu.Game/Online/API/Requests/GetRoomPlaylistScoresRequest.cs @@ -0,0 +1,21 @@ +// 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; + +namespace osu.Game.Online.API.Requests +{ + public class GetRoomPlaylistScoresRequest : APIRequest> + { + private readonly int roomId; + private readonly int playlistItemId; + + public GetRoomPlaylistScoresRequest(int roomId, int playlistItemId) + { + this.roomId = roomId; + this.playlistItemId = playlistItemId; + } + + protected override string Target => $@"rooms/{roomId}/playlist/{playlistItemId}/scores"; + } +} diff --git a/osu.Game/Online/API/RoomScore.cs b/osu.Game/Online/API/RoomScore.cs new file mode 100644 index 0000000000..5d0a9539aa --- /dev/null +++ b/osu.Game/Online/API/RoomScore.cs @@ -0,0 +1,79 @@ +// 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 Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using osu.Game.Online.Multiplayer; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Scoring; +using osu.Game.Scoring; + +namespace osu.Game.Online.API +{ + public class RoomScore + { + [JsonProperty("id")] + public int ID { get; set; } + + [JsonProperty("user_id")] + public int UserID { get; set; } + + [JsonProperty("room_id")] + public int RoomID { get; set; } + + [JsonProperty("playlist_item_id")] + public int PlaylistItemID { get; set; } + + [JsonProperty("beatmap_id")] + public int BeatmapID { get; set; } + + [JsonProperty("rank")] + [JsonConverter(typeof(StringEnumConverter))] + public ScoreRank Rank { get; set; } + + [JsonProperty("total_score")] + public long TotalScore { get; set; } + + [JsonProperty("accuracy")] + public double Accuracy { get; set; } + + [JsonProperty("max_combo")] + public int MaxCombo { get; set; } + + [JsonProperty("mods")] + public APIMod[] Mods { get; set; } + + [JsonProperty("statistics")] + public Dictionary Statistics = new Dictionary(); + + [JsonProperty("passed")] + public bool Passed { get; set; } + + [JsonProperty("ended_at")] + public DateTimeOffset EndedAt { get; set; } + + public ScoreInfo CreateScoreInfo(PlaylistItem playlistItem) + { + var scoreInfo = new ScoreInfo + { + OnlineScoreID = ID, + TotalScore = TotalScore, + MaxCombo = MaxCombo, + Beatmap = playlistItem.Beatmap.Value, + BeatmapInfoID = playlistItem.BeatmapID, + Ruleset = playlistItem.Ruleset.Value, + RulesetID = playlistItem.RulesetID, + User = null, // todo: do we have a user object? + Accuracy = Accuracy, + Date = EndedAt, + Hash = string.Empty, // todo: temporary? + Rank = Rank, + Mods = Array.Empty(), // todo: how? + }; + + return scoreInfo; + } + } +} diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs index caa547ac72..c37f51bcb4 100644 --- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs @@ -10,6 +10,8 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Screens; using osu.Game.Audio; using osu.Game.Beatmaps; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer.GameTypes; using osu.Game.Rulesets.Mods; @@ -162,6 +164,9 @@ namespace osu.Game.Screens.Multi.Match }; } + [Resolved] + private IAPIProvider api { get; set; } + protected override void LoadComplete() { base.LoadComplete(); @@ -185,6 +190,17 @@ namespace osu.Game.Screens.Multi.Match managerAdded = beatmapManager.ItemAdded.GetBoundCopy(); managerAdded.BindValueChanged(beatmapAdded); + + if (roomId.Value != null) + { + var req = new GetRoomPlaylistScoresRequest(roomId.Value.Value, playlist[0].ID); + + req.Success += scores => + { + }; + + api.Queue(req); + } } public override bool OnExiting(IScreen next) diff --git a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs index 7f58de29fb..fbe9e3480f 100644 --- a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs +++ b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs @@ -14,7 +14,9 @@ using osu.Game.Online.API.Requests; using osu.Game.Online.Multiplayer; using osu.Game.Rulesets; using osu.Game.Scoring; +using osu.Game.Screens.Multi.Ranking; using osu.Game.Screens.Play; +using osu.Game.Screens.Ranking; namespace osu.Game.Screens.Multi.Play { @@ -88,6 +90,12 @@ namespace osu.Game.Screens.Multi.Play return false; } + protected override ResultsScreen CreateResults(ScoreInfo score) + { + Debug.Assert(roomId.Value != null); + return new TimeshiftResultsScreen(score, roomId.Value.Value, playlistItem); + } + protected override ScoreInfo CreateScore() { submitScore(); diff --git a/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs new file mode 100644 index 0000000000..60cffc06df --- /dev/null +++ b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs @@ -0,0 +1,34 @@ +// 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.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Online.Multiplayer; +using osu.Game.Scoring; +using osu.Game.Screens.Ranking; + +namespace osu.Game.Screens.Multi.Ranking +{ + public class TimeshiftResultsScreen : ResultsScreen + { + private readonly int roomId; + private readonly PlaylistItem playlistItem; + + public TimeshiftResultsScreen(ScoreInfo score, int roomId, PlaylistItem playlistItem, bool allowRetry = true) + : base(score, allowRetry) + { + this.roomId = roomId; + this.playlistItem = playlistItem; + } + + protected override APIRequest FetchScores(Action> scoresCallback) + { + var req = new GetRoomPlaylistScoresRequest(roomId, playlistItem.ID); + req.Success += r => scoresCallback?.Invoke(r.Select(s => s.CreateScoreInfo(playlistItem))); + return req; + } + } +} From 38502ba88c29615642e986c7dce99bbe16b92e87 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 28 May 2020 14:54:51 +0900 Subject: [PATCH 0933/1117] Remove some unnecessary members --- osu.Game/Online/API/RoomScore.cs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/osu.Game/Online/API/RoomScore.cs b/osu.Game/Online/API/RoomScore.cs index 5d0a9539aa..3ad6169833 100644 --- a/osu.Game/Online/API/RoomScore.cs +++ b/osu.Game/Online/API/RoomScore.cs @@ -20,15 +20,6 @@ namespace osu.Game.Online.API [JsonProperty("user_id")] public int UserID { get; set; } - [JsonProperty("room_id")] - public int RoomID { get; set; } - - [JsonProperty("playlist_item_id")] - public int PlaylistItemID { get; set; } - - [JsonProperty("beatmap_id")] - public int BeatmapID { get; set; } - [JsonProperty("rank")] [JsonConverter(typeof(StringEnumConverter))] public ScoreRank Rank { get; set; } From f9c64d7be38558d5b737ade5053622cbff276906 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 28 May 2020 19:57:58 +0900 Subject: [PATCH 0934/1117] Implement creation of mods --- osu.Game/Online/API/RoomScore.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/API/RoomScore.cs b/osu.Game/Online/API/RoomScore.cs index 3ad6169833..cb4f47c812 100644 --- a/osu.Game/Online/API/RoomScore.cs +++ b/osu.Game/Online/API/RoomScore.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using osu.Game.Online.Multiplayer; @@ -61,7 +62,7 @@ namespace osu.Game.Online.API Date = EndedAt, Hash = string.Empty, // todo: temporary? Rank = Rank, - Mods = Array.Empty(), // todo: how? + Mods = Mods.Select(m => m.ToMod(playlistItem.Ruleset.Value.CreateInstance())).ToArray() }; return scoreInfo; From 7ac08620b80e335f747be668dfe5eaeae2a78703 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 28 May 2020 19:58:07 +0900 Subject: [PATCH 0935/1117] Add a user object for now --- osu.Game/Online/API/RoomScore.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Online/API/RoomScore.cs b/osu.Game/Online/API/RoomScore.cs index cb4f47c812..00907eaa38 100644 --- a/osu.Game/Online/API/RoomScore.cs +++ b/osu.Game/Online/API/RoomScore.cs @@ -7,9 +7,9 @@ using System.Linq; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using osu.Game.Online.Multiplayer; -using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; +using osu.Game.Users; namespace osu.Game.Online.API { @@ -18,8 +18,8 @@ namespace osu.Game.Online.API [JsonProperty("id")] public int ID { get; set; } - [JsonProperty("user_id")] - public int UserID { get; set; } + [JsonProperty("user")] + public User User { get; set; } [JsonProperty("rank")] [JsonConverter(typeof(StringEnumConverter))] @@ -57,7 +57,7 @@ namespace osu.Game.Online.API BeatmapInfoID = playlistItem.BeatmapID, Ruleset = playlistItem.Ruleset.Value, RulesetID = playlistItem.RulesetID, - User = null, // todo: do we have a user object? + User = User, Accuracy = Accuracy, Date = EndedAt, Hash = string.Empty, // todo: temporary? From d88bfa2080f7c1dcf852dfa278fd7f068dd3d8dc Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 28 May 2020 20:07:51 +0900 Subject: [PATCH 0936/1117] Cache ruleset + fix possible nullrefs --- osu.Game/Online/API/RoomScore.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/API/RoomScore.cs b/osu.Game/Online/API/RoomScore.cs index 00907eaa38..a1b08fe40e 100644 --- a/osu.Game/Online/API/RoomScore.cs +++ b/osu.Game/Online/API/RoomScore.cs @@ -7,6 +7,7 @@ using System.Linq; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using osu.Game.Online.Multiplayer; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Users; @@ -48,6 +49,8 @@ namespace osu.Game.Online.API public ScoreInfo CreateScoreInfo(PlaylistItem playlistItem) { + var rulesetInstance = playlistItem.Ruleset.Value.CreateInstance(); + var scoreInfo = new ScoreInfo { OnlineScoreID = ID, @@ -62,7 +65,7 @@ namespace osu.Game.Online.API Date = EndedAt, Hash = string.Empty, // todo: temporary? Rank = Rank, - Mods = Mods.Select(m => m.ToMod(playlistItem.Ruleset.Value.CreateInstance())).ToArray() + Mods = Mods?.Select(m => m.ToMod(rulesetInstance)).ToArray() ?? Array.Empty() }; return scoreInfo; From 0e28ded80fc4b078b10c9666f1399ce8fa994aa8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 28 May 2020 20:07:54 +0900 Subject: [PATCH 0937/1117] Forward statistics --- osu.Game/Online/API/RoomScore.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Online/API/RoomScore.cs b/osu.Game/Online/API/RoomScore.cs index a1b08fe40e..3c7f8c9833 100644 --- a/osu.Game/Online/API/RoomScore.cs +++ b/osu.Game/Online/API/RoomScore.cs @@ -60,6 +60,7 @@ namespace osu.Game.Online.API BeatmapInfoID = playlistItem.BeatmapID, Ruleset = playlistItem.Ruleset.Value, RulesetID = playlistItem.RulesetID, + Statistics = Statistics, User = User, Accuracy = Accuracy, Date = EndedAt, From 0f373acacb55be124e573144a8b112291ea82fdd Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 28 May 2020 20:08:45 +0900 Subject: [PATCH 0938/1117] Add test scene --- .../TestSceneTimeshiftResultsScreen.cs | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs new file mode 100644 index 0000000000..7f43aea56e --- /dev/null +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs @@ -0,0 +1,76 @@ +// 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.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Online.Multiplayer; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Scoring; +using osu.Game.Scoring; +using osu.Game.Screens.Multi.Ranking; +using osu.Game.Tests.Beatmaps; +using osu.Game.Users; + +namespace osu.Game.Tests.Visual.Multiplayer +{ + public class TestSceneTimeshiftResultsScreen : ScreenTestScene + { + [Test] + public void TestShowResults() + { + var score = new TestScoreInfo(new OsuRuleset().RulesetInfo); + var roomScores = new List(); + + for (int i = 0; i < 10; i++) + { + roomScores.Add(new RoomScore + { + ID = i, + Accuracy = 0.9 - 0.01 * i, + EndedAt = DateTimeOffset.Now.Subtract(TimeSpan.FromHours(i)), + Passed = true, + Rank = ScoreRank.B, + MaxCombo = 999, + TotalScore = 999999 - i * 1000, + User = new User + { + Id = 2, + Username = $"peppy{i}", + CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", + }, + Statistics = + { + { HitResult.Miss, 1 }, + { HitResult.Meh, 50 }, + { HitResult.Good, 100 }, + { HitResult.Great, 300 }, + } + }); + } + + AddStep("bind request handler", () => ((DummyAPIAccess)API).HandleRequest = request => + { + switch (request) + { + case GetRoomPlaylistScoresRequest r: + r.TriggerSuccess(roomScores); + break; + } + }); + + AddStep("load results", () => + { + LoadScreen(new TimeshiftResultsScreen(score, 1, new PlaylistItem + { + Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo }, + Ruleset = { Value = new OsuRuleset().RulesetInfo } + })); + }); + + AddWaitStep("wait for display", 10); + } + } +} From a606f41297931301e66225c6ada60cf5fa6b326b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 28 May 2020 22:25:00 +0900 Subject: [PATCH 0939/1117] Add button to open results --- .../TestSceneTimeshiftResultsScreen.cs | 14 ++++++-- .../Screens/Multi/Match/MatchSubScreen.cs | 33 +++++++++++++++++-- .../Multi/Ranking/TimeshiftResultsScreen.cs | 2 +- 3 files changed, 44 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs index 7f43aea56e..d87a2e3408 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs @@ -19,9 +19,19 @@ namespace osu.Game.Tests.Visual.Multiplayer public class TestSceneTimeshiftResultsScreen : ScreenTestScene { [Test] - public void TestShowResults() + public void TestShowResultsWithScore() + { + createResults(new TestScoreInfo(new OsuRuleset().RulesetInfo)); + } + + [Test] + public void TestShowResultsNullScore() + { + createResults(null); + } + + private void createResults(ScoreInfo score) { - var score = new TestScoreInfo(new OsuRuleset().RulesetInfo); var roomScores = new List(); for (int i = 0; i < 10; i++) diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs index c37f51bcb4..01a90139c3 100644 --- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Diagnostics; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -10,6 +11,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Screens; using osu.Game.Audio; using osu.Game.Beatmaps; +using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.Multiplayer; @@ -18,6 +20,7 @@ using osu.Game.Rulesets.Mods; using osu.Game.Screens.Multi.Components; using osu.Game.Screens.Multi.Match.Components; using osu.Game.Screens.Multi.Play; +using osu.Game.Screens.Multi.Ranking; using osu.Game.Screens.Select; using Footer = osu.Game.Screens.Multi.Match.Components.Footer; @@ -114,10 +117,29 @@ namespace osu.Game.Screens.Multi.Match { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Horizontal = 5 }, - Child = new OverlinedPlaylist(true) // Temporarily always allow selection + Child = new GridContainer { RelativeSizeAxes = Axes.Both, - SelectedItem = { BindTarget = SelectedItem } + Content = new[] + { + new Drawable[] + { + new OverlinedPlaylist(true) // Temporarily always allow selection + { + RelativeSizeAxes = Axes.Both, + SelectedItem = { BindTarget = SelectedItem } + } + }, + new Drawable[] + { + new TriangleButton + { + RelativeSizeAxes = Axes.X, + Text = "Show beatmap results", + Action = showBeatmapResults + } + } + } } }, new Container @@ -257,5 +279,12 @@ namespace osu.Game.Screens.Multi.Match break; } } + + private void showBeatmapResults() + { + Debug.Assert(roomId.Value != null); + + this.Push(new TimeshiftResultsScreen(null, roomId.Value.Value, SelectedItem.Value, false)); + } } } diff --git a/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs index 60cffc06df..f2afe15d35 100644 --- a/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs +++ b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs @@ -27,7 +27,7 @@ namespace osu.Game.Screens.Multi.Ranking protected override APIRequest FetchScores(Action> scoresCallback) { var req = new GetRoomPlaylistScoresRequest(roomId, playlistItem.ID); - req.Success += r => scoresCallback?.Invoke(r.Select(s => s.CreateScoreInfo(playlistItem))); + req.Success += r => scoresCallback?.Invoke(r.Where(s => s.ID != Score?.OnlineScoreID).Select(s => s.CreateScoreInfo(playlistItem))); return req; } } From 46689a2fbc46281277feb6fe806b0f756e1bfddc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 May 2020 11:46:08 +0900 Subject: [PATCH 0940/1117] Tidy up and complete xmldoc for HitObjectComposer --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 89 ++++++++++++++++----- osu.sln.DotSettings | 5 +- 2 files changed, 74 insertions(+), 20 deletions(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 38576e02a0..c956439eb2 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -48,8 +48,6 @@ namespace osu.Game.Rulesets.Edit protected ComposeBlueprintContainer BlueprintContainer { get; private set; } - public override Playfield Playfield => drawableRulesetWrapper.Playfield; - private DrawableEditRulesetWrapper drawableRulesetWrapper; protected readonly Container LayerBelowRuleset = new Container { RelativeSizeAxes = Axes.Both }; @@ -61,7 +59,6 @@ namespace osu.Game.Rulesets.Edit protected HitObjectComposer(Ruleset ruleset) { Ruleset = ruleset; - RelativeSizeAxes = Axes.Both; } [BackgroundDependencyLoader] @@ -137,6 +134,49 @@ namespace osu.Game.Rulesets.Edit EditorBeatmap.SelectedHitObjects.CollectionChanged += selectionChanged; } + protected override void LoadComplete() + { + base.LoadComplete(); + + inputManager = GetContainingInputManager(); + } + + public override Playfield Playfield => drawableRulesetWrapper.Playfield; + + public override IEnumerable HitObjects => drawableRulesetWrapper.Playfield.AllHitObjects; + + public override bool CursorInPlacementArea => drawableRulesetWrapper.Playfield.ReceivePositionalInputAt(inputManager.CurrentState.Mouse.Position); + + /// + /// Defines all available composition tools, listed on the left side of the editor screen as button controls. + /// This should usually define one tool for each type used in the target ruleset. + /// + /// + /// A "select" tool is automatically added as the first tool. + /// + protected abstract IReadOnlyList CompositionTools { get; } + + /// + /// Construct a relevant blueprint container. This will manage hitobject selection/placement input handling and display logic. + /// + protected abstract ComposeBlueprintContainer CreateBlueprintContainer(); + + /// + /// Construct a drawable ruleset for the provided ruleset. + /// + /// + /// Can be overridden to add editor-specific logical changes to a 's standard . + /// For example, hit animations or judgement logic may be changed to give a better editor user experience. + /// + /// The ruleset used to construct its drawable counterpart. + /// The loaded beatmap. + /// The mods to be applied. + /// An editor-relevant . + protected virtual DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) + => (DrawableRuleset)ruleset.CreateDrawableRulesetWith(beatmap, mods); + + #region Tool selection logic + protected override bool OnKeyDown(KeyDownEvent e) { if (e.Key >= Key.Number1 && e.Key <= Key.Number9) @@ -153,13 +193,6 @@ namespace osu.Game.Rulesets.Edit return base.OnKeyDown(e); } - protected override void LoadComplete() - { - base.LoadComplete(); - - inputManager = GetContainingInputManager(); - } - private void selectionChanged(object sender, NotifyCollectionChangedEventArgs changedArgs) { if (EditorBeatmap.SelectedHitObjects.Any()) @@ -179,15 +212,9 @@ namespace osu.Game.Rulesets.Edit EditorBeatmap.SelectedHitObjects.Clear(); } - public override IEnumerable HitObjects => drawableRulesetWrapper.Playfield.AllHitObjects; + #endregion - public override bool CursorInPlacementArea => drawableRulesetWrapper.Playfield.ReceivePositionalInputAt(inputManager.CurrentState.Mouse.Position); - - protected abstract IReadOnlyList CompositionTools { get; } - - protected abstract ComposeBlueprintContainer CreateBlueprintContainer(); - - protected abstract DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null); + #region IPlacementHandler public void BeginPlacement(HitObject hitObject) { @@ -209,6 +236,17 @@ namespace osu.Game.Rulesets.Edit public void Delete(HitObject hitObject) => EditorBeatmap.Remove(hitObject); + #endregion + + #region IPositionSnapProvider + + /// + /// Retrieve the relevant at a specified screen-space position. + /// In cases where a ruleset doesn't require custom logic (due to nested playfields, for example) + /// this will return the ruleset's main playfield. + /// + /// The screen-space position to query. + /// The most relevant . protected virtual Playfield PlayfieldAtScreenSpacePosition(Vector2 screenSpacePosition) => drawableRulesetWrapper.Playfield; public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) @@ -257,8 +295,14 @@ namespace osu.Game.Rulesets.Edit return DurationToDistance(referenceTime, snappedEndTime - referenceTime); } + + #endregion } + /// + /// A non-generic definition of a HitObject composer class. + /// Generally used to access certain methods without requiring a generic type for . + /// [Cached(typeof(HitObjectComposer))] [Cached(typeof(IPositionSnapProvider))] public abstract class HitObjectComposer : CompositeDrawable, IPositionSnapProvider @@ -268,10 +312,13 @@ namespace osu.Game.Rulesets.Edit RelativeSizeAxes = Axes.Both; } + /// + /// The target ruleset's playfield. + /// public abstract Playfield Playfield { get; } /// - /// All the s. + /// All s in currently loaded beatmap. /// public abstract IEnumerable HitObjects { get; } @@ -280,6 +327,8 @@ namespace osu.Game.Rulesets.Edit /// public abstract bool CursorInPlacementArea { get; } + #region IPositionSnapProvider + public abstract SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition); public abstract float GetBeatSnapDistanceAt(double referenceTime); @@ -291,5 +340,7 @@ namespace osu.Game.Rulesets.Edit public abstract double GetSnappedDurationFromDistance(double referenceTime, float distance); public abstract float GetSnappedDistanceFromDistance(double referenceTime, float distance); + + #endregion } } diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index e3b64c03b9..b9fc3de734 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -1,4 +1,4 @@ - + True True True @@ -905,14 +905,17 @@ private void load() True True True + True True True True True True True + True True True True + True True True From 8fa8c561e7bc438eb5c64473d70d50e6de4c4584 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 May 2020 12:20:50 +0900 Subject: [PATCH 0941/1117] Pass hitobjects as a parameter to CreateBlueprintContainer --- osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs | 4 +++- osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs | 4 +++- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 6 ++++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index 10d344242c..7e2469a794 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -13,6 +13,7 @@ using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; using osu.Game.Rulesets.Mania.UI; 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.Screens.Edit.Compose.Components; @@ -88,7 +89,8 @@ namespace osu.Game.Rulesets.Mania.Edit return drawableRuleset; } - protected override ComposeBlueprintContainer CreateBlueprintContainer() => new ManiaBlueprintContainer(drawableRuleset.Playfield.AllHitObjects); + protected override ComposeBlueprintContainer CreateBlueprintContainer(IEnumerable hitObjects) + => new ManiaBlueprintContainer(hitObjects); protected override IReadOnlyList CompositionTools => new HitObjectCompositionTool[] { diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index de5c1e54d7..37019a7a05 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -13,6 +13,7 @@ using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.UI; using osu.Game.Screens.Edit.Compose.Components; @@ -46,7 +47,8 @@ namespace osu.Game.Rulesets.Osu.Edit EditorBeatmap.PlacementObject.ValueChanged += _ => updateDistanceSnapGrid(); } - protected override ComposeBlueprintContainer CreateBlueprintContainer() => new OsuBlueprintContainer(HitObjects); + protected override ComposeBlueprintContainer CreateBlueprintContainer(IEnumerable hitObjects) + => new OsuBlueprintContainer(hitObjects); private DistanceSnapGrid distanceSnapGrid; private Container distanceSnapGridContainer; diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index c956439eb2..8b9f531417 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -113,7 +113,7 @@ namespace osu.Game.Rulesets.Edit drawableRulesetWrapper, // layers above playfield drawableRulesetWrapper.CreatePlayfieldAdjustmentContainer() - .WithChild(BlueprintContainer = CreateBlueprintContainer()) + .WithChild(BlueprintContainer = CreateBlueprintContainer(HitObjects)) } } }, @@ -159,7 +159,9 @@ namespace osu.Game.Rulesets.Edit /// /// Construct a relevant blueprint container. This will manage hitobject selection/placement input handling and display logic. /// - protected abstract ComposeBlueprintContainer CreateBlueprintContainer(); + /// A live collection of all s in the editor beatmap. + protected virtual ComposeBlueprintContainer CreateBlueprintContainer(IEnumerable hitObjects) + => new ComposeBlueprintContainer(hitObjects); /// /// Construct a drawable ruleset for the provided ruleset. From f9883373bbd86eb0513968d6dd487b95f4ce759f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 May 2020 16:11:26 +0900 Subject: [PATCH 0942/1117] Flip direction to avoid breaking other usages --- osu.Game/Rulesets/Objects/Types/IHasDuration.cs | 16 +++++++++++++--- osu.Game/Rulesets/Objects/Types/IHasEndTime.cs | 16 +++++++++++++++- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Types/IHasDuration.cs b/osu.Game/Rulesets/Objects/Types/IHasDuration.cs index 2433f9597e..185fd5977b 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasDuration.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasDuration.cs @@ -8,17 +8,27 @@ namespace osu.Game.Rulesets.Objects.Types /// /// A HitObject that ends at a different time than its start time. /// - public interface IHasDuration +#pragma warning disable 618 + public interface IHasDuration : IHasEndTime +#pragma warning restore 618 { + double IHasEndTime.EndTime + { + get => EndTime; + set => Duration = (Duration - EndTime) + value; + } + + double IHasEndTime.Duration => Duration; + /// /// The time at which the HitObject ends. /// - double EndTime { get; } + new double EndTime { get; } /// /// The duration of the HitObject. /// [JsonIgnore] - double Duration { get; set; } + new double Duration { get; set; } } } diff --git a/osu.Game/Rulesets/Objects/Types/IHasEndTime.cs b/osu.Game/Rulesets/Objects/Types/IHasEndTime.cs index 7395223c7e..c3769c5909 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasEndTime.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasEndTime.cs @@ -2,11 +2,25 @@ // See the LICENCE file in the repository root for full licence text. using System; +using Newtonsoft.Json; namespace osu.Game.Rulesets.Objects.Types { + /// + /// A HitObject that ends at a different time than its start time. + /// [Obsolete("Use IHasDuration instead.")] // can be removed 20201126 - public interface IHasEndTime : IHasDuration + public interface IHasEndTime { + /// + /// The time at which the HitObject ends. + /// + [JsonIgnore] + double EndTime { get; set; } + + /// + /// The duration of the HitObject. + /// + double Duration { get; } } } From 7d4e60f05e01b77932ffac10ab5f5735a85a3015 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 23 May 2020 16:06:25 +0900 Subject: [PATCH 0943/1117] Add basic setup for TaikoHitObjectComposer --- .../TestSceneTaikoHitObjectComposer.cs | 58 +++++++++++++++++++ .../TaikoHitObjectComposer.cs | 34 +++++++++++ osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 3 + 3 files changed, 95 insertions(+) create mode 100644 osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectComposer.cs create mode 100644 osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectComposer.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectComposer.cs new file mode 100644 index 0000000000..0fff08fab7 --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectComposer.cs @@ -0,0 +1,58 @@ +// 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.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Taiko.Beatmaps; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Screens.Edit; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Taiko.Tests +{ + public class TestSceneTaikoHitObjectComposer : EditorClockTestScene + { + private TestComposer composer; + + [SetUp] + public void Setup() => Schedule(() => + { + BeatDivisor.Value = 8; + Clock.Seek(0); + + Child = composer = new TestComposer { RelativeSizeAxes = Axes.Both }; + }); + + [Test] + public void BasicTest() + { + } + + private class TestComposer : CompositeDrawable + { + [Cached(typeof(EditorBeatmap))] + [Cached(typeof(IBeatSnapProvider))] + public readonly EditorBeatmap EditorBeatmap; + + public readonly TaikoHitObjectComposer Composer; + + public TestComposer() + { + InternalChildren = new Drawable[] + { + EditorBeatmap = new EditorBeatmap(new TaikoBeatmap()) + { + BeatmapInfo = { Ruleset = new TaikoRuleset().RulesetInfo } + }, + Composer = new TaikoHitObjectComposer(new TaikoRuleset()) + }; + + for (int i = 0; i < 10; i++) + EditorBeatmap.Add(new Hit { StartTime = 125 * i }); + } + } + } +} diff --git a/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs b/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs new file mode 100644 index 0000000000..069517fe46 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs @@ -0,0 +1,34 @@ +// 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.Game.Beatmaps; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Edit.Tools; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Taiko.UI; +using osu.Game.Rulesets.UI; +using osu.Game.Screens.Edit.Compose.Components; + +namespace osu.Game.Rulesets.Taiko +{ + public class TaikoHitObjectComposer : HitObjectComposer + { + private DrawableTaikoRuleset drawableRuleset; + + public TaikoHitObjectComposer(Ruleset ruleset) + : base(ruleset) + { + } + + protected override IReadOnlyList CompositionTools => new List(); + + protected override ComposeBlueprintContainer CreateBlueprintContainer() => new ComposeBlueprintContainer(drawableRuleset.Playfield.AllHitObjects); + + protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) + { + return drawableRuleset = new DrawableTaikoRuleset(ruleset, beatmap, mods); + } + } +} diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 74d9e68ad3..7be16471b4 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -21,6 +21,7 @@ using osu.Game.Rulesets.Taiko.Difficulty; using osu.Game.Rulesets.Taiko.Scoring; using osu.Game.Scoring; using System; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Taiko.Skinning; using osu.Game.Skinning; @@ -144,6 +145,8 @@ namespace osu.Game.Rulesets.Taiko public override Drawable CreateIcon() => new SpriteIcon { Icon = OsuIcon.RulesetTaiko }; + public override HitObjectComposer CreateHitObjectComposer() => new TaikoHitObjectComposer(this); + public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new TaikoDifficultyCalculator(this, beatmap); public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new TaikoPerformanceCalculator(this, beatmap, score); From 90acba8c36ded4f5d6c3873d46c99e27a5ff9517 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 May 2020 19:23:36 +0900 Subject: [PATCH 0944/1117] Introduce initial placement blueprint logic --- .../TestSceneTaikoHitObjectComposer.cs | 8 +- .../TaikoHitObjectComposer.cs | 119 +++++++++++++++++- 2 files changed, 119 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectComposer.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectComposer.cs index 0fff08fab7..b5ee33fa8e 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectComposer.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectComposer.cs @@ -15,15 +15,13 @@ namespace osu.Game.Rulesets.Taiko.Tests { public class TestSceneTaikoHitObjectComposer : EditorClockTestScene { - private TestComposer composer; - [SetUp] public void Setup() => Schedule(() => { BeatDivisor.Value = 8; Clock.Seek(0); - Child = composer = new TestComposer { RelativeSizeAxes = Axes.Both }; + Child = new TestComposer { RelativeSizeAxes = Axes.Both }; }); [Test] @@ -37,8 +35,6 @@ namespace osu.Game.Rulesets.Taiko.Tests [Cached(typeof(IBeatSnapProvider))] public readonly EditorBeatmap EditorBeatmap; - public readonly TaikoHitObjectComposer Composer; - public TestComposer() { InternalChildren = new Drawable[] @@ -47,7 +43,7 @@ namespace osu.Game.Rulesets.Taiko.Tests { BeatmapInfo = { Ruleset = new TaikoRuleset().RulesetInfo } }, - Composer = new TaikoHitObjectComposer(new TaikoRuleset()) + new TaikoHitObjectComposer(new TaikoRuleset()) }; for (int i = 0; i < 10; i++) diff --git a/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs b/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs index 069517fe46..dfdc91f7dc 100644 --- a/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs +++ b/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs @@ -2,14 +2,22 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Events; using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.UI; using osu.Game.Rulesets.UI; using osu.Game.Screens.Edit.Compose.Components; +using osuTK; +using osuTK.Graphics; +using osuTK.Input; namespace osu.Game.Rulesets.Taiko { @@ -22,13 +30,120 @@ namespace osu.Game.Rulesets.Taiko { } - protected override IReadOnlyList CompositionTools => new List(); + protected override IReadOnlyList CompositionTools => new[] + { + new HitCompositionTool() + }; - protected override ComposeBlueprintContainer CreateBlueprintContainer() => new ComposeBlueprintContainer(drawableRuleset.Playfield.AllHitObjects); + protected override ComposeBlueprintContainer CreateBlueprintContainer() => new TaikoBlueprintContainer(drawableRuleset.Playfield.AllHitObjects); protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) { return drawableRuleset = new DrawableTaikoRuleset(ruleset, beatmap, mods); } } + + public class TaikoBlueprintContainer : ComposeBlueprintContainer + { + public TaikoBlueprintContainer(IEnumerable hitObjects) + : base(hitObjects) + { + } + + public override OverlaySelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject) => + new TaikoSelectionBlueprint(hitObject); + } + + public class TaikoSelectionBlueprint : OverlaySelectionBlueprint + { + public TaikoSelectionBlueprint(DrawableHitObject hitObject) + : base(hitObject) + { + RelativeSizeAxes = Axes.None; + + AddInternal(new HitPiece { RelativeSizeAxes = Axes.Both }); + } + + protected override void Update() + { + base.Update(); + + // Move the rectangle to cover the hitobjects + var topLeft = new Vector2(float.MaxValue, float.MaxValue); + var bottomRight = new Vector2(float.MinValue, float.MinValue); + + topLeft = Vector2.ComponentMin(topLeft, Parent.ToLocalSpace(DrawableObject.ScreenSpaceDrawQuad.TopLeft)); + bottomRight = Vector2.ComponentMax(bottomRight, Parent.ToLocalSpace(DrawableObject.ScreenSpaceDrawQuad.BottomRight)); + + Size = bottomRight - topLeft; + Position = topLeft; + } + } + + public class HitCompositionTool : HitObjectCompositionTool + { + public HitCompositionTool() + : base(nameof(Hit)) + { + } + + public override PlacementBlueprint CreatePlacementBlueprint() => new HitPlacementBlueprint(); + } + + public class HitPlacementBlueprint : PlacementBlueprint + + { + private readonly HitPiece piece; + + public HitPlacementBlueprint() + : base(new Hit()) + { + InternalChild = piece = new HitPiece + { + Size = new Vector2(TaikoHitObject.DEFAULT_SIZE * TaikoPlayfield.DEFAULT_HEIGHT) + }; + } + + protected override bool OnMouseDown(MouseDownEvent e) + { + if (e.Button == MouseButton.Left) + { + EndPlacement(true); + return true; + } + + return base.OnMouseDown(e); + } + + public override void UpdatePosition(SnapResult snapResult) + { + piece.Position = ToLocalSpace(snapResult.ScreenSpacePosition); + base.UpdatePosition(snapResult); + } + } + + public class HitPiece : CompositeDrawable + { + public HitPiece() + { + Origin = Anchor.Centre; + + InternalChild = new CircularContainer + { + Masking = true, + BorderThickness = 10, + BorderColour = Color4.Yellow, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + AlwaysPresent = true, + Alpha = 0, + RelativeSizeAxes = Axes.Both + } + } + }; + } + } } From 4b1a2b5bc2d1481120d60700270c7b48436d853c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 May 2020 22:36:49 +0900 Subject: [PATCH 0945/1117] Fix offsets --- osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs b/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs index dfdc91f7dc..c820b3eb92 100644 --- a/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs +++ b/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs @@ -61,7 +61,11 @@ namespace osu.Game.Rulesets.Taiko { RelativeSizeAxes = Axes.None; - AddInternal(new HitPiece { RelativeSizeAxes = Axes.Both }); + AddInternal(new HitPiece + { + RelativeSizeAxes = Axes.Both, + Origin = Anchor.TopLeft + }); } protected override void Update() From 3487c1fd1b9e1d53df72f60cece30f890c239637 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 May 2020 13:51:53 +0900 Subject: [PATCH 0946/1117] Add menus to mark as rim and strong --- .../TestSceneEditor.cs | 17 +++++ osu.Game.Rulesets.Taiko/Objects/Hit.cs | 10 ++- .../Objects/TaikoHitObject.cs | 9 ++- .../TaikoHitObjectComposer.cs | 70 +++++++++++++++++++ .../UserInterface/TernaryStateMenuItem.cs | 12 +--- 5 files changed, 105 insertions(+), 13 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko.Tests/TestSceneEditor.cs diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneEditor.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneEditor.cs new file mode 100644 index 0000000000..089a7ad00b --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneEditor.cs @@ -0,0 +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 NUnit.Framework; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Taiko.Tests +{ + [TestFixture] + public class TestSceneEditor : EditorTestScene + { + public TestSceneEditor() + : base(new TaikoRuleset()) + { + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Objects/Hit.cs b/osu.Game.Rulesets.Taiko/Objects/Hit.cs index 2aca701515..68cc8d0ead 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Hit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Hit.cs @@ -1,13 +1,21 @@ // 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.Taiko.Objects { public class Hit : TaikoHitObject { + public readonly Bindable TypeBindable = new Bindable(); + /// /// The that actuates this . /// - public HitType Type { get; set; } + public HitType Type + { + get => TypeBindable.Value; + set => TypeBindable.Value = value; + } } } diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs index 206bfcfdb2..4de762ce30 100644 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Threading; +using osu.Framework.Bindables; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; @@ -27,11 +28,17 @@ namespace osu.Game.Rulesets.Taiko.Objects /// public const float DEFAULT_STRONG_SIZE = DEFAULT_SIZE * STRONG_SCALE; + public readonly Bindable IsStrongBindable = new BindableBool(); + /// /// Whether this HitObject is a "strong" type. /// Strong hit objects give more points for hitting the hit object with both keys. /// - public virtual bool IsStrong { get; set; } + public virtual bool IsStrong + { + get => IsStrongBindable.Value; + set => IsStrongBindable.Value = value; + } protected override void CreateNestedHitObjects(CancellationToken cancellationToken) { diff --git a/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs b/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs index c820b3eb92..beef2b6155 100644 --- a/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs +++ b/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs @@ -1,12 +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.Linq; 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.Beatmaps; +using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Mods; @@ -50,10 +54,76 @@ namespace osu.Game.Rulesets.Taiko { } + protected override SelectionHandler CreateSelectionHandler() => new TaikoSelectionHandler(); + public override OverlaySelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject) => new TaikoSelectionBlueprint(hitObject); } + public class TaikoSelectionHandler : SelectionHandler + { + protected override IEnumerable GetContextMenuItemsForSelection(IEnumerable selection) + { + if (selection.All(s => s.HitObject is Hit)) + { + var hits = selection.Select(s => s.HitObject).OfType(); + + yield return new TernaryStateMenuItem("Rim", action: state => + { + foreach (var h in hits) + { + switch (state) + { + case TernaryState.True: + h.Type = HitType.Rim; + break; + + case TernaryState.False: + h.Type = HitType.Centre; + break; + } + } + }) + { + State = { Value = getTernaryState(hits, h => h.Type == HitType.Rim) } + }; + } + + if (selection.All(s => s.HitObject is TaikoHitObject)) + { + var hits = selection.Select(s => s.HitObject).OfType(); + + yield return new TernaryStateMenuItem("Strong", action: state => + { + foreach (var h in hits) + { + switch (state) + { + case TernaryState.True: + h.IsStrong = true; + break; + + case TernaryState.False: + h.IsStrong = false; + break; + } + } + }) + { + State = { Value = getTernaryState(hits, h => h.IsStrong) } + }; + } + } + + private TernaryState getTernaryState(IEnumerable selection, Func func) + { + if (selection.Any(func)) + return selection.All(func) ? TernaryState.True : TernaryState.Indeterminate; + + return TernaryState.False; + } + } + public class TaikoSelectionBlueprint : OverlaySelectionBlueprint { public TaikoSelectionBlueprint(DrawableHitObject hitObject) diff --git a/osu.Game/Graphics/UserInterface/TernaryStateMenuItem.cs b/osu.Game/Graphics/UserInterface/TernaryStateMenuItem.cs index 2d9e2106d4..acf4065f49 100644 --- a/osu.Game/Graphics/UserInterface/TernaryStateMenuItem.cs +++ b/osu.Game/Graphics/UserInterface/TernaryStateMenuItem.cs @@ -11,23 +11,13 @@ namespace osu.Game.Graphics.UserInterface /// public class TernaryStateMenuItem : StatefulMenuItem { - /// - /// Creates a new . - /// - /// The text to display. - /// The type of action which this performs. - public TernaryStateMenuItem(string text, MenuItemType type = MenuItemType.Standard) - : this(text, type, null) - { - } - /// /// Creates a new . /// /// The text to display. /// The type of action which this performs. /// A delegate to be invoked when this is pressed. - public TernaryStateMenuItem(string text, MenuItemType type, Action action) + public TernaryStateMenuItem(string text, MenuItemType type = MenuItemType.Standard, Action action = null) : this(text, getNextState, type, action) { } From 4e9631b54631655ee6d92742b14fb3c730d9a463 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 May 2020 14:43:38 +0900 Subject: [PATCH 0947/1117] Support HitType bindable changes --- .../Objects/Drawables/DrawableHit.cs | 24 ++++++++++++++++++- .../Drawables/DrawableTaikoHitObject.cs | 21 ++++++++++++---- 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index 81b969eaf3..92ae7e0fd3 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -5,6 +5,8 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Audio; using osu.Game.Rulesets.Objects.Drawables; @@ -19,7 +21,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables /// /// A list of keys which can result in hits for this HitObject. /// - public TaikoAction[] HitActions { get; } + public TaikoAction[] HitActions { get; private set; } /// /// The action that caused this to be hit. @@ -34,15 +36,35 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables private bool pressHandledThisFrame; + private Bindable type; + public DrawableHit(Hit hit) : base(hit) { FillMode = FillMode.Fit; + } + [BackgroundDependencyLoader] + private void load() + { + type = HitObject.TypeBindable.GetBoundCopy(); + type.BindValueChanged(_ => + { + updateType(); + RecreatePieces(); + }); + + updateType(); + } + + private void updateType() + { HitActions = HitObject.Type == HitType.Centre ? new[] { TaikoAction.LeftCentre, TaikoAction.RightCentre } : new[] { TaikoAction.LeftRim, TaikoAction.RightRim }; + + RecreatePieces(); } protected override SkinnableDrawable CreateMainPiece() => HitObject.Type == HitType.Centre diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs index 3ab09d4cbe..a3dfc9acc0 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs @@ -8,6 +8,7 @@ using osuTK; using System.Linq; using osu.Game.Audio; using System.Collections.Generic; +using osu.Framework.Allocation; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; using osu.Game.Rulesets.Objects; @@ -115,10 +116,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables public new TObject HitObject; - protected readonly Vector2 BaseSize; - protected readonly SkinnableDrawable MainPiece; + protected Vector2 BaseSize; + protected SkinnableDrawable MainPiece; - private readonly Container strongHitContainer; + private Container strongHitContainer; protected DrawableTaikoHitObject(TObject hitObject) : base(hitObject) @@ -129,11 +130,21 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables Origin = Anchor.Custom; RelativeSizeAxes = Axes.Both; + AddInternal(strongHitContainer = new Container()); + } + + [BackgroundDependencyLoader] + private void load() + { + RecreatePieces(); + } + + protected virtual void RecreatePieces() + { Size = BaseSize = new Vector2(HitObject.IsStrong ? TaikoHitObject.DEFAULT_STRONG_SIZE : TaikoHitObject.DEFAULT_SIZE); + Content.Clear(); Content.Add(MainPiece = CreateMainPiece()); - - AddInternal(strongHitContainer = new Container()); } protected override void AddNestedHitObject(DrawableHitObject hitObject) From 50fcd4149f42dae335f9e0eae0507c516a2c058e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 May 2020 14:54:12 +0900 Subject: [PATCH 0948/1117] Support Strong bindable changes --- .../Objects/Drawables/DrawableTaikoHitObject.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs index a3dfc9acc0..929cf8a937 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs @@ -9,6 +9,7 @@ using System.Linq; using osu.Game.Audio; using System.Collections.Generic; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; using osu.Game.Rulesets.Objects; @@ -119,7 +120,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables protected Vector2 BaseSize; protected SkinnableDrawable MainPiece; - private Container strongHitContainer; + private Bindable isStrong; + + private readonly Container strongHitContainer; protected DrawableTaikoHitObject(TObject hitObject) : base(hitObject) @@ -130,20 +133,22 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables Origin = Anchor.Custom; RelativeSizeAxes = Axes.Both; + AddInternal(strongHitContainer = new Container()); } [BackgroundDependencyLoader] private void load() { - RecreatePieces(); + isStrong = HitObject.IsStrongBindable.GetBoundCopy(); + isStrong.BindValueChanged(_ => RecreatePieces(), true); } protected virtual void RecreatePieces() { Size = BaseSize = new Vector2(HitObject.IsStrong ? TaikoHitObject.DEFAULT_STRONG_SIZE : TaikoHitObject.DEFAULT_SIZE); - Content.Clear(); + MainPiece?.Expire(); Content.Add(MainPiece = CreateMainPiece()); } From 910326623c8e24658565e63d95e77b707c244a4a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 May 2020 15:09:22 +0900 Subject: [PATCH 0949/1117] Place rim hits using right mosue for now --- .../TaikoHitObjectComposer.cs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs b/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs index beef2b6155..5d7880278a 100644 --- a/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs +++ b/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs @@ -169,8 +169,10 @@ namespace osu.Game.Rulesets.Taiko { private readonly HitPiece piece; + private static Hit hit; + public HitPlacementBlueprint() - : base(new Hit()) + : base(hit = new Hit()) { InternalChild = piece = new HitPiece { @@ -180,13 +182,20 @@ namespace osu.Game.Rulesets.Taiko protected override bool OnMouseDown(MouseDownEvent e) { - if (e.Button == MouseButton.Left) + switch (e.Button) { - EndPlacement(true); - return true; + case MouseButton.Left: + hit.Type = HitType.Centre; + EndPlacement(true); + return true; + + case MouseButton.Right: + hit.Type = HitType.Rim; + EndPlacement(true); + return true; } - return base.OnMouseDown(e); + return false; } public override void UpdatePosition(SnapResult snapResult) From a2eec5d963dad4cbf4cda878eb7fefc36d3c5d91 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 May 2020 16:58:28 +0900 Subject: [PATCH 0950/1117] Fix strong bindable changes for DrumRolls --- .../Objects/Drawables/DrawableDrumRoll.cs | 17 +++++++++++------ osu.Game.Rulesets.Taiko/Objects/Swell.cs | 6 ------ .../Objects/TaikoHitObject.cs | 2 +- .../TaikoHitObjectComposer.cs | 2 ++ .../Edit/Compose/Components/SelectionHandler.cs | 8 ++++---- 5 files changed, 18 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs index 5e731e5ad6..2c1c2d2bc1 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs @@ -48,12 +48,11 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables colourIdle = colours.YellowDark; colourEngaged = colours.YellowDarker; - updateColour(); - - Content.Add(tickContainer = new Container { RelativeSizeAxes = Axes.Both }); - - if (MainPiece.Drawable is IHasAccentColour accentMain) - accentMain.AccentColour = colourIdle; + Content.Add(tickContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Depth = float.MinValue + }); } protected override void LoadComplete() @@ -63,6 +62,12 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables OnNewResult += onNewResult; } + protected override void RecreatePieces() + { + base.RecreatePieces(); + updateColour(); + } + protected override void AddNestedHitObject(DrawableHitObject hitObject) { base.AddNestedHitObject(hitObject); diff --git a/osu.Game.Rulesets.Taiko/Objects/Swell.cs b/osu.Game.Rulesets.Taiko/Objects/Swell.cs index 390f8d1f3b..b4c09b884e 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Swell.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Swell.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.Threading; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Judgements; @@ -25,11 +24,6 @@ namespace osu.Game.Rulesets.Taiko.Objects /// public int RequiredHits = 10; - public override bool IsStrong - { - set => throw new NotSupportedException($"{nameof(Swell)} cannot be a strong hitobject."); - } - protected override void CreateNestedHitObjects(CancellationToken cancellationToken) { base.CreateNestedHitObjects(cancellationToken); diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs index 4de762ce30..2922010001 100644 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs @@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Taiko.Objects /// Whether this HitObject is a "strong" type. /// Strong hit objects give more points for hitting the hit object with both keys. /// - public virtual bool IsStrong + public bool IsStrong { get => IsStrongBindable.Value; set => IsStrongBindable.Value = value; diff --git a/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs b/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs index 5d7880278a..802bb80fa3 100644 --- a/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs +++ b/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs @@ -107,6 +107,8 @@ namespace osu.Game.Rulesets.Taiko h.IsStrong = false; break; } + + EditorBeatmap?.UpdateHitObject(h); } }) { diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index 7ab6340e07..38893f90a8 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -38,7 +38,7 @@ namespace osu.Game.Screens.Edit.Compose.Components private Drawable outline; [Resolved(CanBeNull = true)] - private EditorBeatmap editorBeatmap { get; set; } + protected EditorBeatmap EditorBeatmap { get; private set; } [Resolved(CanBeNull = true)] private IEditorChangeHandler changeHandler { get; set; } @@ -117,7 +117,7 @@ namespace osu.Game.Screens.Edit.Compose.Components internal void HandleSelected(SelectionBlueprint blueprint) { selectedBlueprints.Add(blueprint); - editorBeatmap.SelectedHitObjects.Add(blueprint.HitObject); + EditorBeatmap.SelectedHitObjects.Add(blueprint.HitObject); UpdateVisibility(); } @@ -129,7 +129,7 @@ namespace osu.Game.Screens.Edit.Compose.Components internal void HandleDeselected(SelectionBlueprint blueprint) { selectedBlueprints.Remove(blueprint); - editorBeatmap.SelectedHitObjects.Remove(blueprint.HitObject); + EditorBeatmap.SelectedHitObjects.Remove(blueprint.HitObject); // We don't want to update visibility if > 0, since we may be deselecting blueprints during drag-selection if (selectedBlueprints.Count == 0) @@ -165,7 +165,7 @@ namespace osu.Game.Screens.Edit.Compose.Components changeHandler?.BeginChange(); foreach (var h in selectedBlueprints.ToList()) - editorBeatmap?.Remove(h.HitObject); + EditorBeatmap?.Remove(h.HitObject); changeHandler?.EndChange(); } From 280b0adb1dc532938d12e4fd966fb7be033030d2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 May 2020 17:44:47 +0900 Subject: [PATCH 0951/1117] Split out IHasPath from IHasCurve to better define hitobjects --- .../Beatmaps/CatchBeatmapConverter.cs | 2 +- .../Objects/JuiceStream.cs | 2 +- .../Legacy/DistanceObjectPatternGenerator.cs | 2 +- .../Beatmaps/OsuBeatmapConverter.cs | 2 +- osu.Game.Rulesets.Osu/Objects/Slider.cs | 2 +- .../Beatmaps/TaikoBeatmapConverter.cs | 2 +- osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs | 10 +---- .../Formats/LegacyBeatmapDecoderTest.cs | 2 +- .../Beatmaps/Formats/OsuJsonDecoderTest.cs | 2 +- .../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 39 +++++++++++-------- .../Rulesets/Objects/Legacy/ConvertSlider.cs | 2 +- osu.Game/Rulesets/Objects/Types/IHasPath.cs | 13 +++++++ .../{IHasCurve.cs => IHasPathWithRepeats.cs} | 20 +++++----- 13 files changed, 57 insertions(+), 43 deletions(-) create mode 100644 osu.Game/Rulesets/Objects/Types/IHasPath.cs rename osu.Game/Rulesets/Objects/Types/{IHasCurve.cs => IHasPathWithRepeats.cs} (77%) diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs index 90a6e609f0..27a9b63e9a 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps switch (obj) { - case IHasCurve curveData: + case IHasPathWithRepeats curveData: return new JuiceStream { StartTime = obj.StartTime, diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs index d32595c2e1..24090e233a 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs @@ -14,7 +14,7 @@ using osu.Game.Rulesets.Objects.Types; namespace osu.Game.Rulesets.Catch.Objects { - public class JuiceStream : CatchHitObject, IHasCurve + public class JuiceStream : CatchHitObject, IHasPathWithRepeats { /// /// Positional distance that results in a duration of one second, before any speed adjustments. diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index d8d5b67c0e..1bd796511b 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -474,7 +474,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy /// private IList sampleInfoListAt(double time) { - if (!(HitObject is IHasCurve curveData)) + if (!(HitObject is IHasPathWithRepeats curveData)) return HitObject.Samples; double segmentTime = (EndTime - HitObject.StartTime) / spanCount; diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs index 147d74c929..060a3919bd 100644 --- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs +++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs @@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps switch (original) { - case IHasCurve curveData: + case IHasPathWithRepeats curveData: return new Slider { StartTime = original.StartTime, diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 6ba0e1c6aa..713d1a61f8 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -17,7 +17,7 @@ using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Objects { - public class Slider : OsuHitObject, IHasCurve + public class Slider : OsuHitObject, IHasPathWithRepeats { public double EndTime { diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index d324441285..1a47be2282 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -114,7 +114,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps if (!isForCurrentRuleset && tickSpacing > 0 && osuDuration < 2 * speedAdjustedBeatLength) { - List> allSamples = obj is IHasCurve curveData ? curveData.NodeSamples : new List>(new[] { samples }); + List> allSamples = obj is IHasPathWithRepeats curveData ? curveData.NodeSamples : new List>(new[] { samples }); int i = 0; diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs index 7b11bce520..5f52160be1 100644 --- a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs @@ -3,9 +3,7 @@ using osu.Game.Rulesets.Objects.Types; using System; -using System.Collections.Generic; using System.Threading; -using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Judgements; @@ -17,7 +15,7 @@ using osuTK; namespace osu.Game.Rulesets.Taiko.Objects { - public class DrumRoll : TaikoHitObject, IHasCurve + public class DrumRoll : TaikoHitObject, IHasPath { /// /// Drum roll distance that results in a duration of 1 speed-adjusted beat length. @@ -115,11 +113,7 @@ namespace osu.Game.Rulesets.Taiko.Objects double IHasDistance.Distance => Duration * Velocity; - int IHasRepeats.RepeatCount { get => 0; set { } } - - List> IHasRepeats.NodeSamples => new List>(); - - SliderPath IHasCurve.Path + SliderPath IHasPath.Path => new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(1) }, ((IHasDistance)this).Distance / TaikoBeatmapConverter.LEGACY_VELOCITY_MULTIPLIER); #endregion diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index acb30a6277..dab923d75b 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -365,7 +365,7 @@ namespace osu.Game.Tests.Beatmaps.Formats { var hitObjects = decoder.Decode(stream).HitObjects; - var curveData = hitObjects[0] as IHasCurve; + var curveData = hitObjects[0] as IHasPathWithRepeats; var positionData = hitObjects[0] as IHasPosition; Assert.IsNotNull(positionData); diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs index b034e66616..b4c78ce273 100644 --- a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs @@ -95,7 +95,7 @@ namespace osu.Game.Tests.Beatmaps.Formats { var beatmap = decodeAsJson(normal); - var curveData = beatmap.HitObjects[0] as IHasCurve; + var curveData = beatmap.HitObjects[0] as IHasPathWithRepeats; var positionData = beatmap.HitObjects[0] as IHasPosition; Assert.IsNotNull(positionData); diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 7727f25967..d7e83fa471 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -233,9 +233,9 @@ namespace osu.Game.Beatmaps.Formats writer.Write(FormattableString.Invariant($"{(int)getObjectType(hitObject)},")); writer.Write(FormattableString.Invariant($"{(int)toLegacyHitSoundType(hitObject.Samples)},")); - if (hitObject is IHasCurve curveData) + if (hitObject is IHasPathWithRepeats curveData) { - addCurveData(writer, curveData, position); + addPathData(writer, curveData, position); writer.Write(getSampleBank(hitObject.Samples, zeroBanks: true)); } else @@ -263,7 +263,7 @@ namespace osu.Game.Beatmaps.Formats switch (hitObject) { - case IHasCurve _: + case IHasPath _: type |= LegacyHitObjectType.Slider; break; @@ -282,13 +282,13 @@ namespace osu.Game.Beatmaps.Formats return type; } - private void addCurveData(TextWriter writer, IHasCurve curveData, Vector2 position) + private void addPathData(TextWriter writer, IHasPath pathData, Vector2 position) { PathType? lastType = null; - for (int i = 0; i < curveData.Path.ControlPoints.Count; i++) + for (int i = 0; i < pathData.Path.ControlPoints.Count; i++) { - PathControlPoint point = curveData.Path.ControlPoints[i]; + PathControlPoint point = pathData.Path.ControlPoints[i]; if (point.Type.Value != null) { @@ -325,23 +325,28 @@ namespace osu.Game.Beatmaps.Formats if (i != 0) { writer.Write(FormattableString.Invariant($"{position.X + point.Position.Value.X}:{position.Y + point.Position.Value.Y}")); - writer.Write(i != curveData.Path.ControlPoints.Count - 1 ? "|" : ","); + writer.Write(i != pathData.Path.ControlPoints.Count - 1 ? "|" : ","); } } - writer.Write(FormattableString.Invariant($"{curveData.RepeatCount + 1},")); - writer.Write(FormattableString.Invariant($"{curveData.Path.Distance},")); + var curveData = pathData as IHasPathWithRepeats; - for (int i = 0; i < curveData.NodeSamples.Count; i++) - { - writer.Write(FormattableString.Invariant($"{(int)toLegacyHitSoundType(curveData.NodeSamples[i])}")); - writer.Write(i != curveData.NodeSamples.Count - 1 ? "|" : ","); - } + writer.Write(FormattableString.Invariant($"{(curveData?.RepeatCount ?? 0) + 1},")); + writer.Write(FormattableString.Invariant($"{pathData.Path.Distance},")); - for (int i = 0; i < curveData.NodeSamples.Count; i++) + if (curveData != null) { - writer.Write(getSampleBank(curveData.NodeSamples[i], true)); - writer.Write(i != curveData.NodeSamples.Count - 1 ? "|" : ","); + for (int i = 0; i < curveData.NodeSamples.Count; i++) + { + writer.Write(FormattableString.Invariant($"{(int)toLegacyHitSoundType(curveData.NodeSamples[i])}")); + writer.Write(i != curveData.NodeSamples.Count - 1 ? "|" : ","); + } + + for (int i = 0; i < curveData.NodeSamples.Count; i++) + { + writer.Write(getSampleBank(curveData.NodeSamples[i], true)); + writer.Write(i != curveData.NodeSamples.Count - 1 ? "|" : ","); + } } } diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs index 924182b265..73192dc42e 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs @@ -9,7 +9,7 @@ using osu.Game.Beatmaps.ControlPoints; namespace osu.Game.Rulesets.Objects.Legacy { - internal abstract class ConvertSlider : ConvertHitObject, IHasCurve, IHasLegacyLastTickOffset + internal abstract class ConvertSlider : ConvertHitObject, IHasPathWithRepeats, IHasLegacyLastTickOffset { /// /// Scoring distance with a speed-adjusted beat length of 1 second. diff --git a/osu.Game/Rulesets/Objects/Types/IHasPath.cs b/osu.Game/Rulesets/Objects/Types/IHasPath.cs new file mode 100644 index 0000000000..567c24a4a2 --- /dev/null +++ b/osu.Game/Rulesets/Objects/Types/IHasPath.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.Rulesets.Objects.Types +{ + public interface IHasPath : IHasDistance + { + /// + /// The curve. + /// + SliderPath Path { get; } + } +} diff --git a/osu.Game/Rulesets/Objects/Types/IHasCurve.cs b/osu.Game/Rulesets/Objects/Types/IHasPathWithRepeats.cs similarity index 77% rename from osu.Game/Rulesets/Objects/Types/IHasCurve.cs rename to osu.Game/Rulesets/Objects/Types/IHasPathWithRepeats.cs index e98a888bd7..fba0fd7aff 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasCurve.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasPathWithRepeats.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 osuTK; namespace osu.Game.Rulesets.Objects.Types @@ -8,15 +9,16 @@ namespace osu.Game.Rulesets.Objects.Types /// /// A HitObject that has a curve. /// - public interface IHasCurve : IHasDistance, IHasRepeats + public interface IHasPathWithRepeats : IHasPath, IHasRepeats { - /// - /// The curve. - /// - SliderPath Path { get; } } - public static class HasCurveExtensions + [Obsolete("Use IHasPathWithRepeats instead.")] // can be removed 20201126 + public interface IHasCurve : IHasPathWithRepeats + { + } + + public static class HasPathWithRepeatsExtensions { /// /// Computes the position on the curve relative to how much of the has been completed. @@ -24,7 +26,7 @@ namespace osu.Game.Rulesets.Objects.Types /// The curve. /// [0, 1] where 0 is the start time of the and 1 is the end time of the . /// The position on the curve. - public static Vector2 CurvePositionAt(this IHasCurve obj, double progress) + public static Vector2 CurvePositionAt(this IHasPathWithRepeats obj, double progress) => obj.Path.PositionAt(obj.ProgressAt(progress)); /// @@ -33,7 +35,7 @@ namespace osu.Game.Rulesets.Objects.Types /// The curve. /// [0, 1] where 0 is the start time of the and 1 is the end time of the . /// [0, 1] where 0 is the beginning of the curve and 1 is the end of the curve. - public static double ProgressAt(this IHasCurve obj, double progress) + public static double ProgressAt(this IHasPathWithRepeats obj, double progress) { double p = progress * obj.SpanCount() % 1; if (obj.SpanAt(progress) % 2 == 1) @@ -47,7 +49,7 @@ namespace osu.Game.Rulesets.Objects.Types /// The curve. /// [0, 1] where 0 is the beginning of the curve and 1 is the end of the curve. /// [0, SpanCount) where 0 is the first run. - public static int SpanAt(this IHasCurve obj, double progress) + public static int SpanAt(this IHasPathWithRepeats obj, double progress) => (int)(progress * obj.SpanCount()); } } From a953f9e422810198b1d431b52f4b365c912c4e84 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 May 2020 20:30:33 +0900 Subject: [PATCH 0952/1117] Add drum roll composition support --- .../TaikoHitObjectComposer.cs | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs b/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs index 802bb80fa3..d233cd5e7f 100644 --- a/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs +++ b/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs @@ -34,9 +34,10 @@ namespace osu.Game.Rulesets.Taiko { } - protected override IReadOnlyList CompositionTools => new[] + protected override IReadOnlyList CompositionTools => new HitObjectCompositionTool[] { - new HitCompositionTool() + new HitCompositionTool(), + new DrumRollCompositionTool() }; protected override ComposeBlueprintContainer CreateBlueprintContainer() => new TaikoBlueprintContainer(drawableRuleset.Playfield.AllHitObjects); @@ -156,6 +157,26 @@ namespace osu.Game.Rulesets.Taiko } } + public class DrumRollCompositionTool : HitObjectCompositionTool + { + public DrumRollCompositionTool() + : base(nameof(DrumRoll)) + { + } + + public override PlacementBlueprint CreatePlacementBlueprint() => new DrumRollPlacementBlueprint(); + } + + public class DrumRollPlacementBlueprint : PlacementBlueprint + { + private static DrumRoll drumRoll; + + public DrumRollPlacementBlueprint() + : base(drumRoll = new DrumRoll()) + { + } + } + public class HitCompositionTool : HitObjectCompositionTool { public HitCompositionTool() From 534dccc0c384a0e33a6fbe49c1b3b2acc97d56b3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 May 2020 12:37:44 +0900 Subject: [PATCH 0953/1117] Move sett from EndTime to Duration --- osu.Game.Rulesets.Catch/Objects/JuiceStream.cs | 10 +++++----- osu.Game.Rulesets.Osu/Objects/Slider.cs | 8 ++++---- .../Objects/Drawables/DrawableSwell.cs | 2 +- .../Gameplay/TestSceneDrawableScrollingRuleset.cs | 6 +++--- .../Objects/Legacy/Catch/ConvertHitObjectParser.cs | 6 +++--- .../Objects/Legacy/Catch/ConvertSpinner.cs | 4 ++-- .../Objects/Legacy/ConvertHitObjectParser.cs | 14 +++++++------- osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs | 6 +++--- .../Objects/Legacy/Mania/ConvertHitObjectParser.cs | 8 ++++---- .../Rulesets/Objects/Legacy/Mania/ConvertHold.cs | 4 ++-- .../Objects/Legacy/Mania/ConvertSpinner.cs | 4 ++-- .../Objects/Legacy/Osu/ConvertHitObjectParser.cs | 6 +++--- .../Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs | 4 ++-- .../Objects/Legacy/Taiko/ConvertHitObjectParser.cs | 6 +++--- .../Objects/Legacy/Taiko/ConvertSpinner.cs | 4 ++-- osu.Game/Rulesets/Objects/Types/IHasEndTime.cs | 6 +++--- .../Timeline/TimelineHitObjectBlueprint.cs | 2 +- 17 files changed, 50 insertions(+), 50 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs index 24090e233a..2c96ee2b19 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs @@ -115,15 +115,15 @@ namespace osu.Game.Rulesets.Catch.Objects } } - public double EndTime + public float EndX => X + this.CurvePositionAt(1).X / CatchPlayfield.BASE_WIDTH; + + public double Duration { - get => StartTime + this.SpanCount() * Path.Distance / Velocity; + get => this.SpanCount() * Path.Distance / Velocity; set => throw new System.NotSupportedException($"Adjust via {nameof(RepeatCount)} instead"); // can be implemented if/when needed. } - public float EndX => X + this.CurvePositionAt(1).X / CatchPlayfield.BASE_WIDTH; - - public double Duration => EndTime - StartTime; + public double EndTime => StartTime + Duration; private readonly SliderPath path = new SliderPath(); diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 713d1a61f8..705e88040f 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -19,14 +19,14 @@ namespace osu.Game.Rulesets.Osu.Objects { public class Slider : OsuHitObject, IHasPathWithRepeats { - public double EndTime + public double EndTime => StartTime + this.SpanCount() * Path.Distance / Velocity; + + public double Duration { - get => StartTime + this.SpanCount() * Path.Distance / Velocity; + get => EndTime - StartTime; set => throw new System.NotSupportedException($"Adjust via {nameof(RepeatCount)} instead"); // can be implemented if/when needed. } - public double Duration => EndTime - StartTime; - private readonly Cached endPositionCache = new Cached(); public override Vector2 EndPosition => endPositionCache.IsValid ? endPositionCache.Value : endPositionCache.Value = Position + this.CurvePositionAt(1); diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs index 32f7acadc8..7294587b10 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs @@ -237,7 +237,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables case ArmedState.Miss: case ArmedState.Hit: - using (BeginAbsoluteSequence(Time.Current, true)) + using (BeginDelayedSequence(HitObject.Duration, true)) { this.FadeOut(transition_duration, Easing.Out); bodyContainer.ScaleTo(1.4f, transition_duration); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs index b25b81c9af..08fd849fa6 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs @@ -281,7 +281,7 @@ namespace osu.Game.Tests.Visual.Gameplay yield return new TestHitObject { StartTime = original.StartTime, - EndTime = (original as IHasEndTime)?.EndTime ?? (original.StartTime + 100) + Duration = (original as IHasEndTime)?.Duration ?? 100 }; } } @@ -292,9 +292,9 @@ namespace osu.Game.Tests.Visual.Gameplay private class TestHitObject : ConvertHitObject, IHasEndTime { - public double EndTime { get; set; } + public double EndTime => StartTime + Duration; - public double Duration => EndTime - StartTime; + public double Duration { get; set; } } private class DrawableTestHitObject : DrawableHitObject diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs index 43e8d01297..c10c8dc30f 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs @@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch }; } - protected override HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double endTime) + 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 @@ -65,11 +65,11 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch return new ConvertSpinner { - EndTime = endTime + Duration = duration }; } - protected override HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double endTime) + protected override HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double duration) { return null; } diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs index 9de311c9d7..4b0270064a 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs @@ -10,9 +10,9 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch /// internal sealed class ConvertSpinner : ConvertHitObject, IHasEndTime, IHasXPosition, IHasCombo { - public double EndTime { get; set; } + public double EndTime => StartTime + Duration; - public double Duration => EndTime - StartTime; + public double Duration { get; set; } public float X => 256; // Required for CatchBeatmapConverter diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 9a60a0a75c..d8d90fddfa 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -189,9 +189,9 @@ namespace osu.Game.Rulesets.Objects.Legacy } else if (type.HasFlag(LegacyHitObjectType.Spinner)) { - double endTime = Math.Max(startTime, Parsing.ParseDouble(split[5]) + Offset); + double duration = Math.Max(0, Parsing.ParseDouble(split[5]) + Offset - startTime); - result = CreateSpinner(new Vector2(512, 384) / 2, combo, comboOffset, endTime); + result = CreateSpinner(new Vector2(512, 384) / 2, combo, comboOffset, duration); if (split.Length > 6) readCustomSampleBanks(split[6], bankInfo); @@ -209,7 +209,7 @@ namespace osu.Game.Rulesets.Objects.Legacy readCustomSampleBanks(string.Join(":", ss.Skip(1)), bankInfo); } - result = CreateHold(pos, combo, comboOffset, endTime + Offset); + result = CreateHold(pos, combo, comboOffset, endTime + Offset - startTime); } if (result == null) @@ -321,9 +321,9 @@ namespace osu.Game.Rulesets.Objects.Legacy /// The position of the hit object. /// Whether the hit object creates a new combo. /// When starting a new combo, the offset of the new combo relative to the current one. - /// The spinner end time. + /// The spinner duration. /// The hit object. - protected abstract HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double endTime); + protected abstract HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double duration); /// /// Creates a legacy Hold-type hit object. @@ -331,8 +331,8 @@ namespace osu.Game.Rulesets.Objects.Legacy /// The position of the hit object. /// Whether the hit object creates a new combo. /// When starting a new combo, the offset of the new combo relative to the current one. - /// The hold end time. - protected abstract HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double endTime); + /// The hold end time. + protected abstract HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double duration); private List convertSoundType(LegacyHitSoundType type, SampleBankInfo bankInfo) { diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs index 73192dc42e..cd2f9f88b8 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs @@ -26,13 +26,13 @@ namespace osu.Game.Rulesets.Objects.Legacy public List> NodeSamples { get; set; } public int RepeatCount { get; set; } - public double EndTime + public double Duration { - get => StartTime + this.SpanCount() * Distance / Velocity; + get => this.SpanCount() * Distance / Velocity; set => throw new System.NotSupportedException($"Adjust via {nameof(RepeatCount)} instead"); // can be implemented if/when needed. } - public double Duration => EndTime - StartTime; + public double EndTime => StartTime + Duration; public double Velocity = 1; diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs index f94c4aaa75..bc64518f40 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs @@ -37,21 +37,21 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania }; } - protected override HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double endTime) + protected override HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double duration) { return new ConvertSpinner { X = position.X, - EndTime = endTime + Duration = duration }; } - protected override HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double endTime) + protected override HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double duration) { return new ConvertHold { X = position.X, - EndTime = endTime + Duration = duration }; } } diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHold.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHold.cs index 1d92d638dd..dcb66163e4 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHold.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHold.cs @@ -9,8 +9,8 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania { public float X { get; set; } - public double EndTime { get; set; } + public double Duration { get; set; } - public double Duration => EndTime - StartTime; + public double EndTime => StartTime + Duration; } } diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSpinner.cs index 7dc13e27cd..b731f7c8d8 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSpinner.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSpinner.cs @@ -10,9 +10,9 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania /// internal sealed class ConvertSpinner : ConvertHitObject, IHasEndTime, IHasXPosition { - public double EndTime { get; set; } + public double Duration { get; set; } - public double Duration => EndTime - StartTime; + public double EndTime => StartTime + Duration; public float X { get; set; } } diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs index b95ec703b6..75ecab0b8f 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs @@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu }; } - protected override HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double endTime) + 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 @@ -66,11 +66,11 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu return new ConvertSpinner { Position = position, - EndTime = endTime + Duration = duration }; } - protected override HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double endTime) + protected override HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double duration) { return null; } diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs index 8b21aab411..a231237077 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs @@ -11,9 +11,9 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu /// internal sealed class ConvertSpinner : ConvertHitObject, IHasEndTime, IHasPosition, IHasCombo { - public double EndTime { get; set; } + public double Duration { get; set; } - public double Duration => EndTime - StartTime; + public double EndTime => StartTime + Duration; public Vector2 Position { get; set; } diff --git a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs index db65a61c90..13e3e84c6a 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs @@ -33,15 +33,15 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko }; } - protected override HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double endTime) + protected override HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double duration) { return new ConvertSpinner { - EndTime = endTime + Duration = duration }; } - protected override HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double endTime) + protected override HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double duration) { return null; } diff --git a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSpinner.cs index 8e28487f2f..0976106ec4 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSpinner.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSpinner.cs @@ -10,8 +10,8 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko /// internal sealed class ConvertSpinner : ConvertHitObject, IHasEndTime { - public double EndTime { get; set; } + public double Duration { get; set; } - public double Duration => EndTime - StartTime; + public double EndTime => StartTime + Duration; } } diff --git a/osu.Game/Rulesets/Objects/Types/IHasEndTime.cs b/osu.Game/Rulesets/Objects/Types/IHasEndTime.cs index bc7103c60d..5eb551e15c 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasEndTime.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasEndTime.cs @@ -13,12 +13,12 @@ namespace osu.Game.Rulesets.Objects.Types /// /// The time at which the HitObject ends. /// - [JsonIgnore] - double EndTime { get; set; } + double EndTime { get; } /// /// The duration of the HitObject. /// - double Duration { get; } + [JsonIgnore] + double Duration { get; set; } } } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index dd2f7a833e..d6fc17f358 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -296,7 +296,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline if (endTimeHitObject.EndTime == snappedTime) return; - endTimeHitObject.EndTime = snappedTime; + endTimeHitObject.Duration = snappedTime - hitObject.StartTime; break; } From dd7dbfd5488efddef6d56f897876ce6cba4eb32d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 May 2020 12:38:39 +0900 Subject: [PATCH 0954/1117] Rename to IHasDuration --- .../Beatmaps/CatchBeatmapConverter.cs | 2 +- .../Objects/BananaShower.cs | 2 +- .../TestSceneNotes.cs | 2 +- .../Beatmaps/ManiaBeatmapConverter.cs | 6 ++--- .../Legacy/EndTimeObjectPatternGenerator.cs | 2 +- osu.Game.Rulesets.Mania/Objects/HoldNote.cs | 2 +- .../Beatmaps/OsuBeatmapConverter.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs | 2 +- osu.Game.Rulesets.Osu/Objects/Spinner.cs | 2 +- .../Beatmaps/TaikoBeatmapConverter.cs | 2 +- osu.Game.Rulesets.Taiko/Objects/Swell.cs | 2 +- .../TestSceneDrawableScrollingRuleset.cs | 4 ++-- .../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 6 ++--- osu.Game/Beatmaps/WorkingBeatmap.cs | 2 +- osu.Game/Rulesets/Objects/HitObject.cs | 4 ++-- .../Objects/Legacy/Catch/ConvertSpinner.cs | 2 +- .../Objects/Legacy/Mania/ConvertHold.cs | 2 +- .../Objects/Legacy/Mania/ConvertSpinner.cs | 2 +- .../Objects/Legacy/Osu/ConvertSpinner.cs | 2 +- .../Objects/Legacy/Taiko/ConvertSpinner.cs | 2 +- .../Rulesets/Objects/Types/IHasDistance.cs | 2 +- .../Rulesets/Objects/Types/IHasDuration.cs | 24 +++++++++++++++++++ .../Rulesets/Objects/Types/IHasEndTime.cs | 20 ++++------------ .../Rulesets/Objects/Types/IHasRepeats.cs | 2 +- .../Scrolling/ScrollingHitObjectContainer.cs | 2 +- .../Timeline/TimelineHitObjectBlueprint.cs | 4 ++-- 27 files changed, 60 insertions(+), 48 deletions(-) create mode 100644 osu.Game/Rulesets/Objects/Types/IHasDuration.cs diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs index 27a9b63e9a..0de2060e2d 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs @@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps LegacyLastTickOffset = (obj as IHasLegacyLastTickOffset)?.LegacyLastTickOffset ?? 0 }.Yield(); - case IHasEndTime endTime: + case IHasDuration endTime: return new BananaShower { StartTime = obj.StartTime, diff --git a/osu.Game.Rulesets.Catch/Objects/BananaShower.cs b/osu.Game.Rulesets.Catch/Objects/BananaShower.cs index 3a0b5ace53..04a995c77e 100644 --- a/osu.Game.Rulesets.Catch/Objects/BananaShower.cs +++ b/osu.Game.Rulesets.Catch/Objects/BananaShower.cs @@ -7,7 +7,7 @@ using osu.Game.Rulesets.Objects.Types; namespace osu.Game.Rulesets.Catch.Objects { - public class BananaShower : CatchHitObject, IHasEndTime + public class BananaShower : CatchHitObject, IHasDuration { public override FruitVisualRepresentation VisualRepresentation => FruitVisualRepresentation.Banana; diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneNotes.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneNotes.cs index ea6a1e2e6a..dd5fd93710 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneNotes.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneNotes.cs @@ -156,7 +156,7 @@ namespace osu.Game.Rulesets.Mania.Tests foreach (var obj in content.OfType()) { - if (!(obj.HitObject is IHasEndTime endTime)) + if (!(obj.HitObject is IHasDuration endTime)) continue; foreach (var nested in obj.NestedHitObjects) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index 1c8116754f..32abf5e7f9 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -54,7 +54,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps } else { - float percentSliderOrSpinner = (float)beatmap.HitObjects.Count(h => h is IHasEndTime) / beatmap.HitObjects.Count; + float percentSliderOrSpinner = (float)beatmap.HitObjects.Count(h => h is IHasDuration) / beatmap.HitObjects.Count; if (percentSliderOrSpinner < 0.2) TargetColumns = 7; else if (percentSliderOrSpinner < 0.3 || roundedCircleSize >= 5) @@ -175,7 +175,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps break; } - case IHasEndTime endTimeData: + case IHasDuration endTimeData: { conversion = new EndTimeObjectPatternGenerator(Random, original, beatmap, originalBeatmap); @@ -231,7 +231,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps var pattern = new Pattern(); - if (HitObject is IHasEndTime endTimeData) + if (HitObject is IHasDuration endTimeData) { pattern.Add(new HoldNote { diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs index 907bed0d65..d5286a3779 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, IBeatmap originalBeatmap) : base(random, hitObject, beatmap, new Pattern(), originalBeatmap) { - endTime = (HitObject as IHasEndTime)?.EndTime ?? 0; + endTime = (HitObject as IHasDuration)?.EndTime ?? 0; } public override IEnumerable Generate() diff --git a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs index e6f722a8a9..a100c9a58e 100644 --- a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mania.Objects /// /// Represents a hit object which requires pressing, holding, and releasing a key. /// - public class HoldNote : ManiaHitObject, IHasEndTime + public class HoldNote : ManiaHitObject, IHasDuration { public double EndTime { diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs index 060a3919bd..fcad356a1c 100644 --- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs +++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs @@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps TickDistanceMultiplier = beatmap.BeatmapInfo.BeatmapVersion < 8 ? 1f / beatmap.ControlPointInfo.DifficultyPointAt(original.StartTime).SpeedMultiplier : 1 }.Yield(); - case IHasEndTime endTimeData: + case IHasDuration endTimeData: return new Spinner { StartTime = original.StartTime, diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs index 7b1941b7f9..5d191119b9 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs @@ -70,7 +70,7 @@ namespace osu.Game.Rulesets.Osu.Mods break; // already hit or beyond the hittable end time. - if (h.IsHit || (h.HitObject is IHasEndTime hasEnd && time > hasEnd.EndTime)) + if (h.IsHit || (h.HitObject is IHasDuration hasEnd && time > hasEnd.EndTime)) continue; switch (h) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs b/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs index 297a0fea79..3cad52faeb 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs @@ -61,7 +61,7 @@ namespace osu.Game.Rulesets.Osu.Mods } // Keep wiggling sliders and spinners for their duration - if (!(osuObject is IHasEndTime endTime)) + if (!(osuObject is IHasDuration endTime)) return; amountWiggles = (int)(endTime.Duration / wiggle_duration); diff --git a/osu.Game.Rulesets.Osu/Objects/Spinner.cs b/osu.Game.Rulesets.Osu/Objects/Spinner.cs index 0b8d03d118..418375c090 100644 --- a/osu.Game.Rulesets.Osu/Objects/Spinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Spinner.cs @@ -11,7 +11,7 @@ using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Objects { - public class Spinner : OsuHitObject, IHasEndTime + public class Spinner : OsuHitObject, IHasDuration { public double EndTime { diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index 1a47be2282..78550ed270 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -150,7 +150,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps break; } - case IHasEndTime endTimeData: + case IHasDuration endTimeData: { double hitMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, 3, 5, 7.5) * swell_hit_multiplier; diff --git a/osu.Game.Rulesets.Taiko/Objects/Swell.cs b/osu.Game.Rulesets.Taiko/Objects/Swell.cs index b4c09b884e..eeae6e79f8 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Swell.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Swell.cs @@ -9,7 +9,7 @@ using osu.Game.Rulesets.Taiko.Judgements; namespace osu.Game.Rulesets.Taiko.Objects { - public class Swell : TaikoHitObject, IHasEndTime + public class Swell : TaikoHitObject, IHasDuration { public double EndTime { diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs index 08fd849fa6..bd7e894cf8 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs @@ -281,7 +281,7 @@ namespace osu.Game.Tests.Visual.Gameplay yield return new TestHitObject { StartTime = original.StartTime, - Duration = (original as IHasEndTime)?.Duration ?? 100 + Duration = (original as IHasDuration)?.Duration ?? 100 }; } } @@ -290,7 +290,7 @@ namespace osu.Game.Tests.Visual.Gameplay #region HitObject - private class TestHitObject : ConvertHitObject, IHasEndTime + private class TestHitObject : ConvertHitObject, IHasDuration { public double EndTime => StartTime + Duration; diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index d7e83fa471..8c63ce6fcb 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -240,7 +240,7 @@ namespace osu.Game.Beatmaps.Formats } else { - if (hitObject is IHasEndTime) + if (hitObject is IHasDuration) addEndTimeData(writer, hitObject); writer.Write(getSampleBank(hitObject.Samples)); @@ -267,7 +267,7 @@ namespace osu.Game.Beatmaps.Formats type |= LegacyHitObjectType.Slider; break; - case IHasEndTime _: + case IHasDuration _: if (beatmap.BeatmapInfo.RulesetID == 3) type |= LegacyHitObjectType.Hold; else @@ -352,7 +352,7 @@ namespace osu.Game.Beatmaps.Formats private void addEndTimeData(TextWriter writer, HitObject hitObject) { - var endTimeData = (IHasEndTime)hitObject; + var endTimeData = (IHasDuration)hitObject; var type = getObjectType(hitObject); char suffix = ','; diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 8126311cbd..ac399e37c4 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -63,7 +63,7 @@ namespace osu.Game.Beatmaps length = emptyLength; break; - case IHasEndTime endTime: + case IHasDuration endTime: length = endTime.EndTime + excess_length; break; diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs index 6f9053d7cb..e2cc98813a 100644 --- a/osu.Game/Rulesets/Objects/HitObject.cs +++ b/osu.Game/Rulesets/Objects/HitObject.cs @@ -175,10 +175,10 @@ namespace osu.Game.Rulesets.Objects /// Returns the end time of this object. /// /// - /// This returns the where available, falling back to otherwise. + /// This returns the where available, falling back to otherwise. /// /// The object. /// The end time of this object. - public static double GetEndTime(this HitObject hitObject) => (hitObject as IHasEndTime)?.EndTime ?? hitObject.StartTime; + public static double GetEndTime(this HitObject hitObject) => (hitObject as IHasDuration)?.EndTime ?? hitObject.StartTime; } } diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs index 4b0270064a..014494ec54 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs @@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch /// /// Legacy osu!catch Spinner-type, used for parsing Beatmaps. /// - internal sealed class ConvertSpinner : ConvertHitObject, IHasEndTime, IHasXPosition, IHasCombo + internal sealed class ConvertSpinner : ConvertHitObject, IHasDuration, IHasXPosition, IHasCombo { public double EndTime => StartTime + Duration; diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHold.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHold.cs index dcb66163e4..2fa4766c1d 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHold.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHold.cs @@ -5,7 +5,7 @@ using osu.Game.Rulesets.Objects.Types; namespace osu.Game.Rulesets.Objects.Legacy.Mania { - internal sealed class ConvertHold : ConvertHitObject, IHasXPosition, IHasEndTime + internal sealed class ConvertHold : ConvertHitObject, IHasXPosition, IHasDuration { public float X { get; set; } diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSpinner.cs index b731f7c8d8..c05aaceb9c 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSpinner.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSpinner.cs @@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania /// /// Legacy osu!mania Spinner-type, used for parsing Beatmaps. /// - internal sealed class ConvertSpinner : ConvertHitObject, IHasEndTime, IHasXPosition + internal sealed class ConvertSpinner : ConvertHitObject, IHasDuration, IHasXPosition { public double Duration { get; set; } diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs index a231237077..e9e5ca8c94 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, IHasEndTime, IHasPosition, IHasCombo + internal sealed class ConvertSpinner : ConvertHitObject, IHasDuration, IHasPosition, IHasCombo { public double Duration { get; set; } diff --git a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSpinner.cs index 0976106ec4..1d5ecb1ef3 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSpinner.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSpinner.cs @@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko /// /// Legacy osu!taiko Spinner-type, used for parsing Beatmaps. /// - internal sealed class ConvertSpinner : ConvertHitObject, IHasEndTime + internal sealed class ConvertSpinner : ConvertHitObject, IHasDuration { public double Duration { get; set; } diff --git a/osu.Game/Rulesets/Objects/Types/IHasDistance.cs b/osu.Game/Rulesets/Objects/Types/IHasDistance.cs index e7f552115e..b497ca5da3 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasDistance.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasDistance.cs @@ -6,7 +6,7 @@ namespace osu.Game.Rulesets.Objects.Types /// /// A HitObject that has a positional length. /// - public interface IHasDistance : IHasEndTime + public interface IHasDistance : IHasDuration { /// /// The positional length of the HitObject. diff --git a/osu.Game/Rulesets/Objects/Types/IHasDuration.cs b/osu.Game/Rulesets/Objects/Types/IHasDuration.cs new file mode 100644 index 0000000000..2433f9597e --- /dev/null +++ b/osu.Game/Rulesets/Objects/Types/IHasDuration.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 Newtonsoft.Json; + +namespace osu.Game.Rulesets.Objects.Types +{ + /// + /// A HitObject that ends at a different time than its start time. + /// + public interface IHasDuration + { + /// + /// The time at which the HitObject ends. + /// + double EndTime { get; } + + /// + /// The duration of the HitObject. + /// + [JsonIgnore] + double Duration { get; set; } + } +} diff --git a/osu.Game/Rulesets/Objects/Types/IHasEndTime.cs b/osu.Game/Rulesets/Objects/Types/IHasEndTime.cs index 5eb551e15c..7395223c7e 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasEndTime.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasEndTime.cs @@ -1,24 +1,12 @@ -// 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 Newtonsoft.Json; +using System; namespace osu.Game.Rulesets.Objects.Types { - /// - /// A HitObject that ends at a different time than its start time. - /// - public interface IHasEndTime + [Obsolete("Use IHasDuration instead.")] // can be removed 20201126 + public interface IHasEndTime : IHasDuration { - /// - /// The time at which the HitObject ends. - /// - double EndTime { get; } - - /// - /// The duration of the HitObject. - /// - [JsonIgnore] - double Duration { get; set; } } } diff --git a/osu.Game/Rulesets/Objects/Types/IHasRepeats.cs b/osu.Game/Rulesets/Objects/Types/IHasRepeats.cs index 256b1f3963..7a3fb16196 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasRepeats.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasRepeats.cs @@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Objects.Types /// /// A HitObject that spans some length. /// - public interface IHasRepeats : IHasEndTime + public interface IHasRepeats : IHasDuration { /// /// The amount of times the HitObject repeats. diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index c817d84d5c..0dc3324559 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -270,7 +270,7 @@ namespace osu.Game.Rulesets.UI.Scrolling // Cant use AddOnce() since the delegate is re-constructed every invocation private void computeInitialStateRecursive(DrawableHitObject hitObject) => hitObject.Schedule(() => { - if (hitObject.HitObject is IHasEndTime e) + if (hitObject.HitObject is IHasDuration e) { switch (direction.Value) { diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index d6fc17f358..b95b3842b3 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -72,7 +72,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline shadowComponents.Add(circle); - if (hitObject is IHasEndTime) + if (hitObject is IHasDuration) { DragBar dragBarUnderlay; Container extensionBar; @@ -290,7 +290,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline repeatHitObject.RepeatCount = proposedCount; break; - case IHasEndTime endTimeHitObject: + case IHasDuration endTimeHitObject: var snappedTime = Math.Max(hitObject.StartTime, beatSnapProvider.SnapTime(time)); if (endTimeHitObject.EndTime == snappedTime) From b2fad915898c445cfe8ef13888de52dc08d232d7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 May 2020 20:33:12 +0900 Subject: [PATCH 0955/1117] Add swell and drumroll blueprints --- .../TaikoHitObjectComposer.cs | 155 +++++++++++++++++- osu.Game/Rulesets/Edit/PlacementBlueprint.cs | 6 +- 2 files changed, 149 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs b/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs index d233cd5e7f..2bb037815e 100644 --- a/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs +++ b/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs @@ -14,7 +14,9 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.UI; using osu.Game.Rulesets.UI; @@ -37,7 +39,8 @@ namespace osu.Game.Rulesets.Taiko protected override IReadOnlyList CompositionTools => new HitObjectCompositionTool[] { new HitCompositionTool(), - new DrumRollCompositionTool() + new DrumRollCompositionTool(), + new SwellCompositionTool() }; protected override ComposeBlueprintContainer CreateBlueprintContainer() => new TaikoBlueprintContainer(drawableRuleset.Playfield.AllHitObjects); @@ -157,6 +160,16 @@ namespace osu.Game.Rulesets.Taiko } } + public class SwellCompositionTool : HitObjectCompositionTool + { + public SwellCompositionTool() + : base(nameof(Swell)) + { + } + + public override PlacementBlueprint CreatePlacementBlueprint() => new SwellPlacementBlueprint(); + } + public class DrumRollCompositionTool : HitObjectCompositionTool { public DrumRollCompositionTool() @@ -167,16 +180,110 @@ namespace osu.Game.Rulesets.Taiko public override PlacementBlueprint CreatePlacementBlueprint() => new DrumRollPlacementBlueprint(); } - public class DrumRollPlacementBlueprint : PlacementBlueprint + public class SwellPlacementBlueprint : TaikoSpanPlacementBlueprint { - private static DrumRoll drumRoll; - - public DrumRollPlacementBlueprint() - : base(drumRoll = new DrumRoll()) + public SwellPlacementBlueprint() + : base(new Swell()) { } } + public class DrumRollPlacementBlueprint : TaikoSpanPlacementBlueprint + { + public DrumRollPlacementBlueprint() + : base(new DrumRoll()) + { + } + } + + public class TaikoSpanPlacementBlueprint : PlacementBlueprint + { + private readonly HitPiece headPiece; + private readonly HitPiece tailPiece; + + private readonly LengthPiece lengthPiece; + + private readonly IHasDuration spanPlacementObject; + + public TaikoSpanPlacementBlueprint(HitObject hitObject) + : base(hitObject) + + { + spanPlacementObject = hitObject as IHasDuration; + + RelativeSizeAxes = Axes.Both; + + InternalChildren = new Drawable[] + { + headPiece = new HitPiece + { + Size = new Vector2(TaikoHitObject.DEFAULT_SIZE * TaikoPlayfield.DEFAULT_HEIGHT) + }, + lengthPiece = new LengthPiece + { + Height = TaikoHitObject.DEFAULT_SIZE * TaikoPlayfield.DEFAULT_HEIGHT + }, + tailPiece = new HitPiece + { + Size = new Vector2(TaikoHitObject.DEFAULT_SIZE * TaikoPlayfield.DEFAULT_HEIGHT) + } + }; + } + + private double originalStartTime; + + protected override bool OnMouseDown(MouseDownEvent e) + { + if (e.Button != MouseButton.Left) + return false; + + BeginPlacement(true); + return true; + } + + protected override void OnMouseUp(MouseUpEvent e) + { + if (e.Button != MouseButton.Left) + return; + + base.OnMouseUp(e); + EndPlacement(true); + } + + public override void UpdatePosition(SnapResult result) + { + base.UpdatePosition(result); + + if (PlacementActive) + { + if (result.Time is double endTime) + { + if (endTime < originalStartTime) + { + HitObject.StartTime = endTime; + spanPlacementObject.Duration = Math.Abs(endTime - originalStartTime); + headPiece.Position = ToLocalSpace(result.ScreenSpacePosition); + lengthPiece.X = headPiece.X; + lengthPiece.Width = tailPiece.X - headPiece.X; + } + else + { + spanPlacementObject.Duration = Math.Abs(endTime - originalStartTime); + tailPiece.Position = ToLocalSpace(result.ScreenSpacePosition); + lengthPiece.Width = tailPiece.X - headPiece.X; + } + } + } + else + { + lengthPiece.Position = headPiece.Position = tailPiece.Position = ToLocalSpace(result.ScreenSpacePosition); + + if (result.Time is double startTime) + originalStartTime = HitObject.StartTime = startTime; + } + } + } + public class HitCompositionTool : HitObjectCompositionTool { public HitCompositionTool() @@ -221,10 +328,40 @@ namespace osu.Game.Rulesets.Taiko return false; } - public override void UpdatePosition(SnapResult snapResult) + public override void UpdatePosition(SnapResult result) { - piece.Position = ToLocalSpace(snapResult.ScreenSpacePosition); - base.UpdatePosition(snapResult); + piece.Position = ToLocalSpace(result.ScreenSpacePosition); + base.UpdatePosition(result); + } + } + + public class LengthPiece : CompositeDrawable + { + public LengthPiece() + { + Origin = Anchor.CentreLeft; + + InternalChild = new Container + { + Masking = true, + Colour = Color4.Yellow, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.X, + Height = 8, + }, + new Box + { + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + RelativeSizeAxes = Axes.X, + Height = 8, + } + } + }; } } diff --git a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs index bb89ba8311..02d5955ae6 100644 --- a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs +++ b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs @@ -87,11 +87,11 @@ namespace osu.Game.Rulesets.Edit /// /// Updates the position of this to a new screen-space position. /// - /// The snap result information. - public virtual void UpdatePosition(SnapResult snapResult) + /// The snap result information. + public virtual void UpdatePosition(SnapResult result) { if (!PlacementActive) - HitObject.StartTime = snapResult.Time ?? EditorClock?.CurrentTime ?? Time.Current; + HitObject.StartTime = result.Time ?? EditorClock?.CurrentTime ?? Time.Current; } /// From 597f2848054977d21c2af3a68dff5df82a18c516 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 May 2020 11:46:08 +0900 Subject: [PATCH 0956/1117] Tidy up and complete xmldoc for HitObjectComposer --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 89 ++++++++++++++++----- osu.sln.DotSettings | 5 +- 2 files changed, 74 insertions(+), 20 deletions(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 38576e02a0..c956439eb2 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -48,8 +48,6 @@ namespace osu.Game.Rulesets.Edit protected ComposeBlueprintContainer BlueprintContainer { get; private set; } - public override Playfield Playfield => drawableRulesetWrapper.Playfield; - private DrawableEditRulesetWrapper drawableRulesetWrapper; protected readonly Container LayerBelowRuleset = new Container { RelativeSizeAxes = Axes.Both }; @@ -61,7 +59,6 @@ namespace osu.Game.Rulesets.Edit protected HitObjectComposer(Ruleset ruleset) { Ruleset = ruleset; - RelativeSizeAxes = Axes.Both; } [BackgroundDependencyLoader] @@ -137,6 +134,49 @@ namespace osu.Game.Rulesets.Edit EditorBeatmap.SelectedHitObjects.CollectionChanged += selectionChanged; } + protected override void LoadComplete() + { + base.LoadComplete(); + + inputManager = GetContainingInputManager(); + } + + public override Playfield Playfield => drawableRulesetWrapper.Playfield; + + public override IEnumerable HitObjects => drawableRulesetWrapper.Playfield.AllHitObjects; + + public override bool CursorInPlacementArea => drawableRulesetWrapper.Playfield.ReceivePositionalInputAt(inputManager.CurrentState.Mouse.Position); + + /// + /// Defines all available composition tools, listed on the left side of the editor screen as button controls. + /// This should usually define one tool for each type used in the target ruleset. + /// + /// + /// A "select" tool is automatically added as the first tool. + /// + protected abstract IReadOnlyList CompositionTools { get; } + + /// + /// Construct a relevant blueprint container. This will manage hitobject selection/placement input handling and display logic. + /// + protected abstract ComposeBlueprintContainer CreateBlueprintContainer(); + + /// + /// Construct a drawable ruleset for the provided ruleset. + /// + /// + /// Can be overridden to add editor-specific logical changes to a 's standard . + /// For example, hit animations or judgement logic may be changed to give a better editor user experience. + /// + /// The ruleset used to construct its drawable counterpart. + /// The loaded beatmap. + /// The mods to be applied. + /// An editor-relevant . + protected virtual DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) + => (DrawableRuleset)ruleset.CreateDrawableRulesetWith(beatmap, mods); + + #region Tool selection logic + protected override bool OnKeyDown(KeyDownEvent e) { if (e.Key >= Key.Number1 && e.Key <= Key.Number9) @@ -153,13 +193,6 @@ namespace osu.Game.Rulesets.Edit return base.OnKeyDown(e); } - protected override void LoadComplete() - { - base.LoadComplete(); - - inputManager = GetContainingInputManager(); - } - private void selectionChanged(object sender, NotifyCollectionChangedEventArgs changedArgs) { if (EditorBeatmap.SelectedHitObjects.Any()) @@ -179,15 +212,9 @@ namespace osu.Game.Rulesets.Edit EditorBeatmap.SelectedHitObjects.Clear(); } - public override IEnumerable HitObjects => drawableRulesetWrapper.Playfield.AllHitObjects; + #endregion - public override bool CursorInPlacementArea => drawableRulesetWrapper.Playfield.ReceivePositionalInputAt(inputManager.CurrentState.Mouse.Position); - - protected abstract IReadOnlyList CompositionTools { get; } - - protected abstract ComposeBlueprintContainer CreateBlueprintContainer(); - - protected abstract DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null); + #region IPlacementHandler public void BeginPlacement(HitObject hitObject) { @@ -209,6 +236,17 @@ namespace osu.Game.Rulesets.Edit public void Delete(HitObject hitObject) => EditorBeatmap.Remove(hitObject); + #endregion + + #region IPositionSnapProvider + + /// + /// Retrieve the relevant at a specified screen-space position. + /// In cases where a ruleset doesn't require custom logic (due to nested playfields, for example) + /// this will return the ruleset's main playfield. + /// + /// The screen-space position to query. + /// The most relevant . protected virtual Playfield PlayfieldAtScreenSpacePosition(Vector2 screenSpacePosition) => drawableRulesetWrapper.Playfield; public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) @@ -257,8 +295,14 @@ namespace osu.Game.Rulesets.Edit return DurationToDistance(referenceTime, snappedEndTime - referenceTime); } + + #endregion } + /// + /// A non-generic definition of a HitObject composer class. + /// Generally used to access certain methods without requiring a generic type for . + /// [Cached(typeof(HitObjectComposer))] [Cached(typeof(IPositionSnapProvider))] public abstract class HitObjectComposer : CompositeDrawable, IPositionSnapProvider @@ -268,10 +312,13 @@ namespace osu.Game.Rulesets.Edit RelativeSizeAxes = Axes.Both; } + /// + /// The target ruleset's playfield. + /// public abstract Playfield Playfield { get; } /// - /// All the s. + /// All s in currently loaded beatmap. /// public abstract IEnumerable HitObjects { get; } @@ -280,6 +327,8 @@ namespace osu.Game.Rulesets.Edit /// public abstract bool CursorInPlacementArea { get; } + #region IPositionSnapProvider + public abstract SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition); public abstract float GetBeatSnapDistanceAt(double referenceTime); @@ -291,5 +340,7 @@ namespace osu.Game.Rulesets.Edit public abstract double GetSnappedDurationFromDistance(double referenceTime, float distance); public abstract float GetSnappedDistanceFromDistance(double referenceTime, float distance); + + #endregion } } diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index e3b64c03b9..b9fc3de734 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -1,4 +1,4 @@ - + True True True @@ -905,14 +905,17 @@ private void load() True True True + True True True True True True True + True True True True + True True True From 3e973c176f0ccf506b2b07c94c5bf317a94f0b34 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 May 2020 11:59:21 +0900 Subject: [PATCH 0957/1117] Remove unnecessary overrides --- osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs b/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs index 2bb037815e..16111f4abf 100644 --- a/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs +++ b/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs @@ -9,17 +9,14 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; -using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; -using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.UI; -using osu.Game.Rulesets.UI; using osu.Game.Screens.Edit.Compose.Components; using osuTK; using osuTK.Graphics; @@ -29,8 +26,6 @@ namespace osu.Game.Rulesets.Taiko { public class TaikoHitObjectComposer : HitObjectComposer { - private DrawableTaikoRuleset drawableRuleset; - public TaikoHitObjectComposer(Ruleset ruleset) : base(ruleset) { @@ -43,12 +38,7 @@ namespace osu.Game.Rulesets.Taiko new SwellCompositionTool() }; - protected override ComposeBlueprintContainer CreateBlueprintContainer() => new TaikoBlueprintContainer(drawableRuleset.Playfield.AllHitObjects); - - protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) - { - return drawableRuleset = new DrawableTaikoRuleset(ruleset, beatmap, mods); - } + protected override ComposeBlueprintContainer CreateBlueprintContainer() => new TaikoBlueprintContainer(Playfield.AllHitObjects); } public class TaikoBlueprintContainer : ComposeBlueprintContainer From 590931b17cfaccafd3bf8f0db168f4082a1dd8be Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 May 2020 12:20:50 +0900 Subject: [PATCH 0958/1117] Pass hitobjects as a parameter to CreateBlueprintContainer --- osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs | 4 +++- osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs | 4 +++- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 6 ++++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index 10d344242c..7e2469a794 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -13,6 +13,7 @@ using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; using osu.Game.Rulesets.Mania.UI; 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.Screens.Edit.Compose.Components; @@ -88,7 +89,8 @@ namespace osu.Game.Rulesets.Mania.Edit return drawableRuleset; } - protected override ComposeBlueprintContainer CreateBlueprintContainer() => new ManiaBlueprintContainer(drawableRuleset.Playfield.AllHitObjects); + protected override ComposeBlueprintContainer CreateBlueprintContainer(IEnumerable hitObjects) + => new ManiaBlueprintContainer(hitObjects); protected override IReadOnlyList CompositionTools => new HitObjectCompositionTool[] { diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index de5c1e54d7..37019a7a05 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -13,6 +13,7 @@ using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.UI; using osu.Game.Screens.Edit.Compose.Components; @@ -46,7 +47,8 @@ namespace osu.Game.Rulesets.Osu.Edit EditorBeatmap.PlacementObject.ValueChanged += _ => updateDistanceSnapGrid(); } - protected override ComposeBlueprintContainer CreateBlueprintContainer() => new OsuBlueprintContainer(HitObjects); + protected override ComposeBlueprintContainer CreateBlueprintContainer(IEnumerable hitObjects) + => new OsuBlueprintContainer(hitObjects); private DistanceSnapGrid distanceSnapGrid; private Container distanceSnapGridContainer; diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index c956439eb2..8b9f531417 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -113,7 +113,7 @@ namespace osu.Game.Rulesets.Edit drawableRulesetWrapper, // layers above playfield drawableRulesetWrapper.CreatePlayfieldAdjustmentContainer() - .WithChild(BlueprintContainer = CreateBlueprintContainer()) + .WithChild(BlueprintContainer = CreateBlueprintContainer(HitObjects)) } } }, @@ -159,7 +159,9 @@ namespace osu.Game.Rulesets.Edit /// /// Construct a relevant blueprint container. This will manage hitobject selection/placement input handling and display logic. /// - protected abstract ComposeBlueprintContainer CreateBlueprintContainer(); + /// A live collection of all s in the editor beatmap. + protected virtual ComposeBlueprintContainer CreateBlueprintContainer(IEnumerable hitObjects) + => new ComposeBlueprintContainer(hitObjects); /// /// Construct a drawable ruleset for the provided ruleset. From 7b52faa76d086ce98dd455e1475448e09c825870 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 May 2020 12:45:09 +0900 Subject: [PATCH 0959/1117] Update override --- osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs b/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs index 16111f4abf..29e0e3a3c0 100644 --- a/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs +++ b/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs @@ -38,7 +38,8 @@ namespace osu.Game.Rulesets.Taiko new SwellCompositionTool() }; - protected override ComposeBlueprintContainer CreateBlueprintContainer() => new TaikoBlueprintContainer(Playfield.AllHitObjects); + protected override ComposeBlueprintContainer CreateBlueprintContainer(IEnumerable hitObjects) + => new TaikoBlueprintContainer(hitObjects); } public class TaikoBlueprintContainer : ComposeBlueprintContainer From 7f8f41715d31f74234e019b4de049c9e0acacc64 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 May 2020 13:15:43 +0900 Subject: [PATCH 0960/1117] Remove stray whitespace --- osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs b/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs index 29e0e3a3c0..b34a8e75cc 100644 --- a/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs +++ b/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs @@ -286,7 +286,6 @@ namespace osu.Game.Rulesets.Taiko } public class HitPlacementBlueprint : PlacementBlueprint - { private readonly HitPiece piece; From 3b6619a3608e7d60c7c22b541478ec62c4442335 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 May 2020 16:11:26 +0900 Subject: [PATCH 0961/1117] Flip direction to avoid breaking other usages --- osu.Game/Rulesets/Objects/Types/IHasDuration.cs | 16 +++++++++++++--- osu.Game/Rulesets/Objects/Types/IHasEndTime.cs | 16 +++++++++++++++- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Types/IHasDuration.cs b/osu.Game/Rulesets/Objects/Types/IHasDuration.cs index 2433f9597e..185fd5977b 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasDuration.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasDuration.cs @@ -8,17 +8,27 @@ namespace osu.Game.Rulesets.Objects.Types /// /// A HitObject that ends at a different time than its start time. /// - public interface IHasDuration +#pragma warning disable 618 + public interface IHasDuration : IHasEndTime +#pragma warning restore 618 { + double IHasEndTime.EndTime + { + get => EndTime; + set => Duration = (Duration - EndTime) + value; + } + + double IHasEndTime.Duration => Duration; + /// /// The time at which the HitObject ends. /// - double EndTime { get; } + new double EndTime { get; } /// /// The duration of the HitObject. /// [JsonIgnore] - double Duration { get; set; } + new double Duration { get; set; } } } diff --git a/osu.Game/Rulesets/Objects/Types/IHasEndTime.cs b/osu.Game/Rulesets/Objects/Types/IHasEndTime.cs index 7395223c7e..c3769c5909 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasEndTime.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasEndTime.cs @@ -2,11 +2,25 @@ // See the LICENCE file in the repository root for full licence text. using System; +using Newtonsoft.Json; namespace osu.Game.Rulesets.Objects.Types { + /// + /// A HitObject that ends at a different time than its start time. + /// [Obsolete("Use IHasDuration instead.")] // can be removed 20201126 - public interface IHasEndTime : IHasDuration + public interface IHasEndTime { + /// + /// The time at which the HitObject ends. + /// + [JsonIgnore] + double EndTime { get; set; } + + /// + /// The duration of the HitObject. + /// + double Duration { get; } } } From da289c474e2ceba450437a20572fa7d7d2b902f6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 May 2020 16:40:10 +0900 Subject: [PATCH 0962/1117] Split files out --- .../TestSceneTaikoHitObjectComposer.cs | 1 + .../Blueprints/DrumRollPlacementBlueprint.cs | 12 + .../Edit/Blueprints/HitPiece.cs | 32 ++ .../Edit/Blueprints/HitPlacementBlueprint.cs | 49 +++ .../Edit/Blueprints/LengthPiece.cs | 37 ++ .../Blueprints/SwellPlacementBlueprint.cs | 12 + .../Blueprints/TaikoSpanPlacementBlueprint.cs | 101 +++++ .../Edit/DrumRollCompositionTool.cs | 17 + .../Edit/HitCompositionTool.cs | 17 + .../Edit/SwellCompositionTool.cs | 17 + .../Edit/TaikoBlueprintContainer.cs | 20 + .../Edit/TaikoHitObjectComposer.cs | 30 ++ .../Edit/TaikoSelectionBlueprint.cs | 41 ++ .../Edit/TaikoSelectionHandler.cs | 80 ++++ .../TaikoHitObjectComposer.cs | 382 ------------------ osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 1 + 16 files changed, 467 insertions(+), 382 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko/Edit/Blueprints/DrumRollPlacementBlueprint.cs create mode 100644 osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPiece.cs create mode 100644 osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPlacementBlueprint.cs create mode 100644 osu.Game.Rulesets.Taiko/Edit/Blueprints/LengthPiece.cs create mode 100644 osu.Game.Rulesets.Taiko/Edit/Blueprints/SwellPlacementBlueprint.cs create mode 100644 osu.Game.Rulesets.Taiko/Edit/Blueprints/TaikoSpanPlacementBlueprint.cs create mode 100644 osu.Game.Rulesets.Taiko/Edit/DrumRollCompositionTool.cs create mode 100644 osu.Game.Rulesets.Taiko/Edit/HitCompositionTool.cs create mode 100644 osu.Game.Rulesets.Taiko/Edit/SwellCompositionTool.cs create mode 100644 osu.Game.Rulesets.Taiko/Edit/TaikoBlueprintContainer.cs create mode 100644 osu.Game.Rulesets.Taiko/Edit/TaikoHitObjectComposer.cs create mode 100644 osu.Game.Rulesets.Taiko/Edit/TaikoSelectionBlueprint.cs create mode 100644 osu.Game.Rulesets.Taiko/Edit/TaikoSelectionHandler.cs delete mode 100644 osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectComposer.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectComposer.cs index b5ee33fa8e..34d5fdf857 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectComposer.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectComposer.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Taiko.Beatmaps; +using osu.Game.Rulesets.Taiko.Edit; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Screens.Edit; using osu.Game.Tests.Visual; diff --git a/osu.Game.Rulesets.Taiko/Edit/Blueprints/DrumRollPlacementBlueprint.cs b/osu.Game.Rulesets.Taiko/Edit/Blueprints/DrumRollPlacementBlueprint.cs new file mode 100644 index 0000000000..2f086891a9 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Edit/Blueprints/DrumRollPlacementBlueprint.cs @@ -0,0 +1,12 @@ +using osu.Game.Rulesets.Taiko.Objects; + +namespace osu.Game.Rulesets.Taiko.Edit.Blueprints +{ + public class DrumRollPlacementBlueprint : TaikoSpanPlacementBlueprint + { + public DrumRollPlacementBlueprint() + : base(new DrumRoll()) + { + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPiece.cs b/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPiece.cs new file mode 100644 index 0000000000..992fba1e29 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPiece.cs @@ -0,0 +1,32 @@ +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Taiko.Edit.Blueprints +{ + public class HitPiece : CompositeDrawable + { + public HitPiece() + { + Origin = Anchor.Centre; + + InternalChild = new CircularContainer + { + Masking = true, + BorderThickness = 10, + BorderColour = Color4.Yellow, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + AlwaysPresent = true, + Alpha = 0, + RelativeSizeAxes = Axes.Both + } + } + }; + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPlacementBlueprint.cs b/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPlacementBlueprint.cs new file mode 100644 index 0000000000..c21aed32e8 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPlacementBlueprint.cs @@ -0,0 +1,49 @@ +using osu.Framework.Input.Events; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Taiko.UI; +using osuTK; +using osuTK.Input; + +namespace osu.Game.Rulesets.Taiko.Edit.Blueprints +{ + public class HitPlacementBlueprint : PlacementBlueprint + { + private readonly HitPiece piece; + + private static Hit hit; + + public HitPlacementBlueprint() + : base(hit = new Hit()) + { + InternalChild = piece = new HitPiece + { + Size = new Vector2(TaikoHitObject.DEFAULT_SIZE * TaikoPlayfield.DEFAULT_HEIGHT) + }; + } + + protected override bool OnMouseDown(MouseDownEvent e) + { + switch (e.Button) + { + case MouseButton.Left: + hit.Type = HitType.Centre; + EndPlacement(true); + return true; + + case MouseButton.Right: + hit.Type = HitType.Rim; + EndPlacement(true); + return true; + } + + return false; + } + + public override void UpdatePosition(SnapResult result) + { + piece.Position = ToLocalSpace(result.ScreenSpacePosition); + base.UpdatePosition(result); + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Edit/Blueprints/LengthPiece.cs b/osu.Game.Rulesets.Taiko/Edit/Blueprints/LengthPiece.cs new file mode 100644 index 0000000000..2139a852b2 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Edit/Blueprints/LengthPiece.cs @@ -0,0 +1,37 @@ +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Taiko.Edit.Blueprints +{ + public class LengthPiece : CompositeDrawable + { + public LengthPiece() + { + Origin = Anchor.CentreLeft; + + InternalChild = new Container + { + Masking = true, + Colour = Color4.Yellow, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.X, + Height = 8, + }, + new Box + { + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + RelativeSizeAxes = Axes.X, + Height = 8, + } + } + }; + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Edit/Blueprints/SwellPlacementBlueprint.cs b/osu.Game.Rulesets.Taiko/Edit/Blueprints/SwellPlacementBlueprint.cs new file mode 100644 index 0000000000..180bf26f34 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Edit/Blueprints/SwellPlacementBlueprint.cs @@ -0,0 +1,12 @@ +using osu.Game.Rulesets.Taiko.Objects; + +namespace osu.Game.Rulesets.Taiko.Edit.Blueprints +{ + public class SwellPlacementBlueprint : TaikoSpanPlacementBlueprint + { + public SwellPlacementBlueprint() + : base(new Swell()) + { + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Edit/Blueprints/TaikoSpanPlacementBlueprint.cs b/osu.Game.Rulesets.Taiko/Edit/Blueprints/TaikoSpanPlacementBlueprint.cs new file mode 100644 index 0000000000..b08cc68225 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Edit/Blueprints/TaikoSpanPlacementBlueprint.cs @@ -0,0 +1,101 @@ +using System; +using osu.Framework.Graphics; +using osu.Framework.Input.Events; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Taiko.UI; +using osuTK; +using osuTK.Input; + +namespace osu.Game.Rulesets.Taiko.Edit.Blueprints +{ + public class TaikoSpanPlacementBlueprint : PlacementBlueprint + { + private readonly HitPiece headPiece; + private readonly HitPiece tailPiece; + + private readonly LengthPiece lengthPiece; + + private readonly IHasDuration spanPlacementObject; + + public TaikoSpanPlacementBlueprint(HitObject hitObject) + : base(hitObject) + + { + spanPlacementObject = hitObject as IHasDuration; + + RelativeSizeAxes = Axes.Both; + + InternalChildren = new Drawable[] + { + headPiece = new HitPiece + { + Size = new Vector2(TaikoHitObject.DEFAULT_SIZE * TaikoPlayfield.DEFAULT_HEIGHT) + }, + lengthPiece = new LengthPiece + { + Height = TaikoHitObject.DEFAULT_SIZE * TaikoPlayfield.DEFAULT_HEIGHT + }, + tailPiece = new HitPiece + { + Size = new Vector2(TaikoHitObject.DEFAULT_SIZE * TaikoPlayfield.DEFAULT_HEIGHT) + } + }; + } + + private double originalStartTime; + + protected override bool OnMouseDown(MouseDownEvent e) + { + if (e.Button != MouseButton.Left) + return false; + + BeginPlacement(true); + return true; + } + + protected override void OnMouseUp(MouseUpEvent e) + { + if (e.Button != MouseButton.Left) + return; + + base.OnMouseUp(e); + EndPlacement(true); + } + + public override void UpdatePosition(SnapResult result) + { + base.UpdatePosition(result); + + if (PlacementActive) + { + if (result.Time is double endTime) + { + if (endTime < originalStartTime) + { + HitObject.StartTime = endTime; + spanPlacementObject.Duration = Math.Abs(endTime - originalStartTime); + headPiece.Position = ToLocalSpace(result.ScreenSpacePosition); + lengthPiece.X = headPiece.X; + lengthPiece.Width = tailPiece.X - headPiece.X; + } + else + { + spanPlacementObject.Duration = Math.Abs(endTime - originalStartTime); + tailPiece.Position = ToLocalSpace(result.ScreenSpacePosition); + lengthPiece.Width = tailPiece.X - headPiece.X; + } + } + } + else + { + lengthPiece.Position = headPiece.Position = tailPiece.Position = ToLocalSpace(result.ScreenSpacePosition); + + if (result.Time is double startTime) + originalStartTime = HitObject.StartTime = startTime; + } + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Edit/DrumRollCompositionTool.cs b/osu.Game.Rulesets.Taiko/Edit/DrumRollCompositionTool.cs new file mode 100644 index 0000000000..c17e22180a --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Edit/DrumRollCompositionTool.cs @@ -0,0 +1,17 @@ +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Edit.Tools; +using osu.Game.Rulesets.Taiko.Edit.Blueprints; +using osu.Game.Rulesets.Taiko.Objects; + +namespace osu.Game.Rulesets.Taiko.Edit +{ + public class DrumRollCompositionTool : HitObjectCompositionTool + { + public DrumRollCompositionTool() + : base(nameof(DrumRoll)) + { + } + + public override PlacementBlueprint CreatePlacementBlueprint() => new DrumRollPlacementBlueprint(); + } +} diff --git a/osu.Game.Rulesets.Taiko/Edit/HitCompositionTool.cs b/osu.Game.Rulesets.Taiko/Edit/HitCompositionTool.cs new file mode 100644 index 0000000000..7e8f245613 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Edit/HitCompositionTool.cs @@ -0,0 +1,17 @@ +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Edit.Tools; +using osu.Game.Rulesets.Taiko.Edit.Blueprints; +using osu.Game.Rulesets.Taiko.Objects; + +namespace osu.Game.Rulesets.Taiko.Edit +{ + public class HitCompositionTool : HitObjectCompositionTool + { + public HitCompositionTool() + : base(nameof(Hit)) + { + } + + public override PlacementBlueprint CreatePlacementBlueprint() => new HitPlacementBlueprint(); + } +} diff --git a/osu.Game.Rulesets.Taiko/Edit/SwellCompositionTool.cs b/osu.Game.Rulesets.Taiko/Edit/SwellCompositionTool.cs new file mode 100644 index 0000000000..aa96a397d0 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Edit/SwellCompositionTool.cs @@ -0,0 +1,17 @@ +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Edit.Tools; +using osu.Game.Rulesets.Taiko.Edit.Blueprints; +using osu.Game.Rulesets.Taiko.Objects; + +namespace osu.Game.Rulesets.Taiko.Edit +{ + public class SwellCompositionTool : HitObjectCompositionTool + { + public SwellCompositionTool() + : base(nameof(Swell)) + { + } + + public override PlacementBlueprint CreatePlacementBlueprint() => new SwellPlacementBlueprint(); + } +} diff --git a/osu.Game.Rulesets.Taiko/Edit/TaikoBlueprintContainer.cs b/osu.Game.Rulesets.Taiko/Edit/TaikoBlueprintContainer.cs new file mode 100644 index 0000000000..b7cda04705 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Edit/TaikoBlueprintContainer.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Screens.Edit.Compose.Components; + +namespace osu.Game.Rulesets.Taiko.Edit +{ + public class TaikoBlueprintContainer : ComposeBlueprintContainer + { + public TaikoBlueprintContainer(IEnumerable hitObjects) + : base(hitObjects) + { + } + + protected override SelectionHandler CreateSelectionHandler() => new TaikoSelectionHandler(); + + public override OverlaySelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject) => + new TaikoSelectionBlueprint(hitObject); + } +} diff --git a/osu.Game.Rulesets.Taiko/Edit/TaikoHitObjectComposer.cs b/osu.Game.Rulesets.Taiko/Edit/TaikoHitObjectComposer.cs new file mode 100644 index 0000000000..7ad40903d2 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Edit/TaikoHitObjectComposer.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.Collections.Generic; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Edit.Tools; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Screens.Edit.Compose.Components; + +namespace osu.Game.Rulesets.Taiko.Edit +{ + public class TaikoHitObjectComposer : HitObjectComposer + { + public TaikoHitObjectComposer(Ruleset ruleset) + : base(ruleset) + { + } + + protected override IReadOnlyList CompositionTools => new HitObjectCompositionTool[] + { + new HitCompositionTool(), + new DrumRollCompositionTool(), + new SwellCompositionTool() + }; + + protected override ComposeBlueprintContainer CreateBlueprintContainer(IEnumerable hitObjects) + => new TaikoBlueprintContainer(hitObjects); + } +} diff --git a/osu.Game.Rulesets.Taiko/Edit/TaikoSelectionBlueprint.cs b/osu.Game.Rulesets.Taiko/Edit/TaikoSelectionBlueprint.cs new file mode 100644 index 0000000000..e3797a5fa6 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Edit/TaikoSelectionBlueprint.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 osu.Framework.Graphics; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Taiko.Edit.Blueprints; +using osuTK; + +namespace osu.Game.Rulesets.Taiko.Edit +{ + public class TaikoSelectionBlueprint : OverlaySelectionBlueprint + { + public TaikoSelectionBlueprint(DrawableHitObject hitObject) + : base(hitObject) + { + RelativeSizeAxes = Axes.None; + + AddInternal(new HitPiece + { + RelativeSizeAxes = Axes.Both, + Origin = Anchor.TopLeft + }); + } + + protected override void Update() + { + base.Update(); + + // Move the rectangle to cover the hitobjects + var topLeft = new Vector2(float.MaxValue, float.MaxValue); + var bottomRight = new Vector2(float.MinValue, float.MinValue); + + topLeft = Vector2.ComponentMin(topLeft, Parent.ToLocalSpace(DrawableObject.ScreenSpaceDrawQuad.TopLeft)); + bottomRight = Vector2.ComponentMax(bottomRight, Parent.ToLocalSpace(DrawableObject.ScreenSpaceDrawQuad.BottomRight)); + + Size = bottomRight - topLeft; + Position = topLeft; + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Edit/TaikoSelectionHandler.cs b/osu.Game.Rulesets.Taiko/Edit/TaikoSelectionHandler.cs new file mode 100644 index 0000000000..eebf6980fe --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Edit/TaikoSelectionHandler.cs @@ -0,0 +1,80 @@ +// 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.UserInterface; +using osu.Game.Graphics.UserInterface; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Screens.Edit.Compose.Components; + +namespace osu.Game.Rulesets.Taiko.Edit +{ + public class TaikoSelectionHandler : SelectionHandler + { + protected override IEnumerable GetContextMenuItemsForSelection(IEnumerable selection) + { + if (selection.All(s => s.HitObject is Hit)) + { + var hits = selection.Select(s => s.HitObject).OfType(); + + yield return new TernaryStateMenuItem("Rim", action: state => + { + foreach (var h in hits) + { + switch (state) + { + case TernaryState.True: + h.Type = HitType.Rim; + break; + + case TernaryState.False: + h.Type = HitType.Centre; + break; + } + } + }) + { + State = { Value = getTernaryState(hits, h => h.Type == HitType.Rim) } + }; + } + + if (selection.All(s => s.HitObject is TaikoHitObject)) + { + var hits = selection.Select(s => s.HitObject).OfType(); + + yield return new TernaryStateMenuItem("Strong", action: state => + { + foreach (var h in hits) + { + switch (state) + { + case TernaryState.True: + h.IsStrong = true; + break; + + case TernaryState.False: + h.IsStrong = false; + break; + } + + EditorBeatmap?.UpdateHitObject(h); + } + }) + { + State = { Value = getTernaryState(hits, h => h.IsStrong) } + }; + } + } + + private TernaryState getTernaryState(IEnumerable selection, Func func) + { + if (selection.Any(func)) + return selection.All(func) ? TernaryState.True : TernaryState.Indeterminate; + + return TernaryState.False; + } + } +} diff --git a/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs b/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs deleted file mode 100644 index b34a8e75cc..0000000000 --- a/osu.Game.Rulesets.Taiko/TaikoHitObjectComposer.cs +++ /dev/null @@ -1,382 +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.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.UserInterface; -using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Edit.Tools; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Objects.Types; -using osu.Game.Rulesets.Taiko.Objects; -using osu.Game.Rulesets.Taiko.UI; -using osu.Game.Screens.Edit.Compose.Components; -using osuTK; -using osuTK.Graphics; -using osuTK.Input; - -namespace osu.Game.Rulesets.Taiko -{ - public class TaikoHitObjectComposer : HitObjectComposer - { - public TaikoHitObjectComposer(Ruleset ruleset) - : base(ruleset) - { - } - - protected override IReadOnlyList CompositionTools => new HitObjectCompositionTool[] - { - new HitCompositionTool(), - new DrumRollCompositionTool(), - new SwellCompositionTool() - }; - - protected override ComposeBlueprintContainer CreateBlueprintContainer(IEnumerable hitObjects) - => new TaikoBlueprintContainer(hitObjects); - } - - public class TaikoBlueprintContainer : ComposeBlueprintContainer - { - public TaikoBlueprintContainer(IEnumerable hitObjects) - : base(hitObjects) - { - } - - protected override SelectionHandler CreateSelectionHandler() => new TaikoSelectionHandler(); - - public override OverlaySelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject) => - new TaikoSelectionBlueprint(hitObject); - } - - public class TaikoSelectionHandler : SelectionHandler - { - protected override IEnumerable GetContextMenuItemsForSelection(IEnumerable selection) - { - if (selection.All(s => s.HitObject is Hit)) - { - var hits = selection.Select(s => s.HitObject).OfType(); - - yield return new TernaryStateMenuItem("Rim", action: state => - { - foreach (var h in hits) - { - switch (state) - { - case TernaryState.True: - h.Type = HitType.Rim; - break; - - case TernaryState.False: - h.Type = HitType.Centre; - break; - } - } - }) - { - State = { Value = getTernaryState(hits, h => h.Type == HitType.Rim) } - }; - } - - if (selection.All(s => s.HitObject is TaikoHitObject)) - { - var hits = selection.Select(s => s.HitObject).OfType(); - - yield return new TernaryStateMenuItem("Strong", action: state => - { - foreach (var h in hits) - { - switch (state) - { - case TernaryState.True: - h.IsStrong = true; - break; - - case TernaryState.False: - h.IsStrong = false; - break; - } - - EditorBeatmap?.UpdateHitObject(h); - } - }) - { - State = { Value = getTernaryState(hits, h => h.IsStrong) } - }; - } - } - - private TernaryState getTernaryState(IEnumerable selection, Func func) - { - if (selection.Any(func)) - return selection.All(func) ? TernaryState.True : TernaryState.Indeterminate; - - return TernaryState.False; - } - } - - public class TaikoSelectionBlueprint : OverlaySelectionBlueprint - { - public TaikoSelectionBlueprint(DrawableHitObject hitObject) - : base(hitObject) - { - RelativeSizeAxes = Axes.None; - - AddInternal(new HitPiece - { - RelativeSizeAxes = Axes.Both, - Origin = Anchor.TopLeft - }); - } - - protected override void Update() - { - base.Update(); - - // Move the rectangle to cover the hitobjects - var topLeft = new Vector2(float.MaxValue, float.MaxValue); - var bottomRight = new Vector2(float.MinValue, float.MinValue); - - topLeft = Vector2.ComponentMin(topLeft, Parent.ToLocalSpace(DrawableObject.ScreenSpaceDrawQuad.TopLeft)); - bottomRight = Vector2.ComponentMax(bottomRight, Parent.ToLocalSpace(DrawableObject.ScreenSpaceDrawQuad.BottomRight)); - - Size = bottomRight - topLeft; - Position = topLeft; - } - } - - public class SwellCompositionTool : HitObjectCompositionTool - { - public SwellCompositionTool() - : base(nameof(Swell)) - { - } - - public override PlacementBlueprint CreatePlacementBlueprint() => new SwellPlacementBlueprint(); - } - - public class DrumRollCompositionTool : HitObjectCompositionTool - { - public DrumRollCompositionTool() - : base(nameof(DrumRoll)) - { - } - - public override PlacementBlueprint CreatePlacementBlueprint() => new DrumRollPlacementBlueprint(); - } - - public class SwellPlacementBlueprint : TaikoSpanPlacementBlueprint - { - public SwellPlacementBlueprint() - : base(new Swell()) - { - } - } - - public class DrumRollPlacementBlueprint : TaikoSpanPlacementBlueprint - { - public DrumRollPlacementBlueprint() - : base(new DrumRoll()) - { - } - } - - public class TaikoSpanPlacementBlueprint : PlacementBlueprint - { - private readonly HitPiece headPiece; - private readonly HitPiece tailPiece; - - private readonly LengthPiece lengthPiece; - - private readonly IHasDuration spanPlacementObject; - - public TaikoSpanPlacementBlueprint(HitObject hitObject) - : base(hitObject) - - { - spanPlacementObject = hitObject as IHasDuration; - - RelativeSizeAxes = Axes.Both; - - InternalChildren = new Drawable[] - { - headPiece = new HitPiece - { - Size = new Vector2(TaikoHitObject.DEFAULT_SIZE * TaikoPlayfield.DEFAULT_HEIGHT) - }, - lengthPiece = new LengthPiece - { - Height = TaikoHitObject.DEFAULT_SIZE * TaikoPlayfield.DEFAULT_HEIGHT - }, - tailPiece = new HitPiece - { - Size = new Vector2(TaikoHitObject.DEFAULT_SIZE * TaikoPlayfield.DEFAULT_HEIGHT) - } - }; - } - - private double originalStartTime; - - protected override bool OnMouseDown(MouseDownEvent e) - { - if (e.Button != MouseButton.Left) - return false; - - BeginPlacement(true); - return true; - } - - protected override void OnMouseUp(MouseUpEvent e) - { - if (e.Button != MouseButton.Left) - return; - - base.OnMouseUp(e); - EndPlacement(true); - } - - public override void UpdatePosition(SnapResult result) - { - base.UpdatePosition(result); - - if (PlacementActive) - { - if (result.Time is double endTime) - { - if (endTime < originalStartTime) - { - HitObject.StartTime = endTime; - spanPlacementObject.Duration = Math.Abs(endTime - originalStartTime); - headPiece.Position = ToLocalSpace(result.ScreenSpacePosition); - lengthPiece.X = headPiece.X; - lengthPiece.Width = tailPiece.X - headPiece.X; - } - else - { - spanPlacementObject.Duration = Math.Abs(endTime - originalStartTime); - tailPiece.Position = ToLocalSpace(result.ScreenSpacePosition); - lengthPiece.Width = tailPiece.X - headPiece.X; - } - } - } - else - { - lengthPiece.Position = headPiece.Position = tailPiece.Position = ToLocalSpace(result.ScreenSpacePosition); - - if (result.Time is double startTime) - originalStartTime = HitObject.StartTime = startTime; - } - } - } - - public class HitCompositionTool : HitObjectCompositionTool - { - public HitCompositionTool() - : base(nameof(Hit)) - { - } - - public override PlacementBlueprint CreatePlacementBlueprint() => new HitPlacementBlueprint(); - } - - public class HitPlacementBlueprint : PlacementBlueprint - { - private readonly HitPiece piece; - - private static Hit hit; - - public HitPlacementBlueprint() - : base(hit = new Hit()) - { - InternalChild = piece = new HitPiece - { - Size = new Vector2(TaikoHitObject.DEFAULT_SIZE * TaikoPlayfield.DEFAULT_HEIGHT) - }; - } - - protected override bool OnMouseDown(MouseDownEvent e) - { - switch (e.Button) - { - case MouseButton.Left: - hit.Type = HitType.Centre; - EndPlacement(true); - return true; - - case MouseButton.Right: - hit.Type = HitType.Rim; - EndPlacement(true); - return true; - } - - return false; - } - - public override void UpdatePosition(SnapResult result) - { - piece.Position = ToLocalSpace(result.ScreenSpacePosition); - base.UpdatePosition(result); - } - } - - public class LengthPiece : CompositeDrawable - { - public LengthPiece() - { - Origin = Anchor.CentreLeft; - - InternalChild = new Container - { - Masking = true, - Colour = Color4.Yellow, - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.X, - Height = 8, - }, - new Box - { - Origin = Anchor.BottomLeft, - Anchor = Anchor.BottomLeft, - RelativeSizeAxes = Axes.X, - Height = 8, - } - } - }; - } - } - - public class HitPiece : CompositeDrawable - { - public HitPiece() - { - Origin = Anchor.Centre; - - InternalChild = new CircularContainer - { - Masking = true, - BorderThickness = 10, - BorderColour = Color4.Yellow, - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - new Box - { - AlwaysPresent = true, - Alpha = 0, - RelativeSizeAxes = Axes.Both - } - } - }; - } - } -} diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 7be16471b4..4cdd1fbc24 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -22,6 +22,7 @@ using osu.Game.Rulesets.Taiko.Scoring; using osu.Game.Scoring; using System; using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Taiko.Edit; using osu.Game.Rulesets.Taiko.Skinning; using osu.Game.Skinning; From e0aae15c0a313549f29a610b0bc697a1fd2c9435 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 May 2020 16:40:23 +0900 Subject: [PATCH 0963/1117] Hard type incoming ruleset --- osu.Game.Rulesets.Taiko/Edit/TaikoHitObjectComposer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Edit/TaikoHitObjectComposer.cs b/osu.Game.Rulesets.Taiko/Edit/TaikoHitObjectComposer.cs index 7ad40903d2..cdc9672a8e 100644 --- a/osu.Game.Rulesets.Taiko/Edit/TaikoHitObjectComposer.cs +++ b/osu.Game.Rulesets.Taiko/Edit/TaikoHitObjectComposer.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Taiko.Edit { public class TaikoHitObjectComposer : HitObjectComposer { - public TaikoHitObjectComposer(Ruleset ruleset) + public TaikoHitObjectComposer(TaikoRuleset ruleset) : base(ruleset) { } From b068992a15f43237bfb3693e0fca4daa63922fd8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 May 2020 18:58:34 +0900 Subject: [PATCH 0964/1117] Add missing licence headers --- .../Edit/Blueprints/DrumRollPlacementBlueprint.cs | 3 +++ osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPiece.cs | 3 +++ .../Edit/Blueprints/HitPlacementBlueprint.cs | 3 +++ osu.Game.Rulesets.Taiko/Edit/Blueprints/LengthPiece.cs | 3 +++ .../Edit/Blueprints/SwellPlacementBlueprint.cs | 3 +++ .../Edit/Blueprints/TaikoSpanPlacementBlueprint.cs | 3 +++ osu.Game.Rulesets.Taiko/Edit/DrumRollCompositionTool.cs | 3 +++ osu.Game.Rulesets.Taiko/Edit/HitCompositionTool.cs | 3 +++ osu.Game.Rulesets.Taiko/Edit/SwellCompositionTool.cs | 3 +++ osu.Game.Rulesets.Taiko/Edit/TaikoBlueprintContainer.cs | 3 +++ 10 files changed, 30 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/Edit/Blueprints/DrumRollPlacementBlueprint.cs b/osu.Game.Rulesets.Taiko/Edit/Blueprints/DrumRollPlacementBlueprint.cs index 2f086891a9..eb07ce7635 100644 --- a/osu.Game.Rulesets.Taiko/Edit/Blueprints/DrumRollPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Taiko/Edit/Blueprints/DrumRollPlacementBlueprint.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 osu.Game.Rulesets.Taiko.Objects; namespace osu.Game.Rulesets.Taiko.Edit.Blueprints diff --git a/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPiece.cs b/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPiece.cs index 992fba1e29..b02e3aa9ba 100644 --- a/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPiece.cs +++ b/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPiece.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 osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; diff --git a/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPlacementBlueprint.cs b/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPlacementBlueprint.cs index c21aed32e8..c5191ab241 100644 --- a/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPlacementBlueprint.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 osu.Framework.Input.Events; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Taiko.Objects; diff --git a/osu.Game.Rulesets.Taiko/Edit/Blueprints/LengthPiece.cs b/osu.Game.Rulesets.Taiko/Edit/Blueprints/LengthPiece.cs index 2139a852b2..6b651fd739 100644 --- a/osu.Game.Rulesets.Taiko/Edit/Blueprints/LengthPiece.cs +++ b/osu.Game.Rulesets.Taiko/Edit/Blueprints/LengthPiece.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 osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; diff --git a/osu.Game.Rulesets.Taiko/Edit/Blueprints/SwellPlacementBlueprint.cs b/osu.Game.Rulesets.Taiko/Edit/Blueprints/SwellPlacementBlueprint.cs index 180bf26f34..95fa82a0f2 100644 --- a/osu.Game.Rulesets.Taiko/Edit/Blueprints/SwellPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Taiko/Edit/Blueprints/SwellPlacementBlueprint.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 osu.Game.Rulesets.Taiko.Objects; namespace osu.Game.Rulesets.Taiko.Edit.Blueprints diff --git a/osu.Game.Rulesets.Taiko/Edit/Blueprints/TaikoSpanPlacementBlueprint.cs b/osu.Game.Rulesets.Taiko/Edit/Blueprints/TaikoSpanPlacementBlueprint.cs index b08cc68225..7f96b5a46e 100644 --- a/osu.Game.Rulesets.Taiko/Edit/Blueprints/TaikoSpanPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Taiko/Edit/Blueprints/TaikoSpanPlacementBlueprint.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 osu.Framework.Graphics; using osu.Framework.Input.Events; diff --git a/osu.Game.Rulesets.Taiko/Edit/DrumRollCompositionTool.cs b/osu.Game.Rulesets.Taiko/Edit/DrumRollCompositionTool.cs index c17e22180a..bf77c76670 100644 --- a/osu.Game.Rulesets.Taiko/Edit/DrumRollCompositionTool.cs +++ b/osu.Game.Rulesets.Taiko/Edit/DrumRollCompositionTool.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 osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Taiko.Edit.Blueprints; diff --git a/osu.Game.Rulesets.Taiko/Edit/HitCompositionTool.cs b/osu.Game.Rulesets.Taiko/Edit/HitCompositionTool.cs index 7e8f245613..e877cf6240 100644 --- a/osu.Game.Rulesets.Taiko/Edit/HitCompositionTool.cs +++ b/osu.Game.Rulesets.Taiko/Edit/HitCompositionTool.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 osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Taiko.Edit.Blueprints; diff --git a/osu.Game.Rulesets.Taiko/Edit/SwellCompositionTool.cs b/osu.Game.Rulesets.Taiko/Edit/SwellCompositionTool.cs index aa96a397d0..a6191fcedc 100644 --- a/osu.Game.Rulesets.Taiko/Edit/SwellCompositionTool.cs +++ b/osu.Game.Rulesets.Taiko/Edit/SwellCompositionTool.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 osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Taiko.Edit.Blueprints; diff --git a/osu.Game.Rulesets.Taiko/Edit/TaikoBlueprintContainer.cs b/osu.Game.Rulesets.Taiko/Edit/TaikoBlueprintContainer.cs index b7cda04705..36227b0798 100644 --- a/osu.Game.Rulesets.Taiko/Edit/TaikoBlueprintContainer.cs +++ b/osu.Game.Rulesets.Taiko/Edit/TaikoBlueprintContainer.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.Collections.Generic; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects.Drawables; From affad4724867f9cda7a5d707a1edca3943e0c0a0 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 29 May 2020 19:44:53 +0300 Subject: [PATCH 0965/1117] Fix genre/language search doesn't work --- .../Online/API/Requests/SearchBeatmapSetsRequest.cs | 13 ++++++++++--- .../BeatmapListing/BeatmapListingFilterControl.cs | 4 +++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs index 0c3272c7de..ce8b40aa30 100644 --- a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs +++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs @@ -27,7 +27,14 @@ namespace osu.Game.Online.API.Requests private string directionString => SortDirection == SortDirection.Descending ? @"desc" : @"asc"; - public SearchBeatmapSetsRequest(string query, RulesetInfo ruleset, Cursor cursor = null, SearchCategory searchCategory = SearchCategory.Any, SortCriteria sortCriteria = SortCriteria.Ranked, SortDirection sortDirection = SortDirection.Descending) + public SearchBeatmapSetsRequest(string query, + RulesetInfo ruleset, + Cursor cursor = null, + SearchCategory searchCategory = SearchCategory.Any, + SortCriteria sortCriteria = SortCriteria.Ranked, + SortDirection sortDirection = SortDirection.Descending, + SearchGenre genre = SearchGenre.Any, + SearchLanguage language = SearchLanguage.Any) { this.query = string.IsNullOrEmpty(query) ? string.Empty : System.Uri.EscapeDataString(query); this.ruleset = ruleset; @@ -36,8 +43,8 @@ namespace osu.Game.Online.API.Requests SearchCategory = searchCategory; SortCriteria = sortCriteria; SortDirection = sortDirection; - Genre = SearchGenre.Any; - Language = SearchLanguage.Any; + Genre = genre; + Language = language; } protected override WebRequest CreateWebRequest() diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs index 41c99d5d03..0ead5cc226 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs @@ -177,7 +177,9 @@ namespace osu.Game.Overlays.BeatmapListing lastResponse?.Cursor, searchControl.Category.Value, sortControl.Current.Value, - sortControl.SortDirection.Value); + sortControl.SortDirection.Value, + searchControl.Genre.Value, + searchControl.Language.Value); getSetsRequest.Success += response => { From 9aa54ed89e059349ca257fbb5b27e8ceee5f835e Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 29 May 2020 19:53:32 +0300 Subject: [PATCH 0966/1117] Fix serach control background never being updated --- .../Overlays/BeatmapListing/BeatmapListingFilterControl.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs index 0ead5cc226..494a0df8f8 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs @@ -188,6 +188,9 @@ namespace osu.Game.Overlays.BeatmapListing if (sets.Count == 0) noMoreResults = true; + if (CurrentPage == 0) + searchControl.BeatmapSet = sets.FirstOrDefault(); + lastResponse = response; getSetsRequest = null; From 11057cd6a83859744ee3057112eef5a8b3daefca Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 29 May 2020 21:43:31 +0300 Subject: [PATCH 0967/1117] CI fix --- osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs index ce8b40aa30..dde45b5aeb 100644 --- a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs +++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs @@ -27,7 +27,8 @@ namespace osu.Game.Online.API.Requests private string directionString => SortDirection == SortDirection.Descending ? @"desc" : @"asc"; - public SearchBeatmapSetsRequest(string query, + public SearchBeatmapSetsRequest( + string query, RulesetInfo ruleset, Cursor cursor = null, SearchCategory searchCategory = SearchCategory.Any, From 816f721f3dffb36ed0d6279237e9137bd791dba6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 30 May 2020 15:24:37 +0900 Subject: [PATCH 0968/1117] Move selection blueprint to correct namespace --- .../Edit/{ => Blueprints}/TaikoSelectionBlueprint.cs | 3 +-- osu.Game.Rulesets.Taiko/Edit/TaikoBlueprintContainer.cs | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) rename osu.Game.Rulesets.Taiko/Edit/{ => Blueprints}/TaikoSelectionBlueprint.cs (93%) diff --git a/osu.Game.Rulesets.Taiko/Edit/TaikoSelectionBlueprint.cs b/osu.Game.Rulesets.Taiko/Edit/Blueprints/TaikoSelectionBlueprint.cs similarity index 93% rename from osu.Game.Rulesets.Taiko/Edit/TaikoSelectionBlueprint.cs rename to osu.Game.Rulesets.Taiko/Edit/Blueprints/TaikoSelectionBlueprint.cs index e3797a5fa6..62f69122cc 100644 --- a/osu.Game.Rulesets.Taiko/Edit/TaikoSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Taiko/Edit/Blueprints/TaikoSelectionBlueprint.cs @@ -4,10 +4,9 @@ using osu.Framework.Graphics; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Taiko.Edit.Blueprints; using osuTK; -namespace osu.Game.Rulesets.Taiko.Edit +namespace osu.Game.Rulesets.Taiko.Edit.Blueprints { public class TaikoSelectionBlueprint : OverlaySelectionBlueprint { diff --git a/osu.Game.Rulesets.Taiko/Edit/TaikoBlueprintContainer.cs b/osu.Game.Rulesets.Taiko/Edit/TaikoBlueprintContainer.cs index 36227b0798..35227b3c64 100644 --- a/osu.Game.Rulesets.Taiko/Edit/TaikoBlueprintContainer.cs +++ b/osu.Game.Rulesets.Taiko/Edit/TaikoBlueprintContainer.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Taiko.Edit.Blueprints; using osu.Game.Screens.Edit.Compose.Components; namespace osu.Game.Rulesets.Taiko.Edit From 82fe99cf4a54b90a473d43855238f2aa5ae58d65 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 31 May 2020 02:18:07 +0300 Subject: [PATCH 0969/1117] Replace any potential usage of Environment.CurrentDirectory with a new RuntimeInfo.StartupDirectory Using `Environment.CurrentDirectory` for storing / reading files is dangerous as the current directory is mutable and can be changed when performing a certain operation (like opening solutions in roslyn type reference builder for example). --- osu.Desktop/Program.cs | 4 +--- .../NonVisual/CustomDataDirectoryTest.cs | 16 ++++++++-------- osu.Game.Tests/Resources/TestResources.cs | 5 +++-- osu.Game/Rulesets/RulesetStore.cs | 5 +++-- osu.Game/Tests/Visual/OsuTestScene.cs | 3 ++- 5 files changed, 17 insertions(+), 16 deletions(-) diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index bd91bcc933..285a813d97 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -33,13 +33,11 @@ namespace osu.Desktop if (args.Length > 0 && args[0].Contains('.')) // easy way to check for a file import in args { var importer = new ArchiveImportIPCChannel(host); - // Restore the cwd so relative paths given at the command line work correctly - Directory.SetCurrentDirectory(cwd); foreach (var file in args) { Console.WriteLine(@"Importing {0}", file); - if (!importer.ImportAsync(Path.GetFullPath(file)).Wait(3000)) + if (!importer.ImportAsync(Path.GetFullPath(file, cwd)).Wait(3000)) throw new TimeoutException(@"IPC took too long to send"); } diff --git a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs index 743c924bbd..1f6e92a535 100644 --- a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs +++ b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using NUnit.Framework; +using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Platform; @@ -35,8 +36,7 @@ namespace osu.Game.Tests.NonVisual var osu = loadOsu(host); var storage = osu.Dependencies.Get(); - string defaultStorageLocation = Path.Combine(Environment.CurrentDirectory, "headless", nameof(TestDefaultDirectory)); - + string defaultStorageLocation = RuntimeInfo.StartupStorage.GetFullPath(Path.Combine("headless", nameof(TestDefaultDirectory))); Assert.That(storage.GetFullPath("."), Is.EqualTo(defaultStorageLocation)); } finally @@ -46,17 +46,17 @@ namespace osu.Game.Tests.NonVisual } } - private string customPath => Path.Combine(Environment.CurrentDirectory, "custom-path"); + private string customPath { get; } = RuntimeInfo.StartupStorage.GetFullPath("custom-path"); [Test] public void TestCustomDirectory() { using (var host = new HeadlessGameHost(nameof(TestCustomDirectory))) { - string headlessPrefix = Path.Combine("headless", nameof(TestCustomDirectory)); + string defaultStorageLocation = RuntimeInfo.StartupStorage.GetFullPath(Path.Combine("headless", nameof(TestCustomDirectory))); // need access before the game has constructed its own storage yet. - Storage storage = new DesktopStorage(headlessPrefix, host); + Storage storage = new DesktopStorage(defaultStorageLocation, host); // manual cleaning so we can prepare a config file. storage.DeleteDirectory(string.Empty); @@ -84,10 +84,10 @@ namespace osu.Game.Tests.NonVisual { using (var host = new HeadlessGameHost(nameof(TestSubDirectoryLookup))) { - string headlessPrefix = Path.Combine("headless", nameof(TestSubDirectoryLookup)); + string defaultStorageLocation = RuntimeInfo.StartupStorage.GetFullPath(Path.Combine("headless", nameof(TestSubDirectoryLookup))); // need access before the game has constructed its own storage yet. - Storage storage = new DesktopStorage(headlessPrefix, host); + Storage storage = new DesktopStorage(defaultStorageLocation, host); // manual cleaning so we can prepare a config file. storage.DeleteDirectory(string.Empty); @@ -136,7 +136,7 @@ namespace osu.Game.Tests.NonVisual // for testing nested files are not ignored (only top level) host.Storage.GetStorageForDirectory("test-nested").GetStorageForDirectory("cache"); - string defaultStorageLocation = Path.Combine(Environment.CurrentDirectory, "headless", nameof(TestMigration)); + string defaultStorageLocation = RuntimeInfo.StartupStorage.GetFullPath(Path.Combine("headless", nameof(TestMigration))); Assert.That(storage.GetFullPath("."), Is.EqualTo(defaultStorageLocation)); diff --git a/osu.Game.Tests/Resources/TestResources.cs b/osu.Game.Tests/Resources/TestResources.cs index 8b892fbb2f..33b580f68f 100644 --- a/osu.Game.Tests/Resources/TestResources.cs +++ b/osu.Game.Tests/Resources/TestResources.cs @@ -3,6 +3,7 @@ using System.IO; using NUnit.Framework; +using osu.Framework; using osu.Framework.IO.Stores; namespace osu.Game.Tests.Resources @@ -20,10 +21,10 @@ namespace osu.Game.Tests.Resources var temp = Path.GetTempFileName() + ".osz"; using (var stream = GetTestBeatmapStream(virtualTrack)) - using (var newFile = File.Create(temp)) + using (var newFile = RuntimeInfo.StartupStorage.GetStream(temp, FileAccess.Write)) stream.CopyTo(newFile); - Assert.IsTrue(File.Exists(temp)); + Assert.IsTrue(RuntimeInfo.StartupStorage.Exists(temp)); return temp; } } diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index b3026bf2b7..5c49141064 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; +using osu.Framework; using osu.Framework.Logging; using osu.Framework.Platform; using osu.Game.Database; @@ -153,14 +154,14 @@ namespace osu.Game.Rulesets { try { - string[] files = Directory.GetFiles(Environment.CurrentDirectory, $"{ruleset_library_prefix}.*.dll"); + var files = RuntimeInfo.StartupStorage.GetFiles($"{ruleset_library_prefix}.*.dll"); foreach (string file in files.Where(f => !Path.GetFileName(f).Contains("Tests"))) loadRulesetFromFile(file); } catch (Exception e) { - Logger.Error(e, $"Could not load rulesets from directory {Environment.CurrentDirectory}"); + Logger.Error(e, $"Could not load rulesets from directory {RuntimeInfo.StartupDirectory}"); } } diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs index 5dc8714c07..2672022720 100644 --- a/osu.Game/Tests/Visual/OsuTestScene.cs +++ b/osu.Game/Tests/Visual/OsuTestScene.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; +using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Track; @@ -118,7 +119,7 @@ namespace osu.Game.Tests.Visual } } - localStorage = new Lazy(() => new NativeStorage($"{GetType().Name}-{Guid.NewGuid()}")); + localStorage = new Lazy(() => RuntimeInfo.StartupStorage.GetStorageForDirectory($"{GetType().Name}-{Guid.NewGuid()}")); } [Resolved] From b06017dbf16a868f353226c1bed6261bed79308c Mon Sep 17 00:00:00 2001 From: mcendu Date: Sun, 31 May 2020 11:28:54 +0800 Subject: [PATCH 0970/1117] supress horizontal scaling of left-and-right stages --- osu.Game.Rulesets.Mania/Skinning/LegacyStageBackground.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/LegacyStageBackground.cs b/osu.Game.Rulesets.Mania/Skinning/LegacyStageBackground.cs index 7680526ac4..f177284399 100644 --- a/osu.Game.Rulesets.Mania/Skinning/LegacyStageBackground.cs +++ b/osu.Game.Rulesets.Mania/Skinning/LegacyStageBackground.cs @@ -52,10 +52,10 @@ namespace osu.Game.Rulesets.Mania.Skinning base.Update(); if (leftSprite?.Height > 0) - leftSprite.Scale = new Vector2(DrawHeight / leftSprite.Height); + leftSprite.Scale = new Vector2(1, DrawHeight / leftSprite.Height); if (rightSprite?.Height > 0) - rightSprite.Scale = new Vector2(DrawHeight / rightSprite.Height); + rightSprite.Scale = new Vector2(1, DrawHeight / rightSprite.Height); } } } From f2dadeeeb5bbd96c2ff9e62f3b09464eb1c7ffc5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 31 May 2020 13:50:42 +0900 Subject: [PATCH 0971/1117] Allow horizontal scroll on results screen when not hovering expanded panel --- osu.Game/Screens/Ranking/ScorePanelList.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Ranking/ScorePanelList.cs b/osu.Game/Screens/Ranking/ScorePanelList.cs index 18db3f2af4..1142297274 100644 --- a/osu.Game/Screens/Ranking/ScorePanelList.cs +++ b/osu.Game/Screens/Ranking/ScorePanelList.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.Bindables; @@ -107,6 +108,9 @@ namespace osu.Game.Screens.Ranking // Find the panel corresponding to the new score. expandedPanel = flow.SingleOrDefault(p => p.Score == score.NewValue); + // handle horizontal scroll only when not hovering the expanded panel. + scroll.HandleScroll = () => expandedPanel?.IsHovered != true; + if (expandedPanel == null) return; @@ -166,6 +170,11 @@ namespace osu.Game.Screens.Ranking /// public float? InstantScrollTarget; + /// + /// Whether this container should handle scroll trigger events. + /// + public Func HandleScroll; + protected override void UpdateAfterChildren() { if (InstantScrollTarget != null) @@ -177,9 +186,9 @@ namespace osu.Game.Screens.Ranking base.UpdateAfterChildren(); } - public override bool HandlePositionalInput => false; + public override bool HandlePositionalInput => HandleScroll(); - public override bool HandleNonPositionalInput => false; + public override bool HandleNonPositionalInput => HandleScroll(); } } } From e43217f5797c4699511078fbe3e3550a85b5a7b1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 31 May 2020 20:01:13 +0900 Subject: [PATCH 0972/1117] Add test resources --- .../Resources/special-skin/mania-stage-left.png | Bin 0 -> 165 bytes .../Resources/special-skin/mania-stage-right.png | Bin 0 -> 899 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 osu.Game.Rulesets.Mania.Tests/Resources/special-skin/mania-stage-left.png create mode 100644 osu.Game.Rulesets.Mania.Tests/Resources/special-skin/mania-stage-right.png diff --git a/osu.Game.Rulesets.Mania.Tests/Resources/special-skin/mania-stage-left.png b/osu.Game.Rulesets.Mania.Tests/Resources/special-skin/mania-stage-left.png new file mode 100644 index 0000000000000000000000000000000000000000..03ca371c4e26972982544c5b8f7b02a85da9a823 GIT binary patch literal 165 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|oCO|{#S9F3${@^GvDCf{D9B#o z>Fdh=gqe+pPssgM@hqT_WQl7;iF1B#Zfaf$gL6@8Vo7R>LV0FMhJw4NZ$Nk>pEyvF zyr+v}h{pNkHS5=>EAW|^m>2+o;RFsh)1E8|D=7xCTxMCBnie0RQU*^~KbLh*2~7Y- CL?;CR literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Mania.Tests/Resources/special-skin/mania-stage-right.png b/osu.Game.Rulesets.Mania.Tests/Resources/special-skin/mania-stage-right.png new file mode 100644 index 0000000000000000000000000000000000000000..45b7be025591b85e6849824f181672905d129933 GIT binary patch literal 899 zcmZ`%OK;Oa5FY1IO4N$d6BHyYw?YLq-XwrTEITw!ATg>-6jCWI9B`9OWAG!gH^fMs zI20jqK%6;5NL)DdA0UCaqe%P;#E~l!?AobQT54-|W@oDM2f3eKxn)?okJ#UJ$W5jWM2romOJPeYQMhd6`KAGifb20Fl9?n0#3 zx#ck?2Jq5=#2B+pB~w?}7RkihJvc=z=jnPg0!6zSQfU)AsPv0_c*2zg-rmt793JK;hyD?z&F8uXqz z>tP`)qhq#lFTr$&yxmMdzx*tmJN_4`a>W_2_g!Taje r`qIzU@0rIC|JptGdXHOvF!Njj=r1MTeKD0y_QurP8|D4AM=$;YKmXm5 literal 0 HcmV?d00001 From 81b8898272fa1bf325afb85236bb0020be65667c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 31 May 2020 22:30:55 +0900 Subject: [PATCH 0973/1117] Fix incorrect type cast in encoder --- osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index d7e83fa471..ab1b8aecfd 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -233,9 +233,9 @@ namespace osu.Game.Beatmaps.Formats writer.Write(FormattableString.Invariant($"{(int)getObjectType(hitObject)},")); writer.Write(FormattableString.Invariant($"{(int)toLegacyHitSoundType(hitObject.Samples)},")); - if (hitObject is IHasPathWithRepeats curveData) + if (hitObject is IHasPath path) { - addPathData(writer, curveData, position); + addPathData(writer, path, position); writer.Write(getSampleBank(hitObject.Samples, zeroBanks: true)); } else From 19be111da098a03227d645ee13f75b3b42b74814 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 31 May 2020 22:33:10 +0900 Subject: [PATCH 0974/1117] Move incorrect placed full stop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index c956439eb2..6dd6e6815b 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -171,7 +171,7 @@ namespace osu.Game.Rulesets.Edit /// The ruleset used to construct its drawable counterpart. /// The loaded beatmap. /// The mods to be applied. - /// An editor-relevant . + /// An editor-relevant . protected virtual DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) => (DrawableRuleset)ruleset.CreateDrawableRulesetWith(beatmap, mods); From e688033967bbd5000f0f515d8e63ccafa1ded00d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 31 May 2020 22:39:03 +0900 Subject: [PATCH 0975/1117] Fix incorrect xmldoc --- 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 d8d90fddfa..9e936c7717 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -331,7 +331,7 @@ namespace osu.Game.Rulesets.Objects.Legacy /// The position of the hit object. /// Whether the hit object creates a new combo. /// When starting a new combo, the offset of the new combo relative to the current one. - /// The hold end time. + /// The hold duration. protected abstract HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double duration); private List convertSoundType(LegacyHitSoundType type, SampleBankInfo bankInfo) From 2c6887e610dec2babe5f1436b2406b31547b7a15 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 31 May 2020 19:49:03 +0300 Subject: [PATCH 0976/1117] Remove unnecessary use of and remove StartupStorage --- osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs | 10 +++++----- osu.Game/Rulesets/RulesetStore.cs | 2 +- osu.Game/Tests/Visual/OsuTestScene.cs | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs index 1f6e92a535..f3d54d876a 100644 --- a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs +++ b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs @@ -36,7 +36,7 @@ namespace osu.Game.Tests.NonVisual var osu = loadOsu(host); var storage = osu.Dependencies.Get(); - string defaultStorageLocation = RuntimeInfo.StartupStorage.GetFullPath(Path.Combine("headless", nameof(TestDefaultDirectory))); + string defaultStorageLocation = Path.Combine(RuntimeInfo.StartupDirectory, "headless", nameof(TestDefaultDirectory)); Assert.That(storage.GetFullPath("."), Is.EqualTo(defaultStorageLocation)); } finally @@ -46,14 +46,14 @@ namespace osu.Game.Tests.NonVisual } } - private string customPath { get; } = RuntimeInfo.StartupStorage.GetFullPath("custom-path"); + private string customPath => Path.Combine(RuntimeInfo.StartupDirectory, "custom-path"); [Test] public void TestCustomDirectory() { using (var host = new HeadlessGameHost(nameof(TestCustomDirectory))) { - string defaultStorageLocation = RuntimeInfo.StartupStorage.GetFullPath(Path.Combine("headless", nameof(TestCustomDirectory))); + string defaultStorageLocation = Path.Combine(RuntimeInfo.StartupDirectory, "headless", nameof(TestCustomDirectory)); // need access before the game has constructed its own storage yet. Storage storage = new DesktopStorage(defaultStorageLocation, host); @@ -84,7 +84,7 @@ namespace osu.Game.Tests.NonVisual { using (var host = new HeadlessGameHost(nameof(TestSubDirectoryLookup))) { - string defaultStorageLocation = RuntimeInfo.StartupStorage.GetFullPath(Path.Combine("headless", nameof(TestSubDirectoryLookup))); + string defaultStorageLocation = Path.Combine(RuntimeInfo.StartupDirectory, "headless", nameof(TestSubDirectoryLookup)); // need access before the game has constructed its own storage yet. Storage storage = new DesktopStorage(defaultStorageLocation, host); @@ -136,7 +136,7 @@ namespace osu.Game.Tests.NonVisual // for testing nested files are not ignored (only top level) host.Storage.GetStorageForDirectory("test-nested").GetStorageForDirectory("cache"); - string defaultStorageLocation = RuntimeInfo.StartupStorage.GetFullPath(Path.Combine("headless", nameof(TestMigration))); + string defaultStorageLocation = Path.Combine(RuntimeInfo.StartupDirectory, "headless", nameof(TestMigration)); Assert.That(storage.GetFullPath("."), Is.EqualTo(defaultStorageLocation)); diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index 5c49141064..10b6edca8c 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -154,7 +154,7 @@ namespace osu.Game.Rulesets { try { - var files = RuntimeInfo.StartupStorage.GetFiles($"{ruleset_library_prefix}.*.dll"); + var files = Directory.GetFiles(Path.Combine(RuntimeInfo.StartupDirectory, $"{ruleset_library_prefix}.*.dll")); foreach (string file in files.Where(f => !Path.GetFileName(f).Contains("Tests"))) loadRulesetFromFile(file); diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs index 2672022720..632d668a01 100644 --- a/osu.Game/Tests/Visual/OsuTestScene.cs +++ b/osu.Game/Tests/Visual/OsuTestScene.cs @@ -119,7 +119,7 @@ namespace osu.Game.Tests.Visual } } - localStorage = new Lazy(() => RuntimeInfo.StartupStorage.GetStorageForDirectory($"{GetType().Name}-{Guid.NewGuid()}")); + localStorage = new Lazy(() => new NativeStorage(Path.Combine(RuntimeInfo.StartupDirectory, $"{GetType().Name}-{Guid.NewGuid()}"))); } [Resolved] From 53b58910c3d0faacf969685c45ba5620e90d5917 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 1 Jun 2020 14:27:39 +0900 Subject: [PATCH 0977/1117] Invert interface definition --- osu.Game/Rulesets/Objects/Types/IHasPathWithRepeats.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Types/IHasPathWithRepeats.cs b/osu.Game/Rulesets/Objects/Types/IHasPathWithRepeats.cs index fba0fd7aff..342b0ec1f6 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasPathWithRepeats.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasPathWithRepeats.cs @@ -9,12 +9,14 @@ namespace osu.Game.Rulesets.Objects.Types /// /// A HitObject that has a curve. /// - public interface IHasPathWithRepeats : IHasPath, IHasRepeats +#pragma warning disable 618 + public interface IHasPathWithRepeats : IHasCurve +#pragma warning restore 618 { } [Obsolete("Use IHasPathWithRepeats instead.")] // can be removed 20201126 - public interface IHasCurve : IHasPathWithRepeats + public interface IHasCurve : IHasPath, IHasRepeats { } From cac6e93575890d31ea6e4276d93b3b4698ea440c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 1 Jun 2020 15:10:22 +0900 Subject: [PATCH 0978/1117] Restore original IHasCurve implementation --- .../Rulesets/Objects/Legacy/ConvertSlider.cs | 5 +- osu.Game/Rulesets/Objects/Types/IHasCurve.cs | 55 +++++++++++++++++++ .../Objects/Types/IHasPathWithRepeats.cs | 11 +--- 3 files changed, 61 insertions(+), 10 deletions(-) create mode 100644 osu.Game/Rulesets/Objects/Types/IHasCurve.cs diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs index 73192dc42e..c946e43df3 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs @@ -9,7 +9,10 @@ using osu.Game.Beatmaps.ControlPoints; namespace osu.Game.Rulesets.Objects.Legacy { - internal abstract class ConvertSlider : ConvertHitObject, IHasPathWithRepeats, IHasLegacyLastTickOffset + internal abstract class ConvertSlider : ConvertHitObject, IHasPathWithRepeats, IHasLegacyLastTickOffset, +#pragma warning disable 618 + IHasCurve +#pragma warning restore 618 { /// /// Scoring distance with a speed-adjusted beat length of 1 second. diff --git a/osu.Game/Rulesets/Objects/Types/IHasCurve.cs b/osu.Game/Rulesets/Objects/Types/IHasCurve.cs new file mode 100644 index 0000000000..26f50ffa31 --- /dev/null +++ b/osu.Game/Rulesets/Objects/Types/IHasCurve.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; +using osuTK; + +namespace osu.Game.Rulesets.Objects.Types +{ + [Obsolete("Use IHasPathWithRepeats instead.")] // can be removed 20201126 + public interface IHasCurve : IHasDistance, IHasRepeats + { + /// + /// The curve. + /// + SliderPath Path { get; } + } + +#pragma warning disable 618 + [Obsolete("Use IHasPathWithRepeats instead.")] // can be removed 20201126 + public static class HasCurveExtensions + { + /// + /// Computes the position on the curve relative to how much of the has been completed. + /// + /// The curve. + /// [0, 1] where 0 is the start time of the and 1 is the end time of the . + /// The position on the curve. + public static Vector2 CurvePositionAt(this IHasCurve obj, double progress) + => obj.Path.PositionAt(obj.ProgressAt(progress)); + + /// + /// Computes the progress along the curve relative to how much of the has been completed. + /// + /// The curve. + /// [0, 1] where 0 is the start time of the and 1 is the end time of the . + /// [0, 1] where 0 is the beginning of the curve and 1 is the end of the curve. + public static double ProgressAt(this IHasCurve obj, double progress) + { + double p = progress * obj.SpanCount() % 1; + if (obj.SpanAt(progress) % 2 == 1) + p = 1 - p; + return p; + } + + /// + /// Determines which span of the curve the progress point is on. + /// + /// The curve. + /// [0, 1] where 0 is the beginning of the curve and 1 is the end of the curve. + /// [0, SpanCount) where 0 is the first run. + public static int SpanAt(this IHasCurve obj, double progress) + => (int)(progress * obj.SpanCount()); + } +#pragma warning restore 618 +} diff --git a/osu.Game/Rulesets/Objects/Types/IHasPathWithRepeats.cs b/osu.Game/Rulesets/Objects/Types/IHasPathWithRepeats.cs index 342b0ec1f6..279946b44e 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasPathWithRepeats.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasPathWithRepeats.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 osuTK; namespace osu.Game.Rulesets.Objects.Types @@ -9,14 +8,8 @@ namespace osu.Game.Rulesets.Objects.Types /// /// A HitObject that has a curve. /// -#pragma warning disable 618 - public interface IHasPathWithRepeats : IHasCurve -#pragma warning restore 618 - { - } - - [Obsolete("Use IHasPathWithRepeats instead.")] // can be removed 20201126 - public interface IHasCurve : IHasPath, IHasRepeats + // ReSharper disable once RedundantExtendsListEntry + public interface IHasPathWithRepeats : IHasPath, IHasRepeats { } From 7a9ed78527d1f9c02d7e3509011cc6be5a8ec60b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 1 Jun 2020 11:57:32 +0300 Subject: [PATCH 0979/1117] Remove missed leftover usages --- osu.Game.Tests/Resources/TestResources.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Resources/TestResources.cs b/osu.Game.Tests/Resources/TestResources.cs index 33b580f68f..1c264f66e0 100644 --- a/osu.Game.Tests/Resources/TestResources.cs +++ b/osu.Game.Tests/Resources/TestResources.cs @@ -18,14 +18,14 @@ namespace osu.Game.Tests.Resources public static string GetTestBeatmapForImport(bool virtualTrack = false) { - var temp = Path.GetTempFileName() + ".osz"; + var tempPath = Path.Combine(RuntimeInfo.StartupDirectory, Path.GetTempFileName() + ".osz"); using (var stream = GetTestBeatmapStream(virtualTrack)) - using (var newFile = RuntimeInfo.StartupStorage.GetStream(temp, FileAccess.Write)) + using (var newFile = File.Create(tempPath)) stream.CopyTo(newFile); - Assert.IsTrue(RuntimeInfo.StartupStorage.Exists(temp)); - return temp; + Assert.IsTrue(File.Exists(tempPath)); + return tempPath; } } } From fbd9ad411f85ceb5b8894701b830d5da8b6bac11 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 1 Jun 2020 09:05:46 +0000 Subject: [PATCH 0980/1117] Bump DiffPlex from 1.6.2 to 1.6.3 Bumps [DiffPlex](https://github.com/mmanela/diffplex) from 1.6.2 to 1.6.3. - [Release notes](https://github.com/mmanela/diffplex/releases) - [Commits](https://github.com/mmanela/diffplex/commits) Signed-off-by: dependabot-preview[bot] --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 3d2a4f3081..305e4e0a92 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -19,7 +19,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 8a7f75b515..016f2ba35d 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -75,7 +75,7 @@ - + From e9b09373e784e50a9bd6c718656df9dc2dd371b1 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Mon, 1 Jun 2020 17:41:04 +0200 Subject: [PATCH 0981/1117] Fix crashing if selected ruleset doesn't have an autoplay mod. --- osu.Game/Rulesets/Ruleset.cs | 2 +- osu.Game/Screens/Select/PlaySongSelect.cs | 22 ++++++++++++++-------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index bee11accca..8f41e421a3 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -100,7 +100,7 @@ namespace osu.Game.Rulesets return value; } - public ModAutoplay GetAutoplayMod() => GetAllMods().OfType().First(); + public ModAutoplay GetAutoplayMod() => GetAllMods().OfType().FirstOrDefault(); public virtual ISkin CreateLegacySkinProvider(ISkinSource source, IBeatmap beatmap) => null; diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index 0a4c0e2085..71ab3715e0 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -49,8 +49,11 @@ namespace osu.Game.Screens.Select if (removeAutoModOnResume) { - var autoType = Ruleset.Value.CreateInstance().GetAutoplayMod().GetType(); - ModSelect.DeselectTypes(new[] { autoType }, true); + var autoType = Ruleset.Value.CreateInstance().GetAutoplayMod()?.GetType(); + + if (autoType != null) + ModSelect.DeselectTypes(new[] { autoType }, true); + removeAutoModOnResume = false; } } @@ -78,14 +81,17 @@ namespace osu.Game.Screens.Select if (GetContainingInputManager().CurrentState?.Keyboard.ControlPressed == true) { var auto = Ruleset.Value.CreateInstance().GetAutoplayMod(); - var autoType = auto.GetType(); + var autoType = auto?.GetType(); - var mods = Mods.Value; - - if (mods.All(m => m.GetType() != autoType)) + if (autoType != null) { - Mods.Value = mods.Append(auto).ToArray(); - removeAutoModOnResume = true; + var mods = Mods.Value; + + if (mods.All(m => m.GetType() != autoType)) + { + Mods.Value = mods.Append(auto).ToArray(); + removeAutoModOnResume = true; + } } } From 1ccdfd736429a43f913491a6d562086c97e5133d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Jun 2020 14:03:13 +0900 Subject: [PATCH 0982/1117] Pull playlist beatmap checksum from api --- osu.Game/Online/API/APIPlaylistBeatmap.cs | 23 +++++++++++++++++++ .../API/Requests/Responses/APIBeatmap.cs | 2 +- osu.Game/Online/Multiplayer/PlaylistItem.cs | 3 +-- 3 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 osu.Game/Online/API/APIPlaylistBeatmap.cs diff --git a/osu.Game/Online/API/APIPlaylistBeatmap.cs b/osu.Game/Online/API/APIPlaylistBeatmap.cs new file mode 100644 index 0000000000..4f7786e880 --- /dev/null +++ b/osu.Game/Online/API/APIPlaylistBeatmap.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 Newtonsoft.Json; +using osu.Game.Beatmaps; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Rulesets; + +namespace osu.Game.Online.API +{ + public class APIPlaylistBeatmap : APIBeatmap + { + [JsonProperty("checksum")] + public string Checksum { get; set; } + + public override BeatmapInfo ToBeatmap(RulesetStore rulesets) + { + var b = base.ToBeatmap(rulesets); + b.MD5Hash = Checksum; + return b; + } + } +} diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs index e023a2502f..ae65ac09b2 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs @@ -64,7 +64,7 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty(@"max_combo")] private int? maxCombo { get; set; } - public BeatmapInfo ToBeatmap(RulesetStore rulesets) + public virtual BeatmapInfo ToBeatmap(RulesetStore rulesets) { var set = BeatmapSet?.ToBeatmapSet(rulesets); diff --git a/osu.Game/Online/Multiplayer/PlaylistItem.cs b/osu.Game/Online/Multiplayer/PlaylistItem.cs index 9d6e8eb8e3..416091a1aa 100644 --- a/osu.Game/Online/Multiplayer/PlaylistItem.cs +++ b/osu.Game/Online/Multiplayer/PlaylistItem.cs @@ -7,7 +7,6 @@ using Newtonsoft.Json; using osu.Framework.Bindables; using osu.Game.Beatmaps; using osu.Game.Online.API; -using osu.Game.Online.API.Requests.Responses; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; @@ -37,7 +36,7 @@ namespace osu.Game.Online.Multiplayer public readonly BindableList RequiredMods = new BindableList(); [JsonProperty("beatmap")] - private APIBeatmap apiBeatmap { get; set; } + private APIPlaylistBeatmap apiBeatmap { get; set; } private APIMod[] allowedModsBacking; From 68fbe9f4c144ad8be6be89286053fb08ccd5f50d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Jun 2020 14:03:50 +0900 Subject: [PATCH 0983/1117] Add checksum validation to the ready/start button --- .../Multi/Match/Components/ReadyButton.cs | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/osu.Game/Screens/Multi/Match/Components/ReadyButton.cs b/osu.Game/Screens/Multi/Match/Components/ReadyButton.cs index e1f86fcc97..a64f24dd7e 100644 --- a/osu.Game/Screens/Multi/Match/Components/ReadyButton.cs +++ b/osu.Game/Screens/Multi/Match/Components/ReadyButton.cs @@ -3,6 +3,7 @@ using System; using System.Linq; +using System.Linq.Expressions; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Beatmaps; @@ -52,24 +53,14 @@ namespace osu.Game.Screens.Multi.Match.Components private void updateSelectedItem(PlaylistItem item) { - hasBeatmap = false; - - int? beatmapId = SelectedItem.Value?.Beatmap.Value?.OnlineBeatmapID; - if (beatmapId == null) - return; - - hasBeatmap = beatmaps.QueryBeatmap(b => b.OnlineBeatmapID == beatmapId) != null; + hasBeatmap = findBeatmap(expr => beatmaps.QueryBeatmap(expr)); } private void beatmapUpdated(ValueChangedEvent> weakSet) { if (weakSet.NewValue.TryGetTarget(out var set)) { - int? beatmapId = SelectedItem.Value?.Beatmap.Value?.OnlineBeatmapID; - if (beatmapId == null) - return; - - if (set.Beatmaps.Any(b => b.OnlineBeatmapID == beatmapId)) + if (findBeatmap(expr => set.Beatmaps.AsQueryable().FirstOrDefault(expr))) Schedule(() => hasBeatmap = true); } } @@ -78,15 +69,22 @@ namespace osu.Game.Screens.Multi.Match.Components { if (weakSet.NewValue.TryGetTarget(out var set)) { - int? beatmapId = SelectedItem.Value?.Beatmap.Value?.OnlineBeatmapID; - if (beatmapId == null) - return; - - if (set.Beatmaps.Any(b => b.OnlineBeatmapID == beatmapId)) + if (findBeatmap(expr => set.Beatmaps.AsQueryable().FirstOrDefault(expr))) Schedule(() => hasBeatmap = false); } } + private bool findBeatmap(Func>, BeatmapInfo> expression) + { + int? beatmapId = SelectedItem.Value?.Beatmap.Value?.OnlineBeatmapID; + string checksum = SelectedItem.Value?.Beatmap.Value?.MD5Hash; + + if (beatmapId == null || checksum == null) + return false; + + return expression(b => b.OnlineBeatmapID == beatmapId && b.MD5Hash == checksum) != null; + } + protected override void Update() { base.Update(); From b41bb5a6824d8f4467b4178708cce88ace77011c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Jun 2020 14:04:00 +0900 Subject: [PATCH 0984/1117] Update databased MD5 hash on save --- osu.Game/Beatmaps/BeatmapManager.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index f626b45e42..e5907809f3 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -201,7 +201,9 @@ namespace osu.Game.Beatmaps using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) new LegacyBeatmapEncoder(beatmapContent).Encode(sw); - stream.Seek(0, SeekOrigin.Begin); + var attachedInfo = setInfo.Beatmaps.Single(b => b.ID == info.ID); + var md5Hash = stream.ComputeMD5Hash(); + attachedInfo.MD5Hash = md5Hash; UpdateFile(setInfo, setInfo.Files.Single(f => string.Equals(f.Filename, info.Path, StringComparison.OrdinalIgnoreCase)), stream); } From 17e91695e0cf982b76b34db1184aca8be4a7020b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Jun 2020 14:04:51 +0900 Subject: [PATCH 0985/1117] Add checksum validation to the panel download buttons --- .../Screens/Multi/DrawableRoomPlaylistItem.cs | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Multi/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/Multi/DrawableRoomPlaylistItem.cs index c024304856..414c1f5748 100644 --- a/osu.Game/Screens/Multi/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/Multi/DrawableRoomPlaylistItem.cs @@ -188,7 +188,7 @@ namespace osu.Game.Screens.Multi X = -18, Children = new Drawable[] { - new PlaylistDownloadButton(item.Beatmap.Value.BeatmapSet) + new PlaylistDownloadButton(item) { Size = new Vector2(50, 30) }, @@ -212,9 +212,15 @@ namespace osu.Game.Screens.Multi private class PlaylistDownloadButton : BeatmapPanelDownloadButton { - public PlaylistDownloadButton(BeatmapSetInfo beatmapSet) - : base(beatmapSet) + private readonly PlaylistItem playlistItem; + + [Resolved] + private BeatmapManager beatmapManager { get; set; } + + public PlaylistDownloadButton(PlaylistItem playlistItem) + : base(playlistItem.Beatmap.Value.BeatmapSet) { + this.playlistItem = playlistItem; Alpha = 0; } @@ -223,11 +229,26 @@ namespace osu.Game.Screens.Multi base.LoadComplete(); State.BindValueChanged(stateChanged, true); + FinishTransforms(true); } private void stateChanged(ValueChangedEvent state) { - this.FadeTo(state.NewValue == DownloadState.LocallyAvailable ? 0 : 1, 500); + switch (state.NewValue) + { + case DownloadState.LocallyAvailable: + // Perform a local query of the beatmap by beatmap checksum, and reset the state if not matching. + if (beatmapManager.QueryBeatmap(b => b.MD5Hash == playlistItem.Beatmap.Value.MD5Hash) == null) + State.Value = DownloadState.NotDownloaded; + else + this.FadeTo(0, 500); + + break; + + default: + this.FadeTo(1, 500); + break; + } } } From 46a209540e65ee40bbbd4020128e4e01fbb91ce8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jun 2020 14:28:48 +0900 Subject: [PATCH 0986/1117] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 7ea1f3140b..8dcd2f1c6f 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 305e4e0a92..a70a263cdc 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -24,7 +24,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 016f2ba35d..9652f967f2 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -80,7 +80,7 @@ - + From 90f9905ed0f142121d8e295f8df4873eee3682e7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jun 2020 14:29:17 +0900 Subject: [PATCH 0987/1117] Update resourcse --- 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 8dcd2f1c6f..07be3ab0d2 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index a70a263cdc..4d6358575b 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -25,7 +25,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 9652f967f2..6b55fa51ff 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -71,7 +71,7 @@ - + From 3c85561cdce09b7531aac9ea14bd8a681c159d8c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Jun 2020 14:31:43 +0900 Subject: [PATCH 0988/1117] Add tests --- .../TestSceneDrawableRoomPlaylist.cs | 73 +++++++++++++++++++ osu.Game/Tests/Beatmaps/TestBeatmap.cs | 21 +++++- 2 files changed, 92 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs index 5ef4dd6773..55b026eff6 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs @@ -4,12 +4,18 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Audio; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Platform; using osu.Framework.Testing; +using osu.Game.Beatmaps; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online.Multiplayer; +using osu.Game.Overlays; +using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Screens.Multi; @@ -23,6 +29,18 @@ namespace osu.Game.Tests.Visual.Multiplayer { private TestPlaylist playlist; + private BeatmapManager manager; + private RulesetStore rulesets; + + [BackgroundDependencyLoader] + private void load(GameHost host, AudioManager audio) + { + Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); + Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default)); + + manager.Import(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo.BeatmapSet).Wait(); + } + [Test] public void TestNonEditableNonSelectable() { @@ -182,6 +200,28 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("click delete button", () => InputManager.Click(MouseButton.Left)); } + [Test] + public void TestDownloadButtonHiddenInitiallyWhenBeatmapExists() + { + createPlaylist(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo); + + AddAssert("download button hidden", () => !playlist.ChildrenOfType().Single().IsPresent); + } + + [Test] + public void TestDownloadButtonVisibleInitiallyWhenBeatmapDoesNotExist() + { + var byOnlineId = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo; + byOnlineId.BeatmapSet.OnlineBeatmapSetID = 1337; // Some random ID that does not exist locally. + + var byChecksum = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo; + byChecksum.MD5Hash = "1337"; // Some random checksum that does not exist locally. + + createPlaylist(byOnlineId, byChecksum); + + AddAssert("download buttons shown", () => playlist.ChildrenOfType().All(d => d.IsPresent)); + } + private void moveToItem(int index, Vector2? offset = null) => AddStep($"move mouse to item {index}", () => InputManager.MoveMouseTo(playlist.ChildrenOfType>().ElementAt(index), offset)); @@ -235,6 +275,39 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("wait for items to load", () => playlist.ItemMap.Values.All(i => i.IsLoaded)); } + private void createPlaylist(params BeatmapInfo[] beatmaps) + { + AddStep("create playlist", () => + { + Child = playlist = new TestPlaylist(false, false) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(500, 300) + }; + + int index = 0; + + foreach (var b in beatmaps) + { + playlist.Items.Add(new PlaylistItem + { + ID = index++, + Beatmap = { Value = b }, + Ruleset = { Value = new OsuRuleset().RulesetInfo }, + RequiredMods = + { + new OsuModHardRock(), + new OsuModDoubleTime(), + new OsuModAutoplay() + } + }); + } + }); + + AddUntilStep("wait for items to load", () => playlist.ItemMap.Values.All(i => i.IsLoaded)); + } + private class TestPlaylist : DrawableRoomPlaylist { public new IReadOnlyDictionary> ItemMap => base.ItemMap; diff --git a/osu.Game/Tests/Beatmaps/TestBeatmap.cs b/osu.Game/Tests/Beatmaps/TestBeatmap.cs index a7c84bf692..9fc20fd0f2 100644 --- a/osu.Game/Tests/Beatmaps/TestBeatmap.cs +++ b/osu.Game/Tests/Beatmaps/TestBeatmap.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 System.Collections.Generic; using System.IO; using System.Text; +using osu.Framework.Extensions; using osu.Game.Beatmaps; using osu.Game.IO; using osu.Game.Rulesets; @@ -43,10 +45,25 @@ namespace osu.Game.Tests.Beatmaps private static Beatmap createTestBeatmap() { using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(test_beatmap_data))) - using (var reader = new LineBufferedReader(stream)) - return Decoder.GetDecoder(reader).Decode(reader); + { + using (var reader = new LineBufferedReader(stream)) + { + var b = Decoder.GetDecoder(reader).Decode(reader); + + b.BeatmapInfo.MD5Hash = test_beatmap_hash.Value.md5; + b.BeatmapInfo.Hash = test_beatmap_hash.Value.sha2; + + return b; + } + } } + private static readonly Lazy<(string md5, string sha2)> test_beatmap_hash = new Lazy<(string md5, string sha2)>(() => + { + using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(test_beatmap_data))) + return (stream.ComputeMD5Hash(), stream.ComputeSHA2Hash()); + }); + private const string test_beatmap_data = @"osu file format v14 [General] From 800c46f7524277b9aa8e9af5732bcfb06487a863 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jun 2020 14:39:15 +0900 Subject: [PATCH 0989/1117] Fix test function override --- .../NonVisual/Skinning/LegacySkinTextureFallbackTest.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/NonVisual/Skinning/LegacySkinTextureFallbackTest.cs b/osu.Game.Tests/NonVisual/Skinning/LegacySkinTextureFallbackTest.cs index 867af9c1b8..69e66942ab 100644 --- a/osu.Game.Tests/NonVisual/Skinning/LegacySkinTextureFallbackTest.cs +++ b/osu.Game.Tests/NonVisual/Skinning/LegacySkinTextureFallbackTest.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; +using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Textures; using osu.Game.Skinning; @@ -103,7 +104,7 @@ namespace osu.Game.Tests.NonVisual.Skinning Textures = fileNames.ToDictionary(fileName => fileName, fileName => new Texture(1, 1)); } - public override Texture Get(string name) => Textures.GetValueOrDefault(name); + public override Texture Get(string name, WrapMode wrapModeS, WrapMode wrapModeT) => Textures.GetValueOrDefault(name); } } } From 6c8c95677f65d707cc9ac657138f1274a393b1b7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jun 2020 14:54:55 +0900 Subject: [PATCH 0990/1117] Fix incorrect usage of Directory.GetFiles --- osu.Game/Rulesets/RulesetStore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index 10b6edca8c..58a2ba056e 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -154,7 +154,7 @@ namespace osu.Game.Rulesets { try { - var files = Directory.GetFiles(Path.Combine(RuntimeInfo.StartupDirectory, $"{ruleset_library_prefix}.*.dll")); + var files = Directory.GetFiles(RuntimeInfo.StartupDirectory, $"{ruleset_library_prefix}.*.dll"); foreach (string file in files.Where(f => !Path.GetFileName(f).Contains("Tests"))) loadRulesetFromFile(file); From 70c84811ed00a37b438cea3941000bba12ed418d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jun 2020 15:49:51 +0900 Subject: [PATCH 0991/1117] Revert incorrect change --- osu.Game.Tests/Resources/TestResources.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Tests/Resources/TestResources.cs b/osu.Game.Tests/Resources/TestResources.cs index 1c264f66e0..e882229570 100644 --- a/osu.Game.Tests/Resources/TestResources.cs +++ b/osu.Game.Tests/Resources/TestResources.cs @@ -3,7 +3,6 @@ using System.IO; using NUnit.Framework; -using osu.Framework; using osu.Framework.IO.Stores; namespace osu.Game.Tests.Resources @@ -18,7 +17,7 @@ namespace osu.Game.Tests.Resources public static string GetTestBeatmapForImport(bool virtualTrack = false) { - var tempPath = Path.Combine(RuntimeInfo.StartupDirectory, Path.GetTempFileName() + ".osz"); + var tempPath = Path.GetTempFileName() + ".osz"; using (var stream = GetTestBeatmapStream(virtualTrack)) using (var newFile = File.Create(tempPath)) From fac96f6dddf9dba724fa0b298444694310c508af Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Jun 2020 17:02:01 +0900 Subject: [PATCH 0992/1117] Fix match beatmap not updating after re-download --- .../Multiplayer/TestSceneMatchSubScreen.cs | 57 +++++++++++++++++-- .../Match/Components/MatchSettingsOverlay.cs | 2 +- .../Screens/Multi/Match/MatchSubScreen.cs | 13 +---- 3 files changed, 55 insertions(+), 17 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSubScreen.cs index d678d5a814..6154e646f8 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSubScreen.cs @@ -5,7 +5,9 @@ using System; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Audio; using osu.Framework.Bindables; +using osu.Framework.Platform; using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Beatmaps; @@ -29,14 +31,20 @@ namespace osu.Game.Tests.Visual.Multiplayer [Cached(typeof(IRoomManager))] private readonly TestRoomManager roomManager = new TestRoomManager(); - [Resolved] - private BeatmapManager beatmaps { get; set; } - - [Resolved] - private RulesetStore rulesets { get; set; } + private BeatmapManager manager; + private RulesetStore rulesets; private TestMatchSubScreen match; + [BackgroundDependencyLoader] + private void load(GameHost host, AudioManager audio) + { + Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); + Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default)); + + manager.Import(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo.BeatmapSet).Wait(); + } + [SetUp] public void Setup() => Schedule(() => { @@ -75,10 +83,49 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("first playlist item selected", () => match.SelectedItem.Value == Room.Playlist[0]); } + [Test] + public void TestBeatmapUpdatedOnReImport() + { + BeatmapSetInfo importedSet = null; + + AddStep("import altered beatmap", () => + { + var beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo); + beatmap.BeatmapInfo.BaseDifficulty.CircleSize = 1; + + importedSet = manager.Import(beatmap.BeatmapInfo.BeatmapSet).Result; + }); + + AddStep("load room", () => + { + Room.Name.Value = "my awesome room"; + Room.Host.Value = new User { Id = 2, Username = "peppy" }; + Room.Playlist.Add(new PlaylistItem + { + Beatmap = { Value = importedSet.Beatmaps[0] }, + Ruleset = { Value = new OsuRuleset().RulesetInfo } + }); + }); + + AddStep("create room", () => + { + InputManager.MoveMouseTo(match.ChildrenOfType().Single()); + InputManager.Click(MouseButton.Left); + }); + + AddAssert("match has altered beatmap", () => match.Beatmap.Value.Beatmap.BeatmapInfo.BaseDifficulty.CircleSize == 1); + + AddStep("re-import original beatmap", () => manager.Import(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo.BeatmapSet).Wait()); + + AddAssert("match has original beatmap", () => match.Beatmap.Value.Beatmap.BeatmapInfo.BaseDifficulty.CircleSize != 1); + } + private class TestMatchSubScreen : MatchSubScreen { public new Bindable SelectedItem => base.SelectedItem; + public new Bindable Beatmap => base.Beatmap; + public TestMatchSubScreen(Room room) : base(room) { diff --git a/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs b/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs index 54c4f8f7c7..49a0fc434b 100644 --- a/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs +++ b/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs @@ -433,7 +433,7 @@ namespace osu.Game.Screens.Multi.Match.Components } } - private class CreateRoomButton : TriangleButton + public class CreateRoomButton : TriangleButton { public CreateRoomButton() { diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs index e1d72d9600..bbfbaf81af 100644 --- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs @@ -207,6 +207,8 @@ namespace osu.Game.Screens.Multi.Match Ruleset.Value = item.Ruleset.Value; } + private void beatmapUpdated(ValueChangedEvent> weakSet) => Schedule(updateWorkingBeatmap); + private void updateWorkingBeatmap() { var beatmap = SelectedItem.Value?.Beatmap.Value; @@ -217,17 +219,6 @@ namespace osu.Game.Screens.Multi.Match Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap); } - private void beatmapUpdated(ValueChangedEvent> weakSet) - { - Schedule(() => - { - if (Beatmap.Value != beatmapManager.DefaultBeatmap) - return; - - updateWorkingBeatmap(); - }); - } - private void onStart() { switch (type.Value) From dfb9687fb5bfc47670ea6b017410e46c76c5905c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Jun 2020 17:22:09 +0900 Subject: [PATCH 0993/1117] Extract update into PreUpdate(), add test --- osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs | 3 +++ osu.Game/Beatmaps/BeatmapManager.cs | 17 +++++++++++++---- osu.Game/Database/ArchiveModelManager.cs | 10 ++++++++++ 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 5eb11a3264..88bb39a521 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -602,6 +602,8 @@ namespace osu.Game.Tests.Beatmaps.IO Beatmap beatmapToUpdate = (Beatmap)manager.GetWorkingBeatmap(setToUpdate.Beatmaps.First(b => b.RulesetID == 0)).Beatmap; BeatmapSetFileInfo fileToUpdate = setToUpdate.Files.First(f => beatmapToUpdate.BeatmapInfo.Path.Contains(f.Filename)); + string oldMd5Hash = beatmapToUpdate.BeatmapInfo.MD5Hash; + using (var stream = new MemoryStream()) { using (var writer = new StreamWriter(stream, Encoding.UTF8, 1024, true)) @@ -624,6 +626,7 @@ namespace osu.Game.Tests.Beatmaps.IO Beatmap updatedBeatmap = (Beatmap)manager.GetWorkingBeatmap(manager.QueryBeatmap(b => b.ID == beatmapToUpdate.BeatmapInfo.ID)).Beatmap; Assert.That(updatedBeatmap.HitObjects.Count, Is.EqualTo(1)); Assert.That(updatedBeatmap.HitObjects[0].StartTime, Is.EqualTo(5000)); + Assert.That(updatedBeatmap.BeatmapInfo.MD5Hash, Is.Not.EqualTo(oldMd5Hash)); } finally { diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index e5907809f3..668ac6ee10 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -201,10 +201,6 @@ namespace osu.Game.Beatmaps using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) new LegacyBeatmapEncoder(beatmapContent).Encode(sw); - var attachedInfo = setInfo.Beatmaps.Single(b => b.ID == info.ID); - var md5Hash = stream.ComputeMD5Hash(); - attachedInfo.MD5Hash = md5Hash; - UpdateFile(setInfo, setInfo.Files.Single(f => string.Equals(f.Filename, info.Path, StringComparison.OrdinalIgnoreCase)), stream); } @@ -213,6 +209,19 @@ namespace osu.Game.Beatmaps workingCache.Remove(working); } + protected override void PreUpdate(BeatmapSetInfo item) + { + base.PreUpdate(item); + + foreach (var info in item.Beatmaps) + { + var file = item.Files.FirstOrDefault(f => string.Equals(f.Filename, info.Path, StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath; + + using (var stream = Files.Store.GetStream(file)) + info.MD5Hash = stream.ComputeMD5Hash(); + } + } + private readonly WeakList workingCache = new WeakList(); /// diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index ae55a7b14a..f7e81ae4bc 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -430,10 +430,20 @@ namespace osu.Game.Database { item.Hash = computeHash(item); + PreUpdate(item); + ModelStore.Update(item); } } + /// + /// Perform any final actions before the update to database executes. + /// + /// The that is being updated. + protected virtual void PreUpdate(TModel item) + { + } + /// /// Delete an item from the manager. /// Is a no-op for already deleted items. From 665530f1c3a9b43ac2925220ee41efc767919974 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jun 2020 17:22:59 +0900 Subject: [PATCH 0994/1117] Remove excess newline --- .../Edit/Blueprints/TaikoSpanPlacementBlueprint.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Edit/Blueprints/TaikoSpanPlacementBlueprint.cs b/osu.Game.Rulesets.Taiko/Edit/Blueprints/TaikoSpanPlacementBlueprint.cs index 7f96b5a46e..a88eff13a1 100644 --- a/osu.Game.Rulesets.Taiko/Edit/Blueprints/TaikoSpanPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Taiko/Edit/Blueprints/TaikoSpanPlacementBlueprint.cs @@ -25,7 +25,6 @@ namespace osu.Game.Rulesets.Taiko.Edit.Blueprints public TaikoSpanPlacementBlueprint(HitObject hitObject) : base(hitObject) - { spanPlacementObject = hitObject as IHasDuration; From 828180ad9b3e6006e70c811e107d340ae18b603e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jun 2020 19:29:22 +0900 Subject: [PATCH 0995/1117] Add default --- osu.Game.Tournament/Screens/SetupScreen.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Screens/SetupScreen.cs b/osu.Game.Tournament/Screens/SetupScreen.cs index e1594de69e..2bd0bcf2f2 100644 --- a/osu.Game.Tournament/Screens/SetupScreen.cs +++ b/osu.Game.Tournament/Screens/SetupScreen.cs @@ -211,7 +211,8 @@ namespace osu.Game.Tournament.Screens private class ResolutionSelector : ActionableInfo { private const int minimum_window_height = 480; - private const int maximum_window_height = 2160; // 4k + private const int maximum_window_height = 2160; + public new Action Action; private OsuNumberBox numberBox; @@ -221,6 +222,7 @@ namespace osu.Game.Tournament.Screens var drawable = base.CreateComponent(); FlowContainer.Insert(-1, numberBox = new OsuNumberBox { + Text = "1080", Width = 100 }); From 78fddc895738ec770447f025bdc8a487bdd5c988 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jun 2020 19:29:59 +0900 Subject: [PATCH 0996/1117] Make button match height --- osu.Game.Tournament/Screens/SetupScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Screens/SetupScreen.cs b/osu.Game.Tournament/Screens/SetupScreen.cs index 2bd0bcf2f2..cf8eb8bd6c 100644 --- a/osu.Game.Tournament/Screens/SetupScreen.cs +++ b/osu.Game.Tournament/Screens/SetupScreen.cs @@ -199,7 +199,7 @@ namespace osu.Game.Tournament.Screens { button = new TriangleButton { - Size = new Vector2(100, 30), + Size = new Vector2(100, 40), Action = () => Action?.Invoke() } } From f63c66396f7a2f1f462377ae4a3b6c4c9e4dce40 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Tue, 2 Jun 2020 13:32:52 +0200 Subject: [PATCH 0997/1117] Apply review suggestions. --- .../Visual/Gameplay/TestSceneReplay.cs | 2 +- osu.Game/Rulesets/Ruleset.cs | 2 ++ osu.Game/Screens/Select/PlaySongSelect.cs | 25 +++++++++++++------ 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplay.cs index 1908988739..3a71d4ca54 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplay.cs @@ -19,7 +19,7 @@ namespace osu.Game.Tests.Visual.Gameplay { var beatmap = Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo, Array.Empty()); - return new ScoreAccessibleReplayPlayer(ruleset.GetAutoplayMod().CreateReplayScore(beatmap)); + return new ScoreAccessibleReplayPlayer(ruleset.GetAutoplayMod()?.CreateReplayScore(beatmap)); } protected override void AddCheckSteps() diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 8f41e421a3..4f28607733 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -22,6 +22,7 @@ using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Skinning; using osu.Game.Users; +using JetBrains.Annotations; namespace osu.Game.Rulesets { @@ -100,6 +101,7 @@ namespace osu.Game.Rulesets return value; } + [CanBeNull] public ModAutoplay GetAutoplayMod() => GetAllMods().OfType().FirstOrDefault(); public virtual ISkin CreateLegacySkinProvider(ISkinSource source, IBeatmap beatmap) => null; diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index 71ab3715e0..a0201e696f 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -7,6 +7,8 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; using osu.Framework.Screens; using osu.Game.Graphics; +using osu.Game.Overlays; +using osu.Game.Overlays.Notifications; using osu.Game.Scoring; using osu.Game.Screens.Play; using osu.Game.Screens.Ranking; @@ -20,6 +22,9 @@ namespace osu.Game.Screens.Select private bool removeAutoModOnResume; private OsuScreen player; + [Resolved] + private NotificationOverlay notifications { get; set; } + public override bool AllowExternalScreenChange => true; protected override UserActivity InitialActivity => new UserActivity.ChoosingBeatmap(); @@ -83,15 +88,21 @@ namespace osu.Game.Screens.Select var auto = Ruleset.Value.CreateInstance().GetAutoplayMod(); var autoType = auto?.GetType(); - if (autoType != null) - { - var mods = Mods.Value; + var mods = Mods.Value; - if (mods.All(m => m.GetType() != autoType)) + if (autoType == null) + { + notifications.Post(new SimpleNotification { - Mods.Value = mods.Append(auto).ToArray(); - removeAutoModOnResume = true; - } + Text = "The current ruleset doesn't have an autoplay mod avalaible!" + }); + return false; + } + + if (mods.All(m => m.GetType() != autoType)) + { + Mods.Value = mods.Append(auto).ToArray(); + removeAutoModOnResume = true; } } From 61f906d9c462279b89881f1083758f493eb34450 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jun 2020 21:02:09 +0900 Subject: [PATCH 0998/1117] Fix span piece being incorrect in some drag scenarios --- .../Blueprints/TaikoSpanPlacementBlueprint.cs | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Edit/Blueprints/TaikoSpanPlacementBlueprint.cs b/osu.Game.Rulesets.Taiko/Edit/Blueprints/TaikoSpanPlacementBlueprint.cs index a88eff13a1..468d980b23 100644 --- a/osu.Game.Rulesets.Taiko/Edit/Blueprints/TaikoSpanPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Taiko/Edit/Blueprints/TaikoSpanPlacementBlueprint.cs @@ -48,6 +48,7 @@ namespace osu.Game.Rulesets.Taiko.Edit.Blueprints } private double originalStartTime; + private Vector2 originalPosition; protected override bool OnMouseDown(MouseDownEvent e) { @@ -73,22 +74,25 @@ namespace osu.Game.Rulesets.Taiko.Edit.Blueprints if (PlacementActive) { - if (result.Time is double endTime) + if (result.Time is double dragTime) { - if (endTime < originalStartTime) + if (dragTime < originalStartTime) { - HitObject.StartTime = endTime; - spanPlacementObject.Duration = Math.Abs(endTime - originalStartTime); + HitObject.StartTime = dragTime; + spanPlacementObject.Duration = Math.Abs(dragTime - originalStartTime); headPiece.Position = ToLocalSpace(result.ScreenSpacePosition); - lengthPiece.X = headPiece.X; - lengthPiece.Width = tailPiece.X - headPiece.X; + tailPiece.Position = originalPosition; } else { - spanPlacementObject.Duration = Math.Abs(endTime - originalStartTime); + HitObject.StartTime = originalStartTime; + spanPlacementObject.Duration = Math.Abs(dragTime - originalStartTime); tailPiece.Position = ToLocalSpace(result.ScreenSpacePosition); - lengthPiece.Width = tailPiece.X - headPiece.X; + headPiece.Position = originalPosition; } + + lengthPiece.X = headPiece.X; + lengthPiece.Width = tailPiece.X - headPiece.X; } } else @@ -96,7 +100,10 @@ namespace osu.Game.Rulesets.Taiko.Edit.Blueprints lengthPiece.Position = headPiece.Position = tailPiece.Position = ToLocalSpace(result.ScreenSpacePosition); if (result.Time is double startTime) + { originalStartTime = HitObject.StartTime = startTime; + originalPosition = ToLocalSpace(result.ScreenSpacePosition); + } } } } From 275d95082a88f63e5bc0ba7be8d6efd4950fc30e Mon Sep 17 00:00:00 2001 From: Lucas A Date: Tue, 2 Jun 2020 16:01:01 +0200 Subject: [PATCH 0999/1117] Fix crash in testing environment. --- osu.Game/Screens/Select/PlaySongSelect.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index a0201e696f..2236aa4d72 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -22,7 +22,7 @@ namespace osu.Game.Screens.Select private bool removeAutoModOnResume; private OsuScreen player; - [Resolved] + [Resolved(CanBeNull = true)] private NotificationOverlay notifications { get; set; } public override bool AllowExternalScreenChange => true; @@ -92,7 +92,7 @@ namespace osu.Game.Screens.Select if (autoType == null) { - notifications.Post(new SimpleNotification + notifications?.Post(new SimpleNotification { Text = "The current ruleset doesn't have an autoplay mod avalaible!" }); From dc41e74e1912ea9a49a641ab091ed536f6f5e38a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Jun 2020 23:47:18 +0900 Subject: [PATCH 1000/1117] Fix cursor trail not displaying --- osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index 37df5ec540..9bcb3abc63 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -237,6 +237,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor { Position = new Vector2(part.Position.X - size.X / 2, part.Position.Y + size.Y / 2), TexturePosition = textureRect.BottomLeft, + TextureRect = new Vector4(0, 0, 1, 1), Colour = DrawColourInfo.Colour.BottomLeft.Linear, Time = part.Time }); @@ -245,6 +246,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor { Position = new Vector2(part.Position.X + size.X / 2, part.Position.Y + size.Y / 2), TexturePosition = textureRect.BottomRight, + TextureRect = new Vector4(0, 0, 1, 1), Colour = DrawColourInfo.Colour.BottomRight.Linear, Time = part.Time }); @@ -253,6 +255,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor { Position = new Vector2(part.Position.X + size.X / 2, part.Position.Y - size.Y / 2), TexturePosition = textureRect.TopRight, + TextureRect = new Vector4(0, 0, 1, 1), Colour = DrawColourInfo.Colour.TopRight.Linear, Time = part.Time }); @@ -261,6 +264,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor { Position = new Vector2(part.Position.X - size.X / 2, part.Position.Y - size.Y / 2), TexturePosition = textureRect.TopLeft, + TextureRect = new Vector4(0, 0, 1, 1), Colour = DrawColourInfo.Colour.TopLeft.Linear, Time = part.Time }); @@ -290,6 +294,9 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor [VertexMember(2, VertexAttribPointerType.Float)] public Vector2 TexturePosition; + [VertexMember(4, VertexAttribPointerType.Float)] + public Vector4 TextureRect; + [VertexMember(1, VertexAttribPointerType.Float)] public float Time; From 40e64eed475b7c09de60643671035e5cd0ec9967 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 2 Jun 2020 23:15:14 +0200 Subject: [PATCH 1001/1117] Add contributing guidelines --- CONTRIBUTING.md | 121 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..441521f9ef --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,121 @@ +# Contributing Guidelines + +Thank you for showing interest in the development of osu!lazer! We aim to provide a good collaborating environment for everyone involved, and as such have decided to list some of the most important things to keep in mind in the process. The guidelines below have been chosen based on past experience. + +These are not "official rules" *per se*, but following them will help everyone deal with things in the most efficient manner. + +## Table of contents + +1. [I would like to submit an issue!](#i-would-like-to-submit-an-issue) +2. [I would like to submit a pull request!](#i-would-like-to-submit-a-pull-request) + +## I would like to submit an issue! + +When it comes to issues, bug reports and feature suggestions are welcomed, although please keep in mind that at any point in time, hundreds of issues are open, which vary in severity and the amount of time needed to address them. As such it's not uncommon for issues to remain unresolved for a long time or even closed outright if they are deemed not important enough to fix in the foreseeable future. Issues that are required to "go live" or otherwise achieve parity with stable are prioritised the most. + +* **Before submitting an issue, try searching existing issues first.** + + For housekeeping purposes, we close issues that overlap with or duplicate other pre-existing issues - you can help us not have to do that by searching existing issues yourself first. The issue search box, as well as the issue tag system, are tools you can use to check if an issue has been reported before. + +* **When submitting a bug report, please try to include as much detail as possible.** + + Bugs are not equal - some of them will be reproducible every time on pretty much all hardware, while others will be hard to track down due to being specific to particular hardware or even somewhat random in nature. As such, providing as much detail as possible when reporting a bug is hugely appreciated. A good starting set of information contains of: + + * the in-game logs, which are located at: + * `%AppData%/osu/logs` (on Windows), + * `~/.local/share/osu/logs` (on Linux and macOS), + * `Android/Data/sh.ppy.osulazer/logs` (on Android), + * on iOS they can be obtained by connecting your device to your desktop and copying the `logs` directory from the app's own document storage using iTunes, + * your system specifications (including the operating system and platform you are playing on), + * a reproduction scenario (list of steps you have performed leading up to the occurrence of the bug), + * a video or picture of the bug, if at all possible. + +* **Provide more information when asked to do so.** + + Sometimes when a bug is more elusive or complicated, none of the information listed above will pinpoint a concrete cause of the problem. In this case we will most likely ask you for additional info, such as a Windows Event Log dump or a copy of your local lazer database (`client.db`). Providing that information is beneficial to both parties - we can track down the problem better, and hopefully fix it for you at some point once we know where it is! + +* **When submitting a feature proposal, please describe it in the most understandable way you can.** + + Communicating your idea for a feature can often be hard, and we would like to avoid any misunderstandings. As such, please try to explain your idea in a short, but understandable manner - it's best to avoid jargon or terms and references that could be considered obscure. A mock-up picture (doesn't have to be good!) of the feature can also go a long way in explaining. + +* **Refrain from posting "+1" comments.** + + If an issue has already been created, saying that you also experience it without providing any additional details doesn't really help us in any way. To express support for a proposal or indicate that you are also affected by a particular bug, you can use comment reactions instead. + +* **Refrain from asking if an issue has been resolved yet.** + + As mentioned above, the issue tracker has hundreds of issues open at any given time. Currently the game is being worked on by two members of the core team, and a handful of outside contributors who offer their free time to help out. As such, it can happen that an issue gets placed on the backburner due to being less important; generally posting a comment demanding its resolution some months or years after it is reported is not very likely to increase its priority. + +* **Avoid long discussions about non-development topics.** + + GitHub is mostly a developer space, and as such isn't really fit for lengthened discussions about gameplay mechanics (which might not even be in any way confirmed for the final release) and similar non-technical matters. Such matters are probably best addressed at the osu! forums. + +## I would like to submit a pull request! + +We also welcome pull requests from unaffiliated contributors. The issue tracker should provide plenty of issues that you can work on; we also mark issues that we think would be good for newcomers with the [`good-first-issue`](https://github.com/ppy/osu/issues?q=is%3Aissue+is%3Aopen+label%3Agood-first-issue) label. + +However, do keep in mind that the core team is committed to bringing osu!lazer up to par with stable first and foremost, so depending on what your contribution concerns, it might not be merged and released right away. Our approach to managing issues and their priorities is described [in the wiki](https://github.com/ppy/osu/wiki/Project-management). + +Here are some key things to note before jumping in: + +* **Make sure you are comfortable with C\# and your development environment.** + + While we are accepting of all kinds of contributions, we also have a certain quality standard we'd like to uphold and limited time to review your code. Therefore, we would like to avoid providing entry-level advice, and as such if you're not very familiar with C\# as a programming language, we'd recommend that you start off with a few personal projects to get acquainted with the language's syntax, toolchain and principles of object-oriented programming first. + +* **Make sure you are familiar with git and the pull request workflow.** + + [git](https://git-scm.com/) is a distributed version control system that might not be very intuitive at the beginning if you're not familiar with version control. In particular, projects using git have a particular workflow for submitting code changes, which is called the pull request workflow. + + To make things run more smoothly, we recommend that you look up some online resources to familiarise yourself with the git vocabulary and commands, and practice working with forks and submitting pull requests at your own pace. A high-level overview of the process can be found in [this article by GitHub](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/proposing-changes-to-your-work-with-pull-requests). + +* **Make sure to submit pull requests off of a topic branch.** + + As described in the article linked in the previous point, topic branches help you parallelise your work and separate it from the main `master` branch, and additionally are easier for maintainers to work with. Working with multiple `master` branches across many remotes is difficult to keep track of, and it's easy to make a mistake and push to the wrong `master` branch by accident. + +* **Refrain from making changes through the GitHub web interface.** + + Even though GitHub provides an option to edit code or replace files in the repository using the web interface, we strongly discourage using it in most scenarios. Editing files this way is inefficient and likely to introduce whitespace or file encoding changes that make it more difficult to review the code. + + Code written through the web interface will also very likely be questioned outright by the reviewers, as it is likely that it has not been properly tested or that it will fail continuous integration checks. We strongly encourage using an IDE like [Visual Studio](https://visualstudio.microsoft.com/), [Visual Studio Code](https://code.visualstudio.com/) or [JetBrains Rider](https://www.jetbrains.com/rider/) instead. + +* **Add tests for your code whenever possible.** + + Automated tests are an essential part of a quality and reliable codebase. They help to make the code more maintainable by ensuring it is safe to reorganise (or refactor) the code in various ways, and also prevent regressions - bugs that resurface after having been fixed at some point in the past. If it is viable, please put in the time to add tests, so that the changes you make can last for a (hopefully) very long time. + +* **Run tests before opening a pull request.** + + Tying into the previous point, sometimes changes in one part of the codebase can result in unpredictable changes in behaviour in other pieces of the code. This is why it is best to always try to run tests before opening a PR. + + Continuous integration will always run the tests for you (and us), too, but it is best not to rely on it, as there might be many builds queued at any time. Running tests on your own will help you be more certain that at the point of clicking the "Create pull request" button, your changes are as ready as can be. + +* **Run code style analysis before opening a pull request.** + + As part of continuous integration, we also run code style analysis, which is supposed to make sure that your code is formatted the same way as all the pre-existing code in the repository. The reason we enforce a particular code style everywhere is to make sure the codebase is consistent in that regard - having one whitespace convention in one place and another one elsewhere causes disorganisation. + +* **Make sure to keep the *Allow edits from maintainers* check box checked.** + + To speed up the merging process, collaborators and team members will sometimes want to push changes to your branch themselves, to make minor code style adjustments or to otherwise refactor the code without having to describe how they'd like the code to look like in painstaking detail. Having the *Allow edits from maintainers* check box checked lets them do that; without it they are forced to report issues back to you and wait for you to address them. + +* **Refrain from continually merging the master branch back to the PR.** + + Unless there are merge conflicts that need resolution, there is no need to keep merging `master` back to a branch over and over again. One of the maintainers will merge `master` themselves before merging the PR itself anyway, and continual merge commits can cause CI to get overwhelmed due to queueing up too many builds. + +* **Refrain from force-pushing to the PR branch.** + + Force-pushing should be avoided, as it can lead to accidentally overwriting a maintainer's changes or CI building wrong commits. We value all history in the project, so there is no need to squash or amend commits in most cases. + + The cases in which force-pushing is warranted are very rare (such as accidentally leaking sensitive info in one of the files committed, adding unrelated files, or mis-merging a dependent PR). + +* **Be patient when waiting for the code to be reviewed and merged.** + + As much as we'd like to review all contributions as fast as possible, our time is limited, as team members have to work on their own tasks in addition to reviewing code. As such, work needs to be prioritised, and it can unfortunately take weeks or months for your PR to be merged, depending on how important it is deemed to be. + +* **Don't mistake criticism of code for criticism of your person.** + + As mentioned before, we are highly committed to quality when it comes to the lazer project. This means that contributions from less experienced community members can take multiple rounds of review to get to a mergeable state. We try our utmost best to never conflate a person with the code they authored, and to keep the discussion focused on the code at all times. Please consider our comments and requests a learning experience, and don't treat it as a personal attack. + +* **Feel free to reach out for help.** + + If you're uncertain about some part of the codebase or some inner workings of the game and framework, please reach out either by leaving a comment in the relevant issue or PR thread, or by posting a message in the [development Discord server](https://discord.gg/ppy). We will try to help you as much as we can. + + When it comes to which form of communication is best, GitHub generally lends better to longer-form discussions, while Discord is better for snappy call-and-response answers. Use your best discretion when deciding, and try to keep a single discussion in one place instead of moving back and forth. From f4f84ede6a2ff88b3f0cf7517c89a59ef309ed20 Mon Sep 17 00:00:00 2001 From: Shane Woolcock Date: Wed, 3 Jun 2020 10:43:16 +0930 Subject: [PATCH 1002/1117] Fix results screen crashing for beatmaps with no online ID --- osu.Game/Screens/Ranking/ResultsScreen.cs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index fbb9b95478..145ba93573 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -140,14 +140,17 @@ namespace osu.Game.Screens.Ranking { base.LoadComplete(); - var req = FetchScores(scores => Schedule(() => + if (Score.Beatmap.OnlineBeatmapID != null) { - foreach (var s in scores) - panels.AddScore(s); - })); + var req = FetchScores(scores => Schedule(() => + { + foreach (var s in scores) + panels.AddScore(s); + })); - if (req != null) - api.Queue(req); + if (req != null) + api.Queue(req); + } } /// From 90213d079d33713d8729739675910ebe1cfdda24 Mon Sep 17 00:00:00 2001 From: Shane Woolcock Date: Wed, 3 Jun 2020 10:48:27 +0930 Subject: [PATCH 1003/1117] Include submission status in check --- osu.Game/Screens/Ranking/ResultsScreen.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index 145ba93573..25c8205c30 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Screens; +using osu.Game.Beatmaps; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; @@ -140,7 +141,7 @@ namespace osu.Game.Screens.Ranking { base.LoadComplete(); - if (Score.Beatmap.OnlineBeatmapID != null) + if (Score.Beatmap.OnlineBeatmapID != null && Score.Beatmap.Status > BeatmapSetOnlineStatus.Pending) { var req = FetchScores(scores => Schedule(() => { From 96e3c6e8e888d47069cc636c3049178cae09733b Mon Sep 17 00:00:00 2001 From: Shane Woolcock Date: Wed, 3 Jun 2020 11:36:47 +0930 Subject: [PATCH 1004/1117] Move check to SoloResultsScreen --- osu.Game/Screens/Ranking/ResultsScreen.cs | 15 ++++++--------- osu.Game/Screens/Ranking/SoloResultsScreen.cs | 4 ++++ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index 25c8205c30..aff0540652 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -141,17 +141,14 @@ namespace osu.Game.Screens.Ranking { base.LoadComplete(); - if (Score.Beatmap.OnlineBeatmapID != null && Score.Beatmap.Status > BeatmapSetOnlineStatus.Pending) + var req = FetchScores(scores => Schedule(() => { - var req = FetchScores(scores => Schedule(() => - { - foreach (var s in scores) - panels.AddScore(s); - })); + foreach (var s in scores) + panels.AddScore(s); + })); - if (req != null) - api.Queue(req); - } + if (req != null) + api.Queue(req); } /// diff --git a/osu.Game/Screens/Ranking/SoloResultsScreen.cs b/osu.Game/Screens/Ranking/SoloResultsScreen.cs index 3ae723683a..9cf2e6757a 100644 --- a/osu.Game/Screens/Ranking/SoloResultsScreen.cs +++ b/osu.Game/Screens/Ranking/SoloResultsScreen.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; +using osu.Game.Beatmaps; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Rulesets; @@ -24,6 +25,9 @@ namespace osu.Game.Screens.Ranking protected override APIRequest FetchScores(Action> scoresCallback) { + if (Score.Beatmap.OnlineBeatmapID == null || Score.Beatmap.Status <= BeatmapSetOnlineStatus.Pending) + return null; + var req = new GetScoresRequest(Score.Beatmap, Score.Ruleset); req.Success += r => scoresCallback?.Invoke(r.Scores.Where(s => s.OnlineScoreID != Score.OnlineScoreID).Select(s => s.CreateScoreInfo(rulesets))); return req; From 0d5a2cf96d089038615d8f30572ae4b7e9b7c7ed Mon Sep 17 00:00:00 2001 From: Shane Woolcock Date: Wed, 3 Jun 2020 11:36:59 +0930 Subject: [PATCH 1005/1117] Add unit tests --- .../Visual/Ranking/TestSceneResultsScreen.cs | 41 ++++++++++++++++--- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs b/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs index 242766ad4b..125aa0a1e7 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs @@ -36,12 +36,14 @@ namespace osu.Game.Tests.Visual.Ranking Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmapInfo); } - private TestSoloResults createResultsScreen() => new TestSoloResults(new TestScoreInfo(new OsuRuleset().RulesetInfo)); + private TestResultsScreen createResultsScreen() => new TestResultsScreen(new TestScoreInfo(new OsuRuleset().RulesetInfo)); + + private UnrankedSoloResultsScreen createUnrankedSoloResultsScreen() => new UnrankedSoloResultsScreen(new TestScoreInfo(new OsuRuleset().RulesetInfo)); [Test] public void ResultsWithoutPlayer() { - TestSoloResults screen = null; + TestResultsScreen screen = null; OsuScreenStack stack; AddStep("load results", () => @@ -60,13 +62,23 @@ namespace osu.Game.Tests.Visual.Ranking [Test] public void ResultsWithPlayer() { - TestSoloResults screen = null; + TestResultsScreen screen = null; AddStep("load results", () => Child = new TestResultsContainer(screen = createResultsScreen())); AddUntilStep("wait for loaded", () => screen.IsLoaded); AddAssert("retry overlay present", () => screen.RetryOverlay != null); } + [Test] + public void ResultsForUnranked() + { + UnrankedSoloResultsScreen screen = null; + + AddStep("load results", () => Child = new TestResultsContainer(screen = createUnrankedSoloResultsScreen())); + AddUntilStep("wait for loaded", () => screen.IsLoaded); + AddAssert("retry overlay present", () => screen.RetryOverlay != null); + } + private class TestResultsContainer : Container { [Cached(typeof(Player))] @@ -86,11 +98,11 @@ namespace osu.Game.Tests.Visual.Ranking } } - private class TestSoloResults : ResultsScreen + private class TestResultsScreen : ResultsScreen { public HotkeyRetryOverlay RetryOverlay; - public TestSoloResults(ScoreInfo score) + public TestResultsScreen(ScoreInfo score) : base(score) { } @@ -102,5 +114,24 @@ namespace osu.Game.Tests.Visual.Ranking RetryOverlay = InternalChildren.OfType().SingleOrDefault(); } } + + private class UnrankedSoloResultsScreen : SoloResultsScreen + { + public HotkeyRetryOverlay RetryOverlay; + + public UnrankedSoloResultsScreen(ScoreInfo score) + : base(score) + { + Score.Beatmap.OnlineBeatmapID = 0; + Score.Beatmap.Status = BeatmapSetOnlineStatus.Pending; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + RetryOverlay = InternalChildren.OfType().SingleOrDefault(); + } + } } } From b174daa94a5f7968ac9ef31b50257ecf6a96698d Mon Sep 17 00:00:00 2001 From: Shane Woolcock Date: Wed, 3 Jun 2020 11:58:56 +0930 Subject: [PATCH 1006/1117] Remove unused using --- osu.Game/Screens/Ranking/ResultsScreen.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index aff0540652..fbb9b95478 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -10,7 +10,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Screens; -using osu.Game.Beatmaps; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; From 13622eff1f12e8338f96ff5005ccbc2a9e12b8f6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 3 Jun 2020 12:54:07 +0900 Subject: [PATCH 1007/1117] Fix response value --- .../Multiplayer/TestSceneTimeshiftResultsScreen.cs | 2 +- .../Online/API/Requests/GetRoomPlaylistScoresRequest.cs | 9 ++++++++- osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs index d87a2e3408..8559e7e2f4 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs @@ -66,7 +66,7 @@ namespace osu.Game.Tests.Visual.Multiplayer switch (request) { case GetRoomPlaylistScoresRequest r: - r.TriggerSuccess(roomScores); + r.TriggerSuccess(new RoomPlaylistScores { Scores = roomScores }); break; } }); diff --git a/osu.Game/Online/API/Requests/GetRoomPlaylistScoresRequest.cs b/osu.Game/Online/API/Requests/GetRoomPlaylistScoresRequest.cs index dd7f80fd46..38f852870b 100644 --- a/osu.Game/Online/API/Requests/GetRoomPlaylistScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetRoomPlaylistScoresRequest.cs @@ -2,10 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using Newtonsoft.Json; namespace osu.Game.Online.API.Requests { - public class GetRoomPlaylistScoresRequest : APIRequest> + public class GetRoomPlaylistScoresRequest : APIRequest { private readonly int roomId; private readonly int playlistItemId; @@ -18,4 +19,10 @@ namespace osu.Game.Online.API.Requests protected override string Target => $@"rooms/{roomId}/playlist/{playlistItemId}/scores"; } + + public class RoomPlaylistScores + { + [JsonProperty("scores")] + public List Scores { get; set; } + } } diff --git a/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs index f2afe15d35..d95cee2ab8 100644 --- a/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs +++ b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs @@ -27,7 +27,7 @@ namespace osu.Game.Screens.Multi.Ranking protected override APIRequest FetchScores(Action> scoresCallback) { var req = new GetRoomPlaylistScoresRequest(roomId, playlistItem.ID); - req.Success += r => scoresCallback?.Invoke(r.Where(s => s.ID != Score?.OnlineScoreID).Select(s => s.CreateScoreInfo(playlistItem))); + req.Success += r => scoresCallback?.Invoke(r.Scores.Where(s => s.ID != Score?.OnlineScoreID).Select(s => s.CreateScoreInfo(playlistItem))); return req; } } From 22f4e9012c58e96b8b03f5e9bb4b9639efa83c34 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 3 Jun 2020 12:54:16 +0900 Subject: [PATCH 1008/1117] Remove temporary code --- osu.Game/Screens/Multi/Match/MatchSubScreen.cs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs index 01a90139c3..3609be2dfe 100644 --- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs @@ -13,7 +13,6 @@ using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; -using osu.Game.Online.API.Requests; using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer.GameTypes; using osu.Game.Rulesets.Mods; @@ -212,17 +211,6 @@ namespace osu.Game.Screens.Multi.Match managerAdded = beatmapManager.ItemAdded.GetBoundCopy(); managerAdded.BindValueChanged(beatmapAdded); - - if (roomId.Value != null) - { - var req = new GetRoomPlaylistScoresRequest(roomId.Value.Value, playlist[0].ID); - - req.Success += scores => - { - }; - - api.Queue(req); - } } public override bool OnExiting(IScreen next) From 74875f9b629715a1e5a13e65704af277fb2c8c35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 3 Jun 2020 06:47:10 +0200 Subject: [PATCH 1009/1117] Apply review suggestions & other cleanups --- CONTRIBUTING.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 441521f9ef..331534ad73 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,21 +11,21 @@ These are not "official rules" *per se*, but following them will help everyone d ## I would like to submit an issue! -When it comes to issues, bug reports and feature suggestions are welcomed, although please keep in mind that at any point in time, hundreds of issues are open, which vary in severity and the amount of time needed to address them. As such it's not uncommon for issues to remain unresolved for a long time or even closed outright if they are deemed not important enough to fix in the foreseeable future. Issues that are required to "go live" or otherwise achieve parity with stable are prioritised the most. +Issues, bug reports and feature suggestions are welcomed, though please keep in mind that at any point in time, hundreds of issues are open, which vary in severity and the amount of time needed to address them. As such it's not uncommon for issues to remain unresolved for a long time or even closed outright if they are deemed not important enough to fix in the foreseeable future. Issues that are required to "go live" or otherwise achieve parity with stable are prioritised the most. * **Before submitting an issue, try searching existing issues first.** - For housekeeping purposes, we close issues that overlap with or duplicate other pre-existing issues - you can help us not have to do that by searching existing issues yourself first. The issue search box, as well as the issue tag system, are tools you can use to check if an issue has been reported before. + For housekeeping purposes, we close issues that overlap with or duplicate other pre-existing issues - you can help us not to have to do that by searching existing issues yourself first. The issue search box, as well as the issue tag system, are tools you can use to check if an issue has been reported before. * **When submitting a bug report, please try to include as much detail as possible.** - Bugs are not equal - some of them will be reproducible every time on pretty much all hardware, while others will be hard to track down due to being specific to particular hardware or even somewhat random in nature. As such, providing as much detail as possible when reporting a bug is hugely appreciated. A good starting set of information contains of: + Bugs are not equal - some of them will be reproducible every time on pretty much all hardware, while others will be hard to track down due to being specific to particular hardware or even somewhat random in nature. As such, providing as much detail as possible when reporting a bug is hugely appreciated. A good starting set of information consists of: * the in-game logs, which are located at: * `%AppData%/osu/logs` (on Windows), * `~/.local/share/osu/logs` (on Linux and macOS), * `Android/Data/sh.ppy.osulazer/logs` (on Android), - * on iOS they can be obtained by connecting your device to your desktop and copying the `logs` directory from the app's own document storage using iTunes, + * on iOS they can be obtained by connecting your device to your desktop and [copying the `logs` directory from the app's own document storage using iTunes](https://support.apple.com/en-us/HT201301#copy-to-computer), * your system specifications (including the operating system and platform you are playing on), * a reproduction scenario (list of steps you have performed leading up to the occurrence of the bug), * a video or picture of the bug, if at all possible. From 1992a3db546126f536ddc9974cd5074bff5f4876 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 3 Jun 2020 15:50:00 +0900 Subject: [PATCH 1010/1117] Fix redundant override showing up in build warnings --- osu.Game/Rulesets/Objects/SliderEventGenerator.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Rulesets/Objects/SliderEventGenerator.cs b/osu.Game/Rulesets/Objects/SliderEventGenerator.cs index 6df0041e7a..d8c6da86f9 100644 --- a/osu.Game/Rulesets/Objects/SliderEventGenerator.cs +++ b/osu.Game/Rulesets/Objects/SliderEventGenerator.cs @@ -11,6 +11,7 @@ namespace osu.Game.Rulesets.Objects public static class SliderEventGenerator { [Obsolete("Use the overload with cancellation support instead.")] // can be removed 20201115 + // ReSharper disable once RedundantOverload.Global public static IEnumerable Generate(double startTime, double spanDuration, double velocity, double tickDistance, double totalDistance, int spanCount, double? legacyLastTickOffset) { From 5f1d44a2bef173fc2745e0e1212672db4e3b5d86 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 3 Jun 2020 15:52:47 +0900 Subject: [PATCH 1011/1117] Update inspectcode / CodeFileSanity versions used in CI --- build/InspectCode.cake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/InspectCode.cake b/build/InspectCode.cake index 2e7a1d1b28..c8f4f37c94 100644 --- a/build/InspectCode.cake +++ b/build/InspectCode.cake @@ -1,5 +1,5 @@ -#addin "nuget:?package=CodeFileSanity&version=0.0.33" -#addin "nuget:?package=JetBrains.ReSharper.CommandLineTools&version=2019.3.2" +#addin "nuget:?package=CodeFileSanity&version=0.0.36" +#addin "nuget:?package=JetBrains.ReSharper.CommandLineTools&version=2020.1.3" #tool "nuget:?package=NVika.MSBuild&version=1.0.1" var nVikaToolPath = GetFiles("./tools/NVika.MSBuild.*/tools/NVika.exe").First(); From 3c07defa1a48a4a778d7e21b9fe9e8fc9bd1abe5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 3 Jun 2020 15:57:01 +0900 Subject: [PATCH 1012/1117] Push to main multiplayer screen instead --- osu.Game/Screens/Multi/Match/MatchSubScreen.cs | 8 ++++---- osu.Game/Screens/Multi/Multiplayer.cs | 14 -------------- 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs index 3609be2dfe..b0717d3d28 100644 --- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs @@ -20,6 +20,7 @@ using osu.Game.Screens.Multi.Components; using osu.Game.Screens.Multi.Match.Components; using osu.Game.Screens.Multi.Play; using osu.Game.Screens.Multi.Ranking; +using osu.Game.Screens.Play; using osu.Game.Screens.Select; using Footer = osu.Game.Screens.Multi.Match.Components.Footer; @@ -260,10 +261,10 @@ namespace osu.Game.Screens.Multi.Match { default: case GameTypeTimeshift _: - multiplayer?.Start(() => new TimeshiftPlayer(SelectedItem.Value) + multiplayer?.Push(new PlayerLoader(() => new TimeshiftPlayer(SelectedItem.Value) { Exited = () => leaderboardChatDisplay.RefreshScores() - }); + })); break; } } @@ -271,8 +272,7 @@ namespace osu.Game.Screens.Multi.Match private void showBeatmapResults() { Debug.Assert(roomId.Value != null); - - this.Push(new TimeshiftResultsScreen(null, roomId.Value.Value, SelectedItem.Value, false)); + multiplayer?.Push(new TimeshiftResultsScreen(null, roomId.Value.Value, SelectedItem.Value, false)); } } } diff --git a/osu.Game/Screens/Multi/Multiplayer.cs b/osu.Game/Screens/Multi/Multiplayer.cs index 863a28609b..e724152e08 100644 --- a/osu.Game/Screens/Multi/Multiplayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer.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.Bindables; using osu.Framework.Extensions.Color4Extensions; @@ -24,7 +23,6 @@ using osu.Game.Screens.Multi.Lounge; using osu.Game.Screens.Multi.Lounge.Components; using osu.Game.Screens.Multi.Match; using osu.Game.Screens.Multi.Match.Components; -using osu.Game.Screens.Play; using osuTK; namespace osu.Game.Screens.Multi @@ -197,18 +195,6 @@ namespace osu.Game.Screens.Multi Logger.Log($"Polling adjusted (listing: {roomManager.TimeBetweenListingPolls}, selection: {roomManager.TimeBetweenSelectionPolls})"); } - /// - /// Push a to the main screen stack to begin gameplay. - /// Generally called from a via DI resolution. - /// - public void Start(Func player) - { - if (!this.IsCurrentScreen()) - return; - - this.Push(new PlayerLoader(player)); - } - public void APIStateChanged(IAPIProvider api, APIState state) { if (state != APIState.Online) From f3b514964894fd38c69cff189d59f71b04b2fe82 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 3 Jun 2020 16:48:44 +0900 Subject: [PATCH 1013/1117] Move some suggestions to warnings, resolve issues --- osu.Desktop/Updater/SquirrelUpdateManager.cs | 2 +- osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs | 3 +-- .../Drawables/Connections/FollowPointConnection.cs | 3 +-- osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs | 4 ++-- osu.Game.Tests/Scores/IO/ImportScoreTest.cs | 7 ++----- osu.Game.Tournament.Tests/LadderTestScene.cs | 3 +-- osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs | 3 +-- osu.Game.Tournament/TournamentGameBase.cs | 7 ++----- osu.Game/Beatmaps/BeatmapManager.cs | 3 +-- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 3 +-- osu.Game/Online/Chat/ChannelManager.cs | 9 +++------ osu.Game/Online/Chat/StandAloneChatDisplay.cs | 3 +-- osu.Game/OsuGameBase.cs | 9 ++++----- osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs | 3 +-- osu.Game/Rulesets/Objects/HitObject.cs | 3 +-- osu.Game/Rulesets/UI/FrameStabilityContainer.cs | 3 +-- osu.Game/Scoring/ScoreInfo.cs | 8 ++------ osu.Game/Screens/Edit/Timing/Section.cs | 2 +- .../Screens/Multi/Lounge/Components/FilterControl.cs | 3 +-- osu.Game/Screens/Select/BeatmapCarousel.cs | 5 +---- osu.Game/Users/Drawables/DrawableAvatar.cs | 2 +- osu.sln.DotSettings | 4 ++++ 22 files changed, 34 insertions(+), 58 deletions(-) diff --git a/osu.Desktop/Updater/SquirrelUpdateManager.cs b/osu.Desktop/Updater/SquirrelUpdateManager.cs index ade8460dd7..dd50b05c75 100644 --- a/osu.Desktop/Updater/SquirrelUpdateManager.cs +++ b/osu.Desktop/Updater/SquirrelUpdateManager.cs @@ -48,7 +48,7 @@ namespace osu.Desktop.Updater try { - if (updateManager == null) updateManager = await UpdateManager.GitHubUpdateManager(@"https://github.com/ppy/osu", @"osulazer", null, null, true); + updateManager ??= await UpdateManager.GitHubUpdateManager(@"https://github.com/ppy/osu", @"osulazer", null, null, true); var info = await updateManager.CheckForUpdate(!useDeltaPatching); if (info.ReleasesToApply.Count == 0) diff --git a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs index 5cd2f1f581..918ed77683 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs @@ -35,8 +35,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills { var catchCurrent = (CatchDifficultyHitObject)current; - if (lastPlayerPosition == null) - lastPlayerPosition = catchCurrent.LastNormalizedPosition; + lastPlayerPosition ??= catchCurrent.LastNormalizedPosition; float playerPosition = Math.Clamp( lastPlayerPosition.Value, diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs index 8a0ef22c4a..2c41e6b0e9 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs @@ -135,8 +135,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections fp.Alpha = 0; fp.Scale = new Vector2(1.5f * osuEnd.Scale); - if (firstTransformStartTime == null) - firstTransformStartTime = fadeInTime; + firstTransformStartTime ??= fadeInTime; fp.AnimationStartTime = fadeInTime; diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 5eb11a3264..195fec6278 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -175,7 +175,7 @@ namespace osu.Game.Tests.Beatmaps.IO var breakTemp = TestResources.GetTestBeatmapForImport(); MemoryStream brokenOsu = new MemoryStream(); - MemoryStream brokenOsz = new MemoryStream(File.ReadAllBytes(breakTemp)); + MemoryStream brokenOsz = new MemoryStream(await File.ReadAllBytesAsync(breakTemp)); File.Delete(breakTemp); @@ -522,7 +522,7 @@ namespace osu.Game.Tests.Beatmaps.IO using (var resourceForkFile = File.CreateText(resourceForkFilePath)) { - resourceForkFile.WriteLine("adding content so that it's not empty"); + await resourceForkFile.WriteLineAsync("adding content so that it's not empty"); } try diff --git a/osu.Game.Tests/Scores/IO/ImportScoreTest.cs b/osu.Game.Tests/Scores/IO/ImportScoreTest.cs index 90bf419644..57f0d7e957 100644 --- a/osu.Game.Tests/Scores/IO/ImportScoreTest.cs +++ b/osu.Game.Tests/Scores/IO/ImportScoreTest.cs @@ -183,11 +183,8 @@ namespace osu.Game.Tests.Scores.IO { var beatmapManager = osu.Dependencies.Get(); - if (score.Beatmap == null) - score.Beatmap = beatmapManager.GetAllUsableBeatmapSets().First().Beatmaps.First(); - - if (score.Ruleset == null) - score.Ruleset = new OsuRuleset().RulesetInfo; + score.Beatmap ??= beatmapManager.GetAllUsableBeatmapSets().First().Beatmaps.First(); + score.Ruleset ??= new OsuRuleset().RulesetInfo; var scoreManager = osu.Dependencies.Get(); await scoreManager.Import(score, archive); diff --git a/osu.Game.Tournament.Tests/LadderTestScene.cs b/osu.Game.Tournament.Tests/LadderTestScene.cs index b962d035ab..2f4373679c 100644 --- a/osu.Game.Tournament.Tests/LadderTestScene.cs +++ b/osu.Game.Tournament.Tests/LadderTestScene.cs @@ -24,8 +24,7 @@ namespace osu.Game.Tournament.Tests [BackgroundDependencyLoader] private void load() { - if (Ladder.Ruleset.Value == null) - Ladder.Ruleset.Value = rulesetStore.AvailableRulesets.First(); + Ladder.Ruleset.Value ??= rulesetStore.AvailableRulesets.First(); Ruleset.BindTo(Ladder.Ruleset); } diff --git a/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs b/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs index 8be66ff98c..e10154b722 100644 --- a/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs +++ b/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs @@ -47,8 +47,7 @@ namespace osu.Game.Tournament.Screens.Drawings this.storage = storage; - if (TeamList == null) - TeamList = new StorageBackedTeamList(storage); + TeamList ??= new StorageBackedTeamList(storage); if (!TeamList.Teams.Any()) { diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index 85db9e61fb..928c6deb3c 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -150,11 +150,8 @@ namespace osu.Game.Tournament ladder = JsonConvert.DeserializeObject(sr.ReadToEnd()); } - if (ladder == null) - ladder = new LadderInfo(); - - if (ladder.Ruleset.Value == null) - ladder.Ruleset.Value = RulesetStore.AvailableRulesets.First(); + ladder ??= new LadderInfo(); + ladder.Ruleset.Value ??= RulesetStore.AvailableRulesets.First(); Ruleset.BindTo(ladder.Ruleset); diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index f626b45e42..0785f9ef0e 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -239,8 +239,7 @@ namespace osu.Game.Beatmaps if (working == null) { - if (beatmapInfo.Metadata == null) - beatmapInfo.Metadata = beatmapInfo.BeatmapSet.Metadata; + beatmapInfo.Metadata ??= beatmapInfo.BeatmapSet.Metadata; workingCache.Add(working = new BeatmapManagerWorkingBeatmap(Files.Store, new LargeTextureStore(host?.CreateTextureLoaderStore(Files.Store)), beatmapInfo, audioManager)); diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 388abf4648..be5cd78dc8 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -425,8 +425,7 @@ namespace osu.Game.Beatmaps.Formats private void handleHitObject(string line) { // If the ruleset wasn't specified, assume the osu!standard ruleset. - if (parser == null) - parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser(getOffsetTime(), FormatVersion); + parser ??= new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser(getOffsetTime(), FormatVersion); var obj = parser.Parse(line); if (obj != null) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 53872ddcba..90d4c2a03b 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -114,8 +114,7 @@ namespace osu.Game.Online.Chat /// An optional target channel. If null, will be used. public void PostMessage(string text, bool isAction = false, Channel target = null) { - if (target == null) - target = CurrentChannel.Value; + target ??= CurrentChannel.Value; if (target == null) return; @@ -198,8 +197,7 @@ namespace osu.Game.Online.Chat /// An optional target channel. If null, will be used. public void PostCommand(string text, Channel target = null) { - if (target == null) - target = CurrentChannel.Value; + target ??= CurrentChannel.Value; if (target == null) return; @@ -378,8 +376,7 @@ namespace osu.Game.Online.Chat } } - if (CurrentChannel.Value == null) - CurrentChannel.Value = channel; + CurrentChannel.Value ??= channel; return channel; } diff --git a/osu.Game/Online/Chat/StandAloneChatDisplay.cs b/osu.Game/Online/Chat/StandAloneChatDisplay.cs index 4fbeac1db9..f8810c778f 100644 --- a/osu.Game/Online/Chat/StandAloneChatDisplay.cs +++ b/osu.Game/Online/Chat/StandAloneChatDisplay.cs @@ -73,8 +73,7 @@ namespace osu.Game.Online.Chat [BackgroundDependencyLoader(true)] private void load(ChannelManager manager) { - if (ChannelManager == null) - ChannelManager = manager; + ChannelManager ??= manager; } protected virtual StandAloneDrawableChannel CreateDrawableChannel(Channel channel) => diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 5e44562144..3e7311092e 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -164,7 +164,7 @@ namespace osu.Game dependencies.Cache(SkinManager = new SkinManager(Storage, contextFactory, Host, Audio, new NamespacedResourceStore(Resources, "Skins/Legacy"))); dependencies.CacheAs(SkinManager); - if (API == null) API = new APIAccess(LocalConfig); + API ??= new APIAccess(LocalConfig); dependencies.CacheAs(API); @@ -311,11 +311,10 @@ namespace osu.Game { base.SetHost(host); - if (Storage == null) // may be non-null for certain tests - Storage = new OsuStorage(host); + // may be non-null for certain tests + Storage ??= new OsuStorage(host); - if (LocalConfig == null) - LocalConfig = new OsuConfigManager(Storage); + LocalConfig ??= new OsuConfigManager(Storage); } private readonly List fileImporters = new List(); diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs b/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs index a72f182450..cb6abb7cc6 100644 --- a/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs +++ b/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs @@ -68,8 +68,7 @@ namespace osu.Game.Overlays.Chat.Tabs if (!Items.Contains(channel)) AddItem(channel); - if (Current.Value == null) - Current.Value = channel; + Current.Value ??= channel; } /// diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs index e2cc98813a..1d60b266e3 100644 --- a/osu.Game/Rulesets/Objects/HitObject.cs +++ b/osu.Game/Rulesets/Objects/HitObject.cs @@ -133,8 +133,7 @@ namespace osu.Game.Rulesets.Objects { Kiai = controlPointInfo.EffectPointAt(StartTime + control_point_leniency).KiaiMode; - if (HitWindows == null) - HitWindows = CreateHitWindows(); + HitWindows ??= CreateHitWindows(); HitWindows?.SetDifficulty(difficulty.OverallDifficulty); } diff --git a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs index bc9401a095..d574991fa0 100644 --- a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs +++ b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs @@ -181,8 +181,7 @@ namespace osu.Game.Rulesets.UI private void setClock() { // in case a parent gameplay clock isn't available, just use the parent clock. - if (parentGameplayClock == null) - parentGameplayClock = Clock; + parentGameplayClock ??= Clock; Clock = GameplayClock; ProcessCustomClock = false; diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index a40f436a6e..7b37c267bc 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -115,9 +115,7 @@ namespace osu.Game.Scoring get => User?.Username; set { - if (User == null) - User = new User(); - + User ??= new User(); User.Username = value; } } @@ -129,9 +127,7 @@ namespace osu.Game.Scoring get => User?.Id ?? 1; set { - if (User == null) - User = new User(); - + User ??= new User(); User.Id = value ?? 1; } } diff --git a/osu.Game/Screens/Edit/Timing/Section.cs b/osu.Game/Screens/Edit/Timing/Section.cs index ccf1582486..603fb77f31 100644 --- a/osu.Game/Screens/Edit/Timing/Section.cs +++ b/osu.Game/Screens/Edit/Timing/Section.cs @@ -57,7 +57,7 @@ namespace osu.Game.Screens.Edit.Timing { checkbox = new OsuCheckbox { - LabelText = typeof(T).Name.Replace(typeof(ControlPoint).Name, string.Empty) + LabelText = typeof(T).Name.Replace(nameof(Beatmaps.ControlPoints.ControlPoint), string.Empty) } } }, diff --git a/osu.Game/Screens/Multi/Lounge/Components/FilterControl.cs b/osu.Game/Screens/Multi/Lounge/Components/FilterControl.cs index 300418441e..2742ef3404 100644 --- a/osu.Game/Screens/Multi/Lounge/Components/FilterControl.cs +++ b/osu.Game/Screens/Multi/Lounge/Components/FilterControl.cs @@ -34,8 +34,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components [BackgroundDependencyLoader] private void load() { - if (filter == null) - filter = new Bindable(); + filter ??= new Bindable(); } protected override void LoadComplete() diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 2d714d1794..e174c46610 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -607,10 +607,7 @@ namespace osu.Game.Screens.Select // todo: remove the need for this. foreach (var b in beatmapSet.Beatmaps) - { - if (b.Metadata == null) - b.Metadata = beatmapSet.Metadata; - } + b.Metadata ??= beatmapSet.Metadata; var set = new CarouselBeatmapSet(beatmapSet) { diff --git a/osu.Game/Users/Drawables/DrawableAvatar.cs b/osu.Game/Users/Drawables/DrawableAvatar.cs index 09750c5bfe..42d2dbb1c6 100644 --- a/osu.Game/Users/Drawables/DrawableAvatar.cs +++ b/osu.Game/Users/Drawables/DrawableAvatar.cs @@ -43,7 +43,7 @@ namespace osu.Game.Users.Drawables Texture texture = null; if (user != null && user.Id > 1) texture = textures.Get($@"https://a.ppy.sh/{user.Id}"); - if (texture == null) texture = textures.Get(@"Online/avatar-guest"); + texture ??= textures.Get(@"Online/avatar-guest"); ClickableArea clickableArea; Add(clickableArea = new ClickableArea diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index b9fc3de734..6e8ecb42d6 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -60,6 +60,7 @@ WARNING WARNING WARNING + WARNING WARNING WARNING WARNING @@ -105,6 +106,8 @@ HINT WARNING WARNING + WARNING + WARNING WARNING WARNING WARNING @@ -222,6 +225,7 @@ WARNING WARNING WARNING + WARNING True WARNING From 8aa8d2c88011adafa5b84ac0adaa7ff88eddda35 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 3 Jun 2020 16:59:37 +0900 Subject: [PATCH 1014/1117] Resolve NREs --- osu.Desktop/OsuGameDesktop.cs | 2 +- osu.Game.Tests/Chat/MessageFormatterTests.cs | 10 +++++----- .../Visual/UserInterface/TestSceneOsuIcon.cs | 2 +- .../Converters/TypedListConverter.cs | 18 +++++++++++++++--- osu.Game/Online/API/APIAccess.cs | 2 +- 5 files changed, 23 insertions(+), 11 deletions(-) diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 9351e17419..5f74883803 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -59,7 +59,7 @@ namespace osu.Desktop try { using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu")) - stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty).ToString().Split('"')[1].Replace("osu!.exe", ""); + stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty).ToString()?.Split('"')[1].Replace("osu!.exe", ""); if (checkExists(stableInstallPath)) return stableInstallPath; diff --git a/osu.Game.Tests/Chat/MessageFormatterTests.cs b/osu.Game.Tests/Chat/MessageFormatterTests.cs index fbb0416c45..d1a859c84b 100644 --- a/osu.Game.Tests/Chat/MessageFormatterTests.cs +++ b/osu.Game.Tests/Chat/MessageFormatterTests.cs @@ -428,23 +428,23 @@ namespace osu.Game.Tests.Chat Assert.AreEqual(5, result.Links.Count); Link f = result.Links.Find(l => l.Url == "https://osu.ppy.sh/wiki/wiki links"); - Assert.AreEqual(44, f.Index); + Assert.AreEqual(44, f!.Index); Assert.AreEqual(10, f.Length); f = result.Links.Find(l => l.Url == "http://www.simple-test.com"); - Assert.AreEqual(10, f.Index); + Assert.AreEqual(10, f!.Index); Assert.AreEqual(11, f.Length); f = result.Links.Find(l => l.Url == "http://google.com"); - Assert.AreEqual(97, f.Index); + Assert.AreEqual(97, f!.Index); Assert.AreEqual(4, f.Length); f = result.Links.Find(l => l.Url == "https://osu.ppy.sh"); - Assert.AreEqual(78, f.Index); + Assert.AreEqual(78, f!.Index); Assert.AreEqual(18, f.Length); f = result.Links.Find(l => l.Url == "\uD83D\uDE12"); - Assert.AreEqual(101, f.Index); + Assert.AreEqual(101, f!.Index); Assert.AreEqual(3, f.Length); } diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuIcon.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuIcon.cs index 061039b297..246eb119e8 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuIcon.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuIcon.cs @@ -45,7 +45,7 @@ namespace osu.Game.Tests.Visual.UserInterface }); foreach (var p in typeof(OsuIcon).GetProperties(BindingFlags.Public | BindingFlags.Static)) - flow.Add(new Icon($"{nameof(OsuIcon)}.{p.Name}", (IconUsage)p.GetValue(null))); + flow.Add(new Icon($"{nameof(OsuIcon)}.{p.Name}", (IconUsage)p.GetValue(null)!)); AddStep("toggle shadows", () => flow.Children.ForEach(i => i.SpriteIcon.Shadow = !i.SpriteIcon.Shadow)); AddStep("change icons", () => flow.Children.ForEach(i => i.SpriteIcon.Icon = new IconUsage((char)(i.SpriteIcon.Icon.Icon + 1)))); diff --git a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs index f98fa05821..ddfdf08f52 100644 --- a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs +++ b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs @@ -41,13 +41,25 @@ namespace osu.Game.IO.Serialization.Converters var list = new List(); var obj = JObject.Load(reader); - var lookupTable = serializer.Deserialize>(obj["$lookup_table"].CreateReader()); - foreach (var tok in obj["$items"]) + if (!obj.TryGetValue("$lookup_table", out var lookupTableToken) || lookupTableToken == null) + return list; + + var lookupTable = serializer.Deserialize>(lookupTableToken.CreateReader()); + if (lookupTable == null) + return list; + + if (!obj.TryGetValue("$items", out var itemsToken) || itemsToken == null) + return list; + + foreach (var tok in itemsToken) { var itemReader = tok.CreateReader(); - var typeName = lookupTable[(int)tok["$type"]]; + if (!obj.TryGetValue("$type", out var typeToken) || typeToken == null) + throw new JsonException("Expected $type token."); + + var typeName = lookupTable[(int)typeToken]; var instance = (T)Activator.CreateInstance(Type.GetType(typeName)); serializer.Populate(itemReader, instance); diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 4945f7f185..f9e2da9af8 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -250,7 +250,7 @@ namespace osu.Game.Online.API { try { - return JObject.Parse(req.GetResponseString()).SelectToken("form_error", true).ToObject(); + return JObject.Parse(req.GetResponseString()).SelectToken("form_error", true)!.ToObject(); } catch { From 1ba3f0ac14dd2d293bf453972f26ebc3db98c544 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 3 Jun 2020 17:31:55 +0900 Subject: [PATCH 1015/1117] Fix chat history not being loaded for multiplayer matches --- osu.Game/Online/Chat/ChannelManager.cs | 51 ++++++++++++++------------ 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 53872ddcba..6812052eeb 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -93,12 +93,6 @@ namespace osu.Game.Online.Chat { if (!(e.NewValue is ChannelSelectorTabItem.ChannelSelectorTabChannel)) JoinChannel(e.NewValue); - - if (e.NewValue?.MessagesLoaded == false) - { - // let's fetch a small number of messages to bring us up-to-date with the backlog. - fetchInitalMessages(e.NewValue); - } } /// @@ -240,7 +234,6 @@ namespace osu.Game.Online.Chat } JoinChannel(channel); - CurrentChannel.Value = channel; break; case "help": @@ -275,7 +268,7 @@ namespace osu.Game.Online.Chat // join any channels classified as "defaults" if (joinDefaults && defaultChannels.Any(c => c.Equals(channel.Name, StringComparison.OrdinalIgnoreCase))) - JoinChannel(ch); + joinChannel(ch); } }; req.Failure += error => @@ -296,7 +289,7 @@ namespace osu.Game.Online.Chat /// The channel private void fetchInitalMessages(Channel channel) { - if (channel.Id <= 0) return; + if (channel.Id <= 0 || channel.MessagesLoaded) return; var fetchInitialMsgReq = new GetMessagesRequest(channel); fetchInitialMsgReq.Success += messages => @@ -351,9 +344,10 @@ namespace osu.Game.Online.Chat /// Joins a channel if it has not already been joined. /// /// The channel to join. - /// Whether the channel has already been joined server-side. Will skip a join request. /// The joined channel. Note that this may not match the parameter channel as it is a backed object. - public Channel JoinChannel(Channel channel, bool alreadyJoined = false) + public Channel JoinChannel(Channel channel) => joinChannel(channel, true); + + private Channel joinChannel(Channel channel, bool fetchInitialMessages = false) { if (channel == null) return null; @@ -362,21 +356,29 @@ namespace osu.Game.Online.Chat // ensure we are joined to the channel if (!channel.Joined.Value) { - if (alreadyJoined) - channel.Joined.Value = true; - else + switch (channel.Type) { - switch (channel.Type) - { - case ChannelType.Public: - var req = new JoinChannelRequest(channel, api.LocalUser.Value); - req.Success += () => JoinChannel(channel, true); - req.Failure += ex => LeaveChannel(channel); - api.Queue(req); - return channel; - } + case ChannelType.Private: + // can't do this yet. + break; + + default: + var req = new JoinChannelRequest(channel, api.LocalUser.Value); + req.Success += () => + { + channel.Joined.Value = true; + joinChannel(channel, fetchInitialMessages); + }; + req.Failure += ex => LeaveChannel(channel); + api.Queue(req); + return channel; } } + else + { + if (fetchInitialMessages) + fetchInitalMessages(channel); + } if (CurrentChannel.Value == null) CurrentChannel.Value = channel; @@ -420,7 +422,8 @@ namespace osu.Game.Online.Chat foreach (var channel in updates.Presence) { // we received this from the server so should mark the channel already joined. - JoinChannel(channel, true); + channel.Joined.Value = true; + joinChannel(channel); } //todo: handle left channels From 092f5b6521c1617d363d9c953a01438e4b38607e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 3 Jun 2020 17:41:05 +0900 Subject: [PATCH 1016/1117] Fix incorrect reference + simplify --- .../Serialization/Converters/TypedListConverter.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs index ddfdf08f52..50b28ea74b 100644 --- a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs +++ b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs @@ -42,24 +42,24 @@ namespace osu.Game.IO.Serialization.Converters var obj = JObject.Load(reader); - if (!obj.TryGetValue("$lookup_table", out var lookupTableToken) || lookupTableToken == null) + if (obj["$lookup_table"] == null) return list; - var lookupTable = serializer.Deserialize>(lookupTableToken.CreateReader()); + var lookupTable = serializer.Deserialize>(obj["$lookup_table"].CreateReader()); if (lookupTable == null) return list; - if (!obj.TryGetValue("$items", out var itemsToken) || itemsToken == null) + if (obj["$items"] == null) return list; - foreach (var tok in itemsToken) + foreach (var tok in obj["$items"]) { var itemReader = tok.CreateReader(); - if (!obj.TryGetValue("$type", out var typeToken) || typeToken == null) + if (tok["$type"] == null) throw new JsonException("Expected $type token."); - var typeName = lookupTable[(int)typeToken]; + var typeName = lookupTable[(int)tok["$type"]]; var instance = (T)Activator.CreateInstance(Type.GetType(typeName)); serializer.Populate(itemReader, instance); From c0881e14ab176ac500e9308607b37f1368ac8d78 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 3 Jun 2020 17:44:14 +0900 Subject: [PATCH 1017/1117] Add "struct can be made readonly" inspection --- osu.sln.DotSettings | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index 6e8ecb42d6..85be2077be 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -199,6 +199,7 @@ WARNING WARNING HINT + WARNING DO_NOT_SHOW DO_NOT_SHOW DO_NOT_SHOW From 3c7e5a5b42832d2d3b801b6baf09481b47a583e6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 3 Jun 2020 18:00:31 +0900 Subject: [PATCH 1018/1117] Fix ChannelManager not being loaded in tests --- osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index 05b33e4386..0025a26baf 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -246,7 +246,12 @@ namespace osu.Game.Tests.Visual.Online { ((BindableList)ChannelManager.AvailableChannels).AddRange(channels); - Child = ChatOverlay = new TestChatOverlay { RelativeSizeAxes = Axes.Both, }; + InternalChildren = new Drawable[] + { + ChannelManager, + ChatOverlay = new TestChatOverlay { RelativeSizeAxes = Axes.Both, }, + }; + ChatOverlay.Show(); } } From c155ab83399a51bdab44a48d5805463c8520318a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 3 Jun 2020 18:03:10 +0900 Subject: [PATCH 1019/1117] Check filenames and timestamps before reusing an already imported model --- osu.Game/Beatmaps/BeatmapManager.cs | 4 ++-- osu.Game/Database/ArchiveModelManager.cs | 22 +++++++++++++++++++--- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index f626b45e42..e7cef13c68 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -258,9 +258,9 @@ namespace osu.Game.Beatmaps /// The first result for the provided query, or null if no results were found. public BeatmapSetInfo QueryBeatmapSet(Expression> query) => beatmaps.ConsumableItems.AsNoTracking().FirstOrDefault(query); - protected override bool CanUndelete(BeatmapSetInfo existing, BeatmapSetInfo import) + protected override bool CanReuseExisting(BeatmapSetInfo existing, BeatmapSetInfo import) { - if (!base.CanUndelete(existing, import)) + if (!base.CanReuseExisting(existing, import)) return false; var existingIds = existing.Beatmaps.Select(b => b.OnlineBeatmapID).OrderBy(i => i); diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index ae55a7b14a..4d7d3e96e6 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -332,7 +332,7 @@ namespace osu.Game.Database if (existing != null) { - if (CanUndelete(existing, item)) + if (CanReuseExisting(existing, item)) { Undelete(existing); LogForModel(item, $"Found existing {HumanisedModelName} for {item} (ID {existing.ID}) – skipping import."); @@ -660,13 +660,29 @@ namespace osu.Game.Database protected TModel CheckForExisting(TModel model) => model.Hash == null ? null : ModelStore.ConsumableItems.FirstOrDefault(b => b.Hash == model.Hash); /// - /// After an existing is found during an import process, the default behaviour is to restore the existing + /// After an existing is found during an import process, the default behaviour is to use/restore the existing /// item and skip the import. This method allows changing that behaviour. /// /// The existing model. /// The newly imported model. /// Whether the existing model should be restored and used. Returning false will delete the existing and force a re-import. - protected virtual bool CanUndelete(TModel existing, TModel import) => true; + protected virtual bool CanReuseExisting(TModel existing, TModel import) => + getFilenames(existing.Files).SequenceEqual(getFilenames(import.Files)) && + // poor-man's (cheap) equality comparison, avoiding hashing unnecessarily. + // can switch to full hash checks on a per-case basis (or for all) if we decide this is not a performance issue. + getTimestamps(existing.Files).SequenceEqual(getTimestamps(import.Files)); + + private IEnumerable getFilenames(List files) + { + foreach (var f in files.OrderBy(f => f.Filename)) + yield return f.Filename; + } + + private IEnumerable getTimestamps(List files) + { + foreach (var f in files.OrderBy(f => f.Filename)) + yield return File.GetLastWriteTimeUtc(Files.Storage.GetFullPath(f.FileInfo.StoragePath)).ToFileTime(); + } private DbSet queryModel() => ContextFactory.Get().Set(); From 012933545eccd4510dc3c0a70b040610dad0bf8d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 3 Jun 2020 18:30:27 +0900 Subject: [PATCH 1020/1117] Add test coverage --- .../Beatmaps/IO/ImportBeatmapTest.cs | 161 ++++++++++++++++++ osu.Game/Database/ArchiveModelManager.cs | 2 +- 2 files changed, 162 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 5eb11a3264..12c9c92e90 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -12,6 +12,7 @@ using NUnit.Framework; using osu.Framework.Platform; using osu.Game.IPC; using osu.Framework.Allocation; +using osu.Framework.Extensions; using osu.Framework.Logging; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Formats; @@ -22,6 +23,7 @@ using SharpCompress.Archives; using SharpCompress.Archives.Zip; using SharpCompress.Common; using SharpCompress.Writers.Zip; +using FileInfo = System.IO.FileInfo; namespace osu.Game.Tests.Beatmaps.IO { @@ -93,6 +95,165 @@ namespace osu.Game.Tests.Beatmaps.IO } } + [Test] + public async Task TestImportThenImportWithReZip() + { + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportThenImportWithNewerTimestamp))) + { + try + { + var osu = loadOsu(host); + + var temp = TestResources.GetTestBeatmapForImport(); + + string extractedFolder = $"{temp}_extracted"; + Directory.CreateDirectory(extractedFolder); + + try + { + var imported = await LoadOszIntoOsu(osu); + + string hashBefore = hashFile(temp); + + using (var zip = ZipArchive.Open(temp)) + zip.WriteToDirectory(extractedFolder); + + using (var zip = ZipArchive.Create()) + { + zip.AddAllFromDirectory(extractedFolder); + zip.SaveTo(temp, new ZipWriterOptions(CompressionType.Deflate)); + } + + // zip files differ because different compression or encoder. + Assert.AreNotEqual(hashBefore, hashFile(temp)); + + var importedSecondTime = await osu.Dependencies.Get().Import(temp); + + ensureLoaded(osu); + + // but contents doesn't, so existing should still be used. + Assert.IsTrue(imported.ID == importedSecondTime.ID); + Assert.IsTrue(imported.Beatmaps.First().ID == importedSecondTime.Beatmaps.First().ID); + } + finally + { + Directory.Delete(extractedFolder, true); + } + } + finally + { + host.Exit(); + } + } + } + + private string hashFile(string filename) + { + using (var s = File.OpenRead(filename)) + return s.ComputeMD5Hash(); + } + + [Test] + public async Task TestImportThenImportWithNewerTimestamp() + { + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportThenImportWithNewerTimestamp))) + { + try + { + var osu = loadOsu(host); + + var temp = TestResources.GetTestBeatmapForImport(); + + string extractedFolder = $"{temp}_extracted"; + Directory.CreateDirectory(extractedFolder); + + try + { + var imported = await LoadOszIntoOsu(osu); + + using (var zip = ZipArchive.Open(temp)) + zip.WriteToDirectory(extractedFolder); + + // change timestamp + new FileInfo(Directory.GetFiles(extractedFolder).First()).LastWriteTime = DateTime.Now; + + using (var zip = ZipArchive.Create()) + { + zip.AddAllFromDirectory(extractedFolder); + zip.SaveTo(temp, new ZipWriterOptions(CompressionType.Deflate)); + } + + var importedSecondTime = await osu.Dependencies.Get().Import(temp); + + ensureLoaded(osu); + + // check the newly "imported" beatmap is not the original. + Assert.IsTrue(imported.ID != importedSecondTime.ID); + Assert.IsTrue(imported.Beatmaps.First().ID != importedSecondTime.Beatmaps.First().ID); + } + finally + { + Directory.Delete(extractedFolder, true); + } + } + finally + { + host.Exit(); + } + } + } + + [Test] + public async Task TestImportThenImportWithDifferentFilename() + { + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportThenImportWithDifferentFilename))) + { + try + { + var osu = loadOsu(host); + + var temp = TestResources.GetTestBeatmapForImport(); + + string extractedFolder = $"{temp}_extracted"; + Directory.CreateDirectory(extractedFolder); + + try + { + var imported = await LoadOszIntoOsu(osu); + + using (var zip = ZipArchive.Open(temp)) + zip.WriteToDirectory(extractedFolder); + + // change filename + var firstFile = new FileInfo(Directory.GetFiles(extractedFolder).First()); + firstFile.MoveTo(Path.Combine(firstFile.DirectoryName, $"{firstFile.Name}-changed{firstFile.Extension}")); + + using (var zip = ZipArchive.Create()) + { + zip.AddAllFromDirectory(extractedFolder); + zip.SaveTo(temp, new ZipWriterOptions(CompressionType.Deflate)); + } + + var importedSecondTime = await osu.Dependencies.Get().Import(temp); + + ensureLoaded(osu); + + // check the newly "imported" beatmap is not the original. + Assert.IsTrue(imported.ID != importedSecondTime.ID); + Assert.IsTrue(imported.Beatmaps.First().ID != importedSecondTime.Beatmaps.First().ID); + } + finally + { + Directory.Delete(extractedFolder, true); + } + } + finally + { + host.Exit(); + } + } + } + [Test] public async Task TestImportCorruptThenImport() { diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 4d7d3e96e6..5ca9423de2 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -276,7 +276,7 @@ namespace osu.Game.Database // for now, concatenate all .osu files in the set to create a unique hash. MemoryStream hashable = new MemoryStream(); - foreach (TFileModel file in item.Files.Where(f => HashableFileTypes.Any(f.Filename.EndsWith))) + foreach (TFileModel file in item.Files.Where(f => HashableFileTypes.Any(f.Filename.EndsWith)).OrderBy(f => f.Filename)) { using (Stream s = Files.Store.GetStream(file.FileInfo.StoragePath)) s.CopyTo(hashable); From 6133c7d74784d0f848add452953ebf56aa39c0a2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 3 Jun 2020 18:51:02 +0900 Subject: [PATCH 1021/1117] Change suggestion to warning --- osu.sln.DotSettings | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index 85be2077be..85d5fce29a 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -141,6 +141,7 @@ WARNING WARNING WARNING + WARNING WARNING WARNING WARNING From 25160dc220d9f2f0bde4125f0bafd7446e3ad354 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 3 Jun 2020 19:15:52 +0900 Subject: [PATCH 1022/1117] Fix test name --- osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 12c9c92e90..12f06059f7 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -98,7 +98,7 @@ namespace osu.Game.Tests.Beatmaps.IO [Test] public async Task TestImportThenImportWithReZip() { - using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportThenImportWithNewerTimestamp))) + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportThenImportWithReZip))) { try { From f6d9f0597b970c9411c623392ffea35a8bcc0fe4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 3 Jun 2020 21:28:29 +0900 Subject: [PATCH 1023/1117] Add implicit join logic for multiplayer rooms --- osu.Game/Online/Chat/ChannelManager.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 6812052eeb..b17e0812da 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -358,6 +358,13 @@ namespace osu.Game.Online.Chat { switch (channel.Type) { + case ChannelType.Multiplayer: + // join is implicit. happens when you join a multiplayer game. + // this will probably change in the future. + channel.Joined.Value = true; + joinChannel(channel, fetchInitialMessages); + return channel; + case ChannelType.Private: // can't do this yet. break; From 5ed3cd205f068d572caddc0ae01aa7ab8a580a6a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 3 Jun 2020 22:35:01 +0900 Subject: [PATCH 1024/1117] Simplify reuse check using FileInfo IDs --- .../Beatmaps/IO/ImportBeatmapTest.cs | 9 +++++---- osu.Game/Database/ArchiveModelManager.cs | 20 +++++++++---------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 12f06059f7..9b34eece5f 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -154,9 +154,9 @@ namespace osu.Game.Tests.Beatmaps.IO } [Test] - public async Task TestImportThenImportWithNewerTimestamp() + public async Task TestImportThenImportWithChangedFile() { - using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportThenImportWithNewerTimestamp))) + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportThenImportWithChangedFile))) { try { @@ -174,8 +174,9 @@ namespace osu.Game.Tests.Beatmaps.IO using (var zip = ZipArchive.Open(temp)) zip.WriteToDirectory(extractedFolder); - // change timestamp - new FileInfo(Directory.GetFiles(extractedFolder).First()).LastWriteTime = DateTime.Now; + // arbitrary write to non-hashed file + using (var sw = new FileInfo(Directory.GetFiles(extractedFolder, "*.mp3").First()).AppendText()) + sw.WriteLine("text"); using (var zip = ZipArchive.Create()) { diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 5ca9423de2..0fe8dd1268 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -667,10 +667,16 @@ namespace osu.Game.Database /// The newly imported model. /// Whether the existing model should be restored and used. Returning false will delete the existing and force a re-import. protected virtual bool CanReuseExisting(TModel existing, TModel import) => - getFilenames(existing.Files).SequenceEqual(getFilenames(import.Files)) && - // poor-man's (cheap) equality comparison, avoiding hashing unnecessarily. - // can switch to full hash checks on a per-case basis (or for all) if we decide this is not a performance issue. - getTimestamps(existing.Files).SequenceEqual(getTimestamps(import.Files)); + // for the best or worst, we copy and import files of a new import before checking whether + // it is a duplicate. so to check if anything has changed, we can just compare all FileInfo IDs. + getIDs(existing.Files).SequenceEqual(getIDs(import.Files)) && + getFilenames(existing.Files).SequenceEqual(getFilenames(import.Files)); + + private IEnumerable getIDs(List files) + { + foreach (var f in files.OrderBy(f => f.Filename)) + yield return f.FileInfo.ID; + } private IEnumerable getFilenames(List files) { @@ -678,12 +684,6 @@ namespace osu.Game.Database yield return f.Filename; } - private IEnumerable getTimestamps(List files) - { - foreach (var f in files.OrderBy(f => f.Filename)) - yield return File.GetLastWriteTimeUtc(Files.Storage.GetFullPath(f.FileInfo.StoragePath)).ToFileTime(); - } - private DbSet queryModel() => ContextFactory.Get().Set(); protected virtual string HumanisedModelName => $"{typeof(TModel).Name.Replace("Info", "").ToLower()}"; From 66ec2afe5cdcd9eb77adf5015966e5dcb652c1d1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 3 Jun 2020 23:38:40 +0900 Subject: [PATCH 1025/1117] Remove broken import test --- .../Beatmaps/IO/ImportBeatmapTest.cs | 33 +------------------ 1 file changed, 1 insertion(+), 32 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 9b34eece5f..546bf758c1 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.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; @@ -374,37 +374,6 @@ namespace osu.Game.Tests.Beatmaps.IO } } - [Test] - public async Task TestImportThenImportDifferentHash() - { - // unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. - using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportThenImportDifferentHash))) - { - try - { - var osu = loadOsu(host); - var manager = osu.Dependencies.Get(); - - var imported = await LoadOszIntoOsu(osu); - - imported.Hash += "-changed"; - manager.Update(imported); - - var importedSecondTime = await LoadOszIntoOsu(osu); - - Assert.IsTrue(imported.ID != importedSecondTime.ID); - Assert.IsTrue(imported.Beatmaps.First().ID < importedSecondTime.Beatmaps.First().ID); - - // only one beatmap will exist as the online set ID matched, causing purging of the first import. - checkBeatmapSetCount(osu, 1); - } - finally - { - host.Exit(); - } - } - } - [Test] public async Task TestImportThenDeleteThenImport() { From 611f64fd364525be3f98c553ce9501a4c3505291 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 3 Jun 2020 23:23:56 +0300 Subject: [PATCH 1026/1117] Add base ready-made abstract scene for osu! mod tests --- osu.Game.Rulesets.Osu.Tests/Mods/OsuModTestScene.cs | 12 ++++++++++++ .../Mods/TestSceneOsuModDifficultyAdjust.cs | 5 +---- .../Mods/TestSceneOsuModDoubleTime.cs | 5 +---- .../Mods/TestSceneOsuModHidden.cs | 5 +---- 4 files changed, 15 insertions(+), 12 deletions(-) create mode 100644 osu.Game.Rulesets.Osu.Tests/Mods/OsuModTestScene.cs diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/OsuModTestScene.cs b/osu.Game.Rulesets.Osu.Tests/Mods/OsuModTestScene.cs new file mode 100644 index 0000000000..7697f46160 --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/Mods/OsuModTestScene.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.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Osu.Tests.Mods +{ + public class OsuModTestScene : ModTestScene + { + protected override Ruleset CreatePlayerRuleset() => new OsuRuleset(); + } +} diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModDifficultyAdjust.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModDifficultyAdjust.cs index 7c396054f1..49c1fe8540 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModDifficultyAdjust.cs @@ -9,14 +9,11 @@ using osu.Framework.Utils; using osu.Game.Graphics.Containers; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Objects.Drawables; -using osu.Game.Tests.Visual; namespace osu.Game.Rulesets.Osu.Tests.Mods { - public class TestSceneOsuModDifficultyAdjust : ModTestScene + public class TestSceneOsuModDifficultyAdjust : OsuModTestScene { - protected override Ruleset CreatePlayerRuleset() => new OsuRuleset(); - [Test] public void TestNoAdjustment() => CreateModTest(new ModTestData { diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModDoubleTime.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModDoubleTime.cs index 94ef6140e9..335ef31019 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModDoubleTime.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModDoubleTime.cs @@ -4,14 +4,11 @@ using NUnit.Framework; using osu.Framework.Utils; using osu.Game.Rulesets.Osu.Mods; -using osu.Game.Tests.Visual; namespace osu.Game.Rulesets.Osu.Tests.Mods { - public class TestSceneOsuModDoubleTime : ModTestScene + public class TestSceneOsuModDoubleTime : OsuModTestScene { - protected override Ruleset CreatePlayerRuleset() => new OsuRuleset(); - [TestCase(0.5)] [TestCase(1.01)] [TestCase(1.5)] diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs index 8ef2240c66..40f1c4a52f 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs @@ -8,15 +8,12 @@ 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.Tests.Visual; using osuTK; namespace osu.Game.Rulesets.Osu.Tests.Mods { - public class TestSceneOsuModHidden : ModTestScene + public class TestSceneOsuModHidden : OsuModTestScene { - protected override Ruleset CreatePlayerRuleset() => new OsuRuleset(); - [Test] public void TestDefaultBeatmapTest() => CreateModTest(new ModTestData { From 11da045d8cc54111157e76a9f91e6ae93a7b2c3d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 3 Jun 2020 23:43:18 +0300 Subject: [PATCH 1027/1117] Reorder declaration position of ruleset-creation methods Should be recognized as a normal protected method in its declaring class. --- .../TestSceneHyperDash.cs | 1 - osu.Game/Tests/Visual/PlayerTestScene.cs | 17 +++++++------- osu.Game/Tests/Visual/SkinnableTestScene.cs | 23 +++++++++++-------- 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs index 83a6dc3d07..a0dcb86d57 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs @@ -16,7 +16,6 @@ namespace osu.Game.Rulesets.Catch.Tests [TestFixture] public class TestSceneHyperDash : TestSceneCatchPlayer { - protected override bool Autoplay => true; [Test] diff --git a/osu.Game/Tests/Visual/PlayerTestScene.cs b/osu.Game/Tests/Visual/PlayerTestScene.cs index 53abf83e72..d663848bbf 100644 --- a/osu.Game/Tests/Visual/PlayerTestScene.cs +++ b/osu.Game/Tests/Visual/PlayerTestScene.cs @@ -24,15 +24,6 @@ namespace osu.Game.Tests.Visual protected OsuConfigManager LocalConfig; - /// - /// Creates the ruleset for setting up the component. - /// - [NotNull] - protected abstract Ruleset CreatePlayerRuleset(); - - protected sealed override Ruleset CreateRuleset() => CreatePlayerRuleset(); - - [NotNull] private readonly Ruleset ruleset; protected PlayerTestScene() @@ -97,6 +88,14 @@ namespace osu.Game.Tests.Visual LoadScreen(Player); } + /// + /// Creates the ruleset for setting up the component. + /// + [NotNull] + protected abstract Ruleset CreatePlayerRuleset(); + + protected sealed override Ruleset CreateRuleset() => CreatePlayerRuleset(); + protected virtual TestPlayer CreatePlayer(Ruleset ruleset) => new TestPlayer(false, false); } } diff --git a/osu.Game/Tests/Visual/SkinnableTestScene.cs b/osu.Game/Tests/Visual/SkinnableTestScene.cs index 98164031b0..41147d3768 100644 --- a/osu.Game/Tests/Visual/SkinnableTestScene.cs +++ b/osu.Game/Tests/Visual/SkinnableTestScene.cs @@ -23,27 +23,24 @@ namespace osu.Game.Tests.Visual { public abstract class SkinnableTestScene : OsuGridTestScene { + private readonly Ruleset ruleset; + private Skin metricsSkin; private Skin defaultSkin; private Skin specialSkin; private Skin oldSkin; - /// - /// Creates the ruleset for adding the ruleset-specific skin transforming component. - /// - [NotNull] - protected abstract Ruleset CreateRulesetForSkinProvider(); - - protected sealed override Ruleset CreateRuleset() => CreateRulesetForSkinProvider(); - protected SkinnableTestScene() : base(2, 3) { + ruleset = CreateRulesetForSkinProvider(); } [BackgroundDependencyLoader] private void load(AudioManager audio, SkinManager skinManager) { + Ruleset.Value = ruleset.RulesetInfo; + var dllStore = new DllResourceStore(DynamicCompilationOriginal.GetType().Assembly); metricsSkin = new TestLegacySkin(new SkinInfo { Name = "metrics-skin" }, new NamespacedResourceStore(dllStore, "Resources/metrics_skin"), audio, true); @@ -113,7 +110,7 @@ namespace osu.Game.Tests.Visual { new OutlineBox { Alpha = autoSize ? 1 : 0 }, mainProvider.WithChild( - new SkinProvidingContainer(CreateRulesetForSkinProvider().CreateLegacySkinProvider(mainProvider, beatmap)) + new SkinProvidingContainer(ruleset.CreateLegacySkinProvider(mainProvider, beatmap)) { Child = created, RelativeSizeAxes = !autoSize ? Axes.Both : Axes.None, @@ -126,6 +123,14 @@ namespace osu.Game.Tests.Visual }; } + /// + /// Creates the ruleset for adding the corresponding skin transforming component. + /// + [NotNull] + protected abstract Ruleset CreateRulesetForSkinProvider(); + + protected sealed override Ruleset CreateRuleset() => CreateRulesetForSkinProvider(); + protected virtual IBeatmap CreateBeatmapForSkinProvider() => CreateWorkingBeatmap(Ruleset.Value).GetPlayableBeatmap(Ruleset.Value); private class OutlineBox : CompositeDrawable From 136e10086acef397193487e11597623f2867f05b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 4 Jun 2020 00:37:06 +0300 Subject: [PATCH 1028/1117] Set the ruleset bindable value at the BDL for its subclasses usages There are test scenes using current value of ruleset bindable on their BDL (example in TestSceneSliderSnaking's BDL) --- osu.Game/Tests/Visual/PlayerTestScene.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Tests/Visual/PlayerTestScene.cs b/osu.Game/Tests/Visual/PlayerTestScene.cs index d663848bbf..1e267726e0 100644 --- a/osu.Game/Tests/Visual/PlayerTestScene.cs +++ b/osu.Game/Tests/Visual/PlayerTestScene.cs @@ -34,6 +34,8 @@ namespace osu.Game.Tests.Visual [BackgroundDependencyLoader] private void load() { + Ruleset.Value = ruleset.RulesetInfo; + Dependencies.Cache(LocalConfig = new OsuConfigManager(LocalStorage)); LocalConfig.GetBindable(OsuSetting.DimLevel).Value = 1.0; } @@ -67,7 +69,6 @@ namespace osu.Game.Tests.Visual var beatmap = CreateBeatmap(ruleset.RulesetInfo); Beatmap.Value = CreateWorkingBeatmap(beatmap); - Ruleset.Value = ruleset.RulesetInfo; SelectedMods.Value = Array.Empty(); if (!AllowFail) From bbad70c3f0101fed3121bb894f28b3d6884a1322 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 4 Jun 2020 00:40:24 +0300 Subject: [PATCH 1029/1117] Fix mod perfect test scenes failing due to null ruleset provided Just a workaround for now, a better fix may be to put the test data creation in an action that is guaranteed to be invoked after the test scene has fully loaded (all dependencies would've been resolved by then). --- osu.Game/Tests/Visual/ModPerfectTestScene.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Tests/Visual/ModPerfectTestScene.cs b/osu.Game/Tests/Visual/ModPerfectTestScene.cs index bfd540093b..93b38a149c 100644 --- a/osu.Game/Tests/Visual/ModPerfectTestScene.cs +++ b/osu.Game/Tests/Visual/ModPerfectTestScene.cs @@ -22,7 +22,7 @@ namespace osu.Game.Tests.Visual Mod = mod, Beatmap = new Beatmap { - BeatmapInfo = { Ruleset = Ruleset.Value }, + BeatmapInfo = { Ruleset = CreatePlayerRuleset().RulesetInfo }, HitObjects = { testData.HitObject } }, Autoplay = !shouldMiss, From c2fd2b861642a6fcac8412891d0365104c6ed6b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 3 Jun 2020 23:20:43 +0200 Subject: [PATCH 1030/1117] Add notes about draft PRs & pushing --- CONTRIBUTING.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 331534ad73..9666f249e2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -92,6 +92,16 @@ Here are some key things to note before jumping in: As part of continuous integration, we also run code style analysis, which is supposed to make sure that your code is formatted the same way as all the pre-existing code in the repository. The reason we enforce a particular code style everywhere is to make sure the codebase is consistent in that regard - having one whitespace convention in one place and another one elsewhere causes disorganisation. +* **Make sure that the pull request is complete before opening it.** + + Whether it's fixing a bug or implementing new functionality, it's best that you make sure that the change you want to submit as a pull request is as complete as it can be before clicking the *Create pull request* button. Having to track if a pull request is ready for review or not places additional burden on reviewers. + + Draft pull requests are an option, but use them sparingly and within reason. They are best suited to discuss code changes that cannot be easily described in natural language or have a potential large impact on the future direction of the project. When in doubt, don't open drafts unless a maintainer asks you to do so. + +* **Only push code when it's ready.** + + As an extension of the above, when making changes to an already-open PR, please try to only push changes you are reasonably certain of. Pushing after every commit causes the continuous integration build queue to grow in size, slowing down work and taking up time that could be spent verifying other changes. + * **Make sure to keep the *Allow edits from maintainers* check box checked.** To speed up the merging process, collaborators and team members will sometimes want to push changes to your branch themselves, to make minor code style adjustments or to otherwise refactor the code without having to describe how they'd like the code to look like in painstaking detail. Having the *Allow edits from maintainers* check box checked lets them do that; without it they are forced to report issues back to you and wait for you to address them. From ddf5282d0e24d798a157d2c9704f8b30a7944731 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 3 Jun 2020 23:33:49 +0200 Subject: [PATCH 1031/1117] Move items from README.md to contributing guidelines --- CONTRIBUTING.md | 8 +++++++- README.md | 6 ------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9666f249e2..6c327f01b3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -52,7 +52,7 @@ Issues, bug reports and feature suggestions are welcomed, though please keep in ## I would like to submit a pull request! -We also welcome pull requests from unaffiliated contributors. The issue tracker should provide plenty of issues that you can work on; we also mark issues that we think would be good for newcomers with the [`good-first-issue`](https://github.com/ppy/osu/issues?q=is%3Aissue+is%3Aopen+label%3Agood-first-issue) label. +We also welcome pull requests from unaffiliated contributors. The [issue tracker](https://github.com/ppy/osu/issues) should provide plenty of issues that you can work on; we also mark issues that we think would be good for newcomers with the [`good-first-issue`](https://github.com/ppy/osu/issues?q=is%3Aissue+is%3Aopen+label%3Agood-first-issue) label. However, do keep in mind that the core team is committed to bringing osu!lazer up to par with stable first and foremost, so depending on what your contribution concerns, it might not be merged and released right away. Our approach to managing issues and their priorities is described [in the wiki](https://github.com/ppy/osu/wiki/Project-management). @@ -62,12 +62,18 @@ Here are some key things to note before jumping in: While we are accepting of all kinds of contributions, we also have a certain quality standard we'd like to uphold and limited time to review your code. Therefore, we would like to avoid providing entry-level advice, and as such if you're not very familiar with C\# as a programming language, we'd recommend that you start off with a few personal projects to get acquainted with the language's syntax, toolchain and principles of object-oriented programming first. + In addition, please take the time to take a look at and get acquainted with the [development and testing](https://github.com/ppy/osu-framework/wiki/Development-and-Testing) procedure we have set up. + * **Make sure you are familiar with git and the pull request workflow.** [git](https://git-scm.com/) is a distributed version control system that might not be very intuitive at the beginning if you're not familiar with version control. In particular, projects using git have a particular workflow for submitting code changes, which is called the pull request workflow. To make things run more smoothly, we recommend that you look up some online resources to familiarise yourself with the git vocabulary and commands, and practice working with forks and submitting pull requests at your own pace. A high-level overview of the process can be found in [this article by GitHub](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/proposing-changes-to-your-work-with-pull-requests). +* **Double-check designs before starting work on new functionality.** + + When implementing new features, keep in mind that we already have a lot of the UI designed. If you wish to work on something with the intention of having it included in the official distribution, please open an issue for discussion and we will give you what you need from a design perspective to proceed. If you want to make *changes* to the design, we recommend you open an issue with your intentions before spending too much time to ensure no effort is wasted. + * **Make sure to submit pull requests off of a topic branch.** As described in the article linked in the previous point, topic branches help you parallelise your work and separate it from the main `master` branch, and additionally are easier for maintainers to work with. Working with multiple `master` branches across many remotes is difficult to keep track of, and it's easy to make a mistake and push to the wrong `master` branch by accident. diff --git a/README.md b/README.md index 336bf33f7e..9e1cc20c8b 100644 --- a/README.md +++ b/README.md @@ -93,12 +93,6 @@ JetBrains ReSharper InspectCode is also used for wider rule sets. You can run it ## Contributing -We welcome all contributions, but keep in mind that we already have a lot of the UI designed. If you wish to work on something with the intention of having it included in the official distribution, please open an issue for discussion and we will give you what you need from a design perspective to proceed. If you want to make *changes* to the design, we recommend you open an issue with your intentions before spending too much time to ensure no effort is wasted. - -If you're unsure of what you can help with, check out the [list of open issues](https://github.com/ppy/osu/issues) (especially those with the ["good first issue"](https://github.com/ppy/osu/issues?q=is%3Aopen+label%3Agood-first-issue+sort%3Aupdated-desc) label). - -Before starting, please make sure you are familiar with the [development and testing](https://github.com/ppy/osu-framework/wiki/Development-and-Testing) procedure we have set up. New component development, and where possible, bug fixing and debugging existing components **should always be done under VisualTests**. - Note that while we already have certain standards in place, nothing is set in stone. If you have an issue with the way code is structured, with any libraries we are using, or with any processes involved with contributing, *please* bring it up. We welcome all feedback so we can make contributing to this project as painless as possible. For those interested, we love to reward quality contributions via [bounties](https://docs.google.com/spreadsheets/d/1jNXfj_S3Pb5PErA-czDdC9DUu4IgUbe1Lt8E7CYUJuE/view?&rm=minimal#gid=523803337), paid out via PayPal or osu!supporter tags. Don't hesitate to [request a bounty](https://docs.google.com/forms/d/e/1FAIpQLSet_8iFAgPMG526pBZ2Kic6HSh7XPM3fE8xPcnWNkMzINDdYg/viewform) for your work on this project. From af3daaaeafd294a740d9586ee56e13858b75b5e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 3 Jun 2020 23:39:29 +0200 Subject: [PATCH 1032/1117] Add reference to contributing guidelines in README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 9e1cc20c8b..dc3ee63844 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,8 @@ JetBrains ReSharper InspectCode is also used for wider rule sets. You can run it ## Contributing +When it comes to contributing to the project, the two main things you can do to help out are reporting issues and submitting pull requests. Based on past experiences, we have prepared a [list of contributing guidelines](CONTRIBUTING.md) that should hopefully ease you into our collaboration process and answer the most frequently-asked questions. + Note that while we already have certain standards in place, nothing is set in stone. If you have an issue with the way code is structured, with any libraries we are using, or with any processes involved with contributing, *please* bring it up. We welcome all feedback so we can make contributing to this project as painless as possible. For those interested, we love to reward quality contributions via [bounties](https://docs.google.com/spreadsheets/d/1jNXfj_S3Pb5PErA-czDdC9DUu4IgUbe1Lt8E7CYUJuE/view?&rm=minimal#gid=523803337), paid out via PayPal or osu!supporter tags. Don't hesitate to [request a bounty](https://docs.google.com/forms/d/e/1FAIpQLSet_8iFAgPMG526pBZ2Kic6HSh7XPM3fE8xPcnWNkMzINDdYg/viewform) for your work on this project. From c72592c52ce200cd1500d261f349d72caa99dd41 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 4 Jun 2020 00:44:28 +0300 Subject: [PATCH 1033/1117] Remove bindable-disabling logic and don't tie immediately to CreateRuleset() --- osu.Game/Tests/Visual/OsuTestScene.cs | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs index 1b0dff162b..88bd087215 100644 --- a/osu.Game/Tests/Visual/OsuTestScene.cs +++ b/osu.Game/Tests/Visual/OsuTestScene.cs @@ -72,22 +72,7 @@ namespace osu.Game.Tests.Visual Beatmap.SetDefault(); Ruleset = Dependencies.Ruleset; - - var definedRuleset = CreateRuleset()?.RulesetInfo; - - if (definedRuleset != null) - { - // re-enable the bindable in case it was disabled. - // happens when restarting current test scene. - Ruleset.Disabled = false; - - // Set global ruleset bindable to the ruleset defined - // for this test scene and disallow changing it. - Ruleset.Value = definedRuleset; - Ruleset.Disabled = true; - } - else - Ruleset.SetDefault(); + Ruleset.SetDefault(); SelectedMods = Dependencies.Mods; SelectedMods.SetDefault(); @@ -145,7 +130,7 @@ namespace osu.Game.Tests.Visual /// Creates the ruleset to be used for this test scene. /// /// - /// When testing against ruleset-specific components, this method must be overriden to their ruleset. + /// When testing against ruleset-specific components, this method must be overriden to their corresponding ruleset. /// [CanBeNull] protected virtual Ruleset CreateRuleset() => null; From 741fa201492c41b916744315f28593f1e3c57cd9 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 4 Jun 2020 00:47:10 +0300 Subject: [PATCH 1034/1117] Use CreateRuleset() for editor test scenes as well --- .../TestSceneEditor.cs | 3 ++- .../TestSceneEditor.cs | 5 +---- .../TestSceneEditor.cs | 5 +---- .../Editing/TestSceneEditorChangeStates.cs | 8 +++----- osu.Game/Tests/Visual/EditorTestScene.cs | 19 +++++++++++-------- 5 files changed, 18 insertions(+), 22 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneEditor.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneEditor.cs index 7ed886be49..3b9c03b86a 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneEditor.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneEditor.cs @@ -15,8 +15,9 @@ namespace osu.Game.Rulesets.Mania.Tests { private readonly Bindable direction = new Bindable(); + protected override Ruleset CreateEditorRuleset() => new ManiaRuleset(); + public TestSceneEditor() - : base(new ManiaRuleset()) { AddStep("upwards scroll", () => direction.Value = ManiaScrollingDirection.Up); AddStep("downwards scroll", () => direction.Value = ManiaScrollingDirection.Down); diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneEditor.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneEditor.cs index 4aca34bf64..9239034a53 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneEditor.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneEditor.cs @@ -9,9 +9,6 @@ namespace osu.Game.Rulesets.Osu.Tests [TestFixture] public class TestSceneEditor : EditorTestScene { - public TestSceneEditor() - : base(new OsuRuleset()) - { - } + protected override Ruleset CreateEditorRuleset() => new OsuRuleset(); } } diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneEditor.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneEditor.cs index 089a7ad00b..411fe08bcf 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneEditor.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneEditor.cs @@ -9,9 +9,6 @@ namespace osu.Game.Rulesets.Taiko.Tests [TestFixture] public class TestSceneEditor : EditorTestScene { - public TestSceneEditor() - : base(new TaikoRuleset()) - { - } + protected override Ruleset CreateEditorRuleset() => new TaikoRuleset(); } } diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorChangeStates.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorChangeStates.cs index 20862e9cac..293a6e6869 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorChangeStates.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorChangeStates.cs @@ -4,6 +4,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Testing; +using osu.Game.Rulesets; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Objects; @@ -13,13 +14,10 @@ namespace osu.Game.Tests.Visual.Editing { public class TestSceneEditorChangeStates : EditorTestScene { - public TestSceneEditorChangeStates() - : base(new OsuRuleset()) - { - } - private EditorBeatmap editorBeatmap; + protected override Ruleset CreateEditorRuleset() => new OsuRuleset(); + public override void SetUpSteps() { base.SetUpSteps(); diff --git a/osu.Game/Tests/Visual/EditorTestScene.cs b/osu.Game/Tests/Visual/EditorTestScene.cs index 2f6e6fb599..4f9a5b53b8 100644 --- a/osu.Game/Tests/Visual/EditorTestScene.cs +++ b/osu.Game/Tests/Visual/EditorTestScene.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Linq; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Testing; using osu.Game.Rulesets; @@ -15,17 +16,11 @@ namespace osu.Game.Tests.Visual { protected Editor Editor { get; private set; } - private readonly Ruleset ruleset; - - protected EditorTestScene(Ruleset ruleset) - { - this.ruleset = ruleset; - } - [BackgroundDependencyLoader] private void load() { - Beatmap.Value = CreateWorkingBeatmap(ruleset.RulesetInfo); + Ruleset.Value = CreateEditorRuleset().RulesetInfo; + Beatmap.Value = CreateWorkingBeatmap(Ruleset.Value); } public override void SetUpSteps() @@ -37,6 +32,14 @@ namespace osu.Game.Tests.Visual && Editor.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); } + /// + /// Creates the ruleset for providing a corresponding beatmap to load the editor on. + /// + [NotNull] + protected abstract Ruleset CreateEditorRuleset(); + + protected sealed override Ruleset CreateRuleset() => CreateEditorRuleset(); + protected virtual Editor CreateEditor() => new Editor(); } } From 9920911390833ffb35fddffaaa62803ebf92ecd1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Jun 2020 17:20:08 +0900 Subject: [PATCH 1035/1117] Fix tournament displayed beatmap potentially being out of order on quick changes --- osu.Game.Tournament/IPC/FileBasedIPC.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tournament/IPC/FileBasedIPC.cs b/osu.Game.Tournament/IPC/FileBasedIPC.cs index 53ba597a7e..de4d482d13 100644 --- a/osu.Game.Tournament/IPC/FileBasedIPC.cs +++ b/osu.Game.Tournament/IPC/FileBasedIPC.cs @@ -34,6 +34,7 @@ namespace osu.Game.Tournament.IPC private int lastBeatmapId; private ScheduledDelegate scheduled; + private GetBeatmapRequest beatmapLookupRequest; public Storage Storage { get; private set; } @@ -77,6 +78,8 @@ namespace osu.Game.Tournament.IPC if (lastBeatmapId != beatmapId) { + beatmapLookupRequest?.Cancel(); + lastBeatmapId = beatmapId; var existing = ladder.CurrentMatch.Value?.Round.Value?.Beatmaps.FirstOrDefault(b => b.ID == beatmapId && b.BeatmapInfo != null); @@ -85,9 +88,9 @@ namespace osu.Game.Tournament.IPC Beatmap.Value = existing.BeatmapInfo; else { - var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = beatmapId }); - req.Success += b => Beatmap.Value = b.ToBeatmap(Rulesets); - API.Queue(req); + beatmapLookupRequest = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = beatmapId }); + beatmapLookupRequest.Success += b => Beatmap.Value = b.ToBeatmap(Rulesets); + API.Queue(beatmapLookupRequest); } } From 5d7bb8cb4e9e44eeb8a504f7d8e43f9046003aca Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 4 Jun 2020 21:33:38 +0900 Subject: [PATCH 1036/1117] Change format of date on score panel --- osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs index fd8ac33aef..81d5d113ae 100644 --- a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs @@ -211,7 +211,7 @@ namespace osu.Game.Screens.Ranking.Expanded Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold), - Text = $"Played on {score.Date.ToLocalTime():g}" + Text = $"Played on {score.Date.ToLocalTime():d MMMM yyyy HH:mm}" } } }; From afcefe01bf74177240c59e70b3ea2b87745a4223 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 4 Jun 2020 21:48:55 +0900 Subject: [PATCH 1037/1117] Fix score panel not receiving input in some places --- osu.Game/Screens/Ranking/ScorePanel.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Screens/Ranking/ScorePanel.cs b/osu.Game/Screens/Ranking/ScorePanel.cs index a99b48e8f0..65fb901c89 100644 --- a/osu.Game/Screens/Ranking/ScorePanel.cs +++ b/osu.Game/Screens/Ranking/ScorePanel.cs @@ -243,5 +243,10 @@ namespace osu.Game.Screens.Ranking return true; } + + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) + => base.ReceivePositionalInputAt(screenSpacePos) + || topLayerContainer.ReceivePositionalInputAt(screenSpacePos) + || middleLayerContainer.ReceivePositionalInputAt(screenSpacePos); } } From 9c1542f8979637fcea83b327b5252f2fae8933a1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 4 Jun 2020 22:17:00 +0900 Subject: [PATCH 1038/1117] Fix crash when pressing clear button twice --- .../Settings/TestSceneKeyBindingPanel.cs | 45 ++++++++++++++++++- osu.Game/Overlays/KeyBinding/KeyBindingRow.cs | 5 ++- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs index 745820696a..3d335995ac 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs @@ -1,13 +1,19 @@ // 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 System.Linq; using NUnit.Framework; +using osu.Framework.Testing; +using osu.Framework.Threading; using osu.Game.Overlays; +using osu.Game.Overlays.KeyBinding; +using osuTK.Input; namespace osu.Game.Tests.Visual.Settings { [TestFixture] - public class TestSceneKeyBindingPanel : OsuTestScene + public class TestSceneKeyBindingPanel : OsuManualInputManagerTestScene { private readonly KeyBindingPanel panel; @@ -21,5 +27,42 @@ namespace osu.Game.Tests.Visual.Settings base.LoadComplete(); panel.Show(); } + + [Test] + public void TestClickTwiceOnClearButton() + { + KeyBindingRow firstRow = null; + + AddStep("click first row", () => + { + firstRow = panel.ChildrenOfType().First(); + InputManager.MoveMouseTo(firstRow); + InputManager.Click(MouseButton.Left); + }); + + AddStep("schedule button clicks", () => + { + var clearButton = firstRow.ChildrenOfType().Single(); + + InputManager.MoveMouseTo(clearButton); + + int buttonClicks = 0; + ScheduledDelegate clickDelegate = null; + + clickDelegate = Scheduler.AddDelayed(() => + { + InputManager.PressButton(MouseButton.Left); + InputManager.ReleaseButton(MouseButton.Left); + + if (++buttonClicks == 2) + { + // ReSharper disable once AccessToModifiedClosure + Debug.Assert(clickDelegate != null); + // ReSharper disable once AccessToModifiedClosure + clickDelegate.Cancel(); + } + }, 0, true); + }); + } } } diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs index 01d5991d3e..eafb4572ca 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs @@ -274,6 +274,9 @@ namespace osu.Game.Overlays.KeyBinding private void clear() { + if (bindTarget == null) + return; + bindTarget.UpdateKeyCombination(InputKey.None); finalise(); } @@ -333,7 +336,7 @@ namespace osu.Game.Overlays.KeyBinding } } - private class ClearButton : TriangleButton + public class ClearButton : TriangleButton { public ClearButton() { From 6b88141e58b6d3863b1aeb9db41d39225cd00bda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 4 Jun 2020 21:30:59 +0200 Subject: [PATCH 1039/1117] Add mania sample conversion test --- .../ManiaBeatmapSampleConversionTest.cs | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 osu.Game.Rulesets.Mania.Tests/ManiaBeatmapSampleConversionTest.cs diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapSampleConversionTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapSampleConversionTest.cs new file mode 100644 index 0000000000..dbf1cf5f72 --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapSampleConversionTest.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; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Utils; +using osu.Game.Audio; +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Objects; +using osu.Game.Tests.Beatmaps; + +namespace osu.Game.Rulesets.Mania.Tests +{ + [TestFixture] + public class ManiaBeatmapSampleConversionTest : BeatmapConversionTest, SampleConvertValue> + { + protected override string ResourceAssembly => "osu.Game.Rulesets.Mania"; + + public void Test(string name) => base.Test(name); + + protected override IEnumerable CreateConvertValue(HitObject hitObject) + { + yield return new SampleConvertValue + { + StartTime = hitObject.StartTime, + EndTime = hitObject.GetEndTime(), + Column = ((ManiaHitObject)hitObject).Column, + NodeSamples = getSampleNames((hitObject as HoldNote)?.NodeSamples) + }; + } + + private IList> getSampleNames(List> hitSampleInfo) + => hitSampleInfo?.Select(samples => + (IList)samples.Select(sample => sample.LookupNames.First()).ToList()) + .ToList(); + + protected override Ruleset CreateRuleset() => new ManiaRuleset(); + } + + public struct SampleConvertValue : IEquatable + { + /// + /// A sane value to account for osu!stable using ints everywhere. + /// + private const float conversion_lenience = 2; + + public double StartTime; + public double EndTime; + public int Column; + public IList> NodeSamples; + + public bool Equals(SampleConvertValue other) + => Precision.AlmostEquals(StartTime, other.StartTime, conversion_lenience) + && Precision.AlmostEquals(EndTime, other.EndTime, conversion_lenience) + && samplesEqual(NodeSamples, other.NodeSamples); + + private static bool samplesEqual(ICollection> first, ICollection> second) + { + if (first == null && second == null) + return true; + + // both items can't be null now, so if any single one is, then they're not equal + if (first == null || second == null) + return false; + + return first.Count == second.Count + && first.Zip(second).All(samples => samples.First.SequenceEqual(samples.Second)); + } + } +} From 35544ede50069851ff7cfa0fecdf141fe94345db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 4 Jun 2020 21:54:19 +0200 Subject: [PATCH 1040/1117] Add failing test cases --- .../ManiaBeatmapSampleConversionTest.cs | 2 ++ .../convert-samples-expected-conversion.json | 30 +++++++++++++++++++ .../Testing/Beatmaps/convert-samples.osu | 16 ++++++++++ .../mania-samples-expected-conversion.json | 25 ++++++++++++++++ .../Testing/Beatmaps/mania-samples.osu | 19 ++++++++++++ 5 files changed, 92 insertions(+) create mode 100644 osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/convert-samples-expected-conversion.json create mode 100644 osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/convert-samples.osu create mode 100644 osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/mania-samples-expected-conversion.json create mode 100644 osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/mania-samples.osu diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapSampleConversionTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapSampleConversionTest.cs index dbf1cf5f72..2f6918d263 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapSampleConversionTest.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapSampleConversionTest.cs @@ -18,6 +18,8 @@ namespace osu.Game.Rulesets.Mania.Tests { protected override string ResourceAssembly => "osu.Game.Rulesets.Mania"; + [TestCase("convert-samples")] + [TestCase("mania-samples")] public void Test(string name) => base.Test(name); protected override IEnumerable CreateConvertValue(HitObject hitObject) diff --git a/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/convert-samples-expected-conversion.json b/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/convert-samples-expected-conversion.json new file mode 100644 index 0000000000..b8ce85eef5 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/convert-samples-expected-conversion.json @@ -0,0 +1,30 @@ +{ + "Mappings": [{ + "StartTime": 1000.0, + "Objects": [{ + "StartTime": 1000.0, + "EndTime": 2750.0, + "Column": 1, + "NodeSamples": [ + ["normal-hitnormal"], + ["soft-hitnormal"], + ["drum-hitnormal"] + ] + }, { + "StartTime": 1875.0, + "EndTime": 2750.0, + "Column": 0, + "NodeSamples": [ + ["soft-hitnormal"], + ["drum-hitnormal"] + ] + }] + }, { + "StartTime": 3750.0, + "Objects": [{ + "StartTime": 3750.0, + "EndTime": 3750.0, + "Column": 3 + }] + }] +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/convert-samples.osu b/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/convert-samples.osu new file mode 100644 index 0000000000..16b73992d2 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/convert-samples.osu @@ -0,0 +1,16 @@ +osu file format v14 + +[Difficulty] +HPDrainRate:5 +CircleSize:5 +OverallDifficulty:5 +ApproachRate:5 +SliderMultiplier:1.4 +SliderTickRate:1 + +[TimingPoints] +0,500,4,1,0,100,1,0 + +[HitObjects] +88,99,1000,6,0,L|306:259,2,245,0|0|0,1:0|2:0|3:0,0:0:0:0: +259,118,3750,1,0,0:0:0:0: diff --git a/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/mania-samples-expected-conversion.json b/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/mania-samples-expected-conversion.json new file mode 100644 index 0000000000..e22540614d --- /dev/null +++ b/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/mania-samples-expected-conversion.json @@ -0,0 +1,25 @@ +{ + "Mappings": [{ + "StartTime": 500.0, + "Objects": [{ + "StartTime": 500.0, + "EndTime": 1500.0, + "Column": 0, + "NodeSamples": [ + ["normal-hitnormal"], + [] + ] + }] + }, { + "StartTime": 2000.0, + "Objects": [{ + "StartTime": 2000.0, + "EndTime": 3000.0, + "Column": 2, + "NodeSamples": [ + ["drum-hitnormal"], + [] + ] + }] + }] +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/mania-samples.osu b/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/mania-samples.osu new file mode 100644 index 0000000000..7c75b45e5f --- /dev/null +++ b/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/mania-samples.osu @@ -0,0 +1,19 @@ +osu file format v14 + +[General] +Mode: 3 + +[Difficulty] +HPDrainRate:5 +CircleSize:5 +OverallDifficulty:5 +ApproachRate:5 +SliderMultiplier:1.4 +SliderTickRate:1 + +[TimingPoints] +0,500,4,1,0,100,1,0 + +[HitObjects] +51,192,500,128,0,1500:1:0:0:0: +256,192,2000,128,0,3000:3:0:0:0: From ac019bddd61b798f86c0f9e545a5d1e45de5a746 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 4 Jun 2020 22:28:55 +0200 Subject: [PATCH 1041/1117] Only play samples at start of hold note in mania maps --- .../Beatmaps/ManiaBeatmapConverter.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index 32abf5e7f9..b025ac7992 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -6,6 +6,7 @@ using System; using System.Linq; using System.Collections.Generic; using osu.Framework.Utils; +using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; @@ -239,7 +240,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps Duration = endTimeData.Duration, Column = column, Samples = HitObject.Samples, - NodeSamples = (HitObject as IHasRepeats)?.NodeSamples + NodeSamples = (HitObject as IHasRepeats)?.NodeSamples ?? defaultNodeSamples }); } else if (HitObject is IHasXPosition) @@ -254,6 +255,16 @@ namespace osu.Game.Rulesets.Mania.Beatmaps return pattern; } + + /// + /// osu!mania-specific beatmaps in stable only play samples at the start of the hold note. + /// + private List> defaultNodeSamples + => new List> + { + HitObject.Samples, + new List() + }; } } } From c4cae006aa800e78f29b46dfcdccda0220838a60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 4 Jun 2020 22:47:14 +0200 Subject: [PATCH 1042/1117] Correctly slice node sample list when converting --- .../Legacy/DistanceObjectPatternGenerator.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index 1bd796511b..b49b881656 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -472,15 +472,21 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy /// /// The time to retrieve the sample info list from. /// - private IList sampleInfoListAt(double time) + private IList sampleInfoListAt(double time) => nodeSamplesAt(time)?.First() ?? HitObject.Samples; + + /// + /// Retrieves the list of node samples that occur at time greater than or equal to . + /// + /// The time to retrieve node samples at. + private IEnumerable> nodeSamplesAt(double time) { if (!(HitObject is IHasPathWithRepeats curveData)) - return HitObject.Samples; + return null; double segmentTime = (EndTime - HitObject.StartTime) / spanCount; int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime); - return curveData.NodeSamples[index]; + return curveData.NodeSamples.Skip(index); } /// @@ -511,7 +517,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy Duration = endTime - startTime, Column = column, Samples = HitObject.Samples, - NodeSamples = (HitObject as IHasRepeats)?.NodeSamples + NodeSamples = nodeSamplesAt(startTime)?.ToList() }; } From 4c6116e6e7c9aa1300430954ef4e6cbcc793f39b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 4 Jun 2020 23:50:58 +0200 Subject: [PATCH 1043/1117] Fix compilation failure in Android test project --- .../ManiaBeatmapSampleConversionTest.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapSampleConversionTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapSampleConversionTest.cs index 2f6918d263..d8f87195d1 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapSampleConversionTest.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapSampleConversionTest.cs @@ -58,17 +58,19 @@ namespace osu.Game.Rulesets.Mania.Tests && Precision.AlmostEquals(EndTime, other.EndTime, conversion_lenience) && samplesEqual(NodeSamples, other.NodeSamples); - private static bool samplesEqual(ICollection> first, ICollection> second) + private static bool samplesEqual(ICollection> firstSampleList, ICollection> secondSampleList) { - if (first == null && second == null) + if (firstSampleList == null && secondSampleList == null) return true; // both items can't be null now, so if any single one is, then they're not equal - if (first == null || second == null) + if (firstSampleList == null || secondSampleList == null) return false; - return first.Count == second.Count - && first.Zip(second).All(samples => samples.First.SequenceEqual(samples.Second)); + return firstSampleList.Count == secondSampleList.Count + // cannot use .Zip() without the selector function as it doesn't compile in android test project + && firstSampleList.Zip(secondSampleList, (first, second) => (first, second)) + .All(samples => samples.first.SequenceEqual(samples.second)); } } } From 896177801a57e0bd2161309d05775be4d0d087bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 5 Jun 2020 00:07:27 +0200 Subject: [PATCH 1044/1117] Avoid creating copies of node samples every time --- .../Patterns/Legacy/DistanceObjectPatternGenerator.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index b49b881656..9fbdf58e21 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -478,7 +478,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy /// Retrieves the list of node samples that occur at time greater than or equal to . /// /// The time to retrieve node samples at. - private IEnumerable> nodeSamplesAt(double time) + private List> nodeSamplesAt(double time) { if (!(HitObject is IHasPathWithRepeats curveData)) return null; @@ -486,7 +486,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy double segmentTime = (EndTime - HitObject.StartTime) / spanCount; int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime); - return curveData.NodeSamples.Skip(index); + + // avoid slicing the list & creating copies, if at all possible. + return index == 0 ? curveData.NodeSamples : curveData.NodeSamples.Skip(index).ToList(); } /// @@ -517,7 +519,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy Duration = endTime - startTime, Column = column, Samples = HitObject.Samples, - NodeSamples = nodeSamplesAt(startTime)?.ToList() + NodeSamples = nodeSamplesAt(startTime) }; } From 0107e9ba16deb94cd8f04c5dcf36b7ca2a781adc Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 5 Jun 2020 19:18:00 +0900 Subject: [PATCH 1045/1117] Change lookups to use SingleOrDefault() --- osu.Game/Beatmaps/BeatmapManager.cs | 2 +- osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 668ac6ee10..1f92d5461f 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -215,7 +215,7 @@ namespace osu.Game.Beatmaps foreach (var info in item.Beatmaps) { - var file = item.Files.FirstOrDefault(f => string.Equals(f.Filename, info.Path, StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath; + var file = item.Files.SingleOrDefault(f => string.Equals(f.Filename, info.Path, StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath; using (var stream = Files.Store.GetStream(file)) info.MD5Hash = stream.ComputeMD5Hash(); diff --git a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs index e62a9bb39d..39c5ccab27 100644 --- a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs @@ -42,7 +42,7 @@ namespace osu.Game.Beatmaps } } - private string getPathForFile(string filename) => BeatmapSetInfo.Files.FirstOrDefault(f => string.Equals(f.Filename, filename, StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath; + private string getPathForFile(string filename) => BeatmapSetInfo.Files.SingleOrDefault(f => string.Equals(f.Filename, filename, StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath; private TextureStore textureStore; From bb89114b70eb820096ae3dc1b371c0d3ce8c05c7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 5 Jun 2020 20:52:27 +0900 Subject: [PATCH 1046/1117] Show a loading spinner on multiplayer lounge loads --- .../TestSceneLoungeRoomsContainer.cs | 3 ++ .../TestSceneMatchSettingsOverlay.cs | 2 ++ .../Multiplayer/TestSceneMatchSubScreen.cs | 2 ++ osu.Game/Screens/Multi/IRoomManager.cs | 5 +++ .../Screens/Multi/Lounge/LoungeSubScreen.cs | 33 +++++++++++++++++-- osu.Game/Screens/Multi/RoomManager.cs | 14 +++++++- 6 files changed, 56 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs index 77b41c89b0..83f2297bd2 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs @@ -141,6 +141,9 @@ namespace osu.Game.Tests.Visual.Multiplayer } public readonly BindableList Rooms = new BindableList(); + + public Bindable InitialRoomsReceived { get; } = new Bindable(true); + IBindableList IRoomManager.Rooms => Rooms; public void CreateRoom(Room room, Action onSuccess = null, Action onError = null) => Rooms.Add(room); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs index 34c6940552..fdc20dc477 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs @@ -133,6 +133,8 @@ namespace osu.Game.Tests.Visual.Multiplayer remove { } } + public Bindable InitialRoomsReceived { get; } = new Bindable(true); + public IBindableList Rooms { get; } = null; public void CreateRoom(Room room, Action onSuccess = null, Action onError = null) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSubScreen.cs index d678d5a814..9d0c159549 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSubScreen.cs @@ -93,6 +93,8 @@ namespace osu.Game.Tests.Visual.Multiplayer remove => throw new NotImplementedException(); } + public Bindable InitialRoomsReceived { get; } = new Bindable(true); + public IBindableList Rooms { get; } = new BindableList(); public void CreateRoom(Room room, Action onSuccess = null, Action onError = null) diff --git a/osu.Game/Screens/Multi/IRoomManager.cs b/osu.Game/Screens/Multi/IRoomManager.cs index f6c979851e..bf75843c3e 100644 --- a/osu.Game/Screens/Multi/IRoomManager.cs +++ b/osu.Game/Screens/Multi/IRoomManager.cs @@ -14,6 +14,11 @@ namespace osu.Game.Screens.Multi /// event Action RoomsUpdated; + /// + /// Whether an initial listing of rooms has been received. + /// + Bindable InitialRoomsReceived { get; } + /// /// All the active s. /// diff --git a/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs index 7c10f0f975..d4b6a3b79f 100644 --- a/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs @@ -22,12 +22,16 @@ namespace osu.Game.Screens.Multi.Lounge protected readonly FilterControl Filter; + private readonly Bindable initialRoomsReceived = new Bindable(); + private readonly Container content; private readonly LoadingLayer loadingLayer; [Resolved] private Bindable selectedRoom { get; set; } + private bool joiningRoom; + public LoungeSubScreen() { SearchContainer searchContainer; @@ -73,6 +77,14 @@ namespace osu.Game.Screens.Multi.Lounge }; } + protected override void LoadComplete() + { + base.LoadComplete(); + + initialRoomsReceived.BindTo(RoomManager.InitialRoomsReceived); + initialRoomsReceived.BindValueChanged(onInitialRoomsReceivedChanged, true); + } + protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); @@ -126,12 +138,29 @@ namespace osu.Game.Screens.Multi.Lounge private void joinRequested(Room room) { - loadingLayer.Show(); + joiningRoom = true; + updateLoadingLayer(); + RoomManager?.JoinRoom(room, r => { Open(room); + joiningRoom = false; + updateLoadingLayer(); + }, _ => + { + joiningRoom = false; + updateLoadingLayer(); + }); + } + + private void onInitialRoomsReceivedChanged(ValueChangedEvent received) => updateLoadingLayer(); + + private void updateLoadingLayer() + { + if (joiningRoom || !initialRoomsReceived.Value) + loadingLayer.Show(); + else loadingLayer.Hide(); - }, _ => loadingLayer.Hide()); } /// diff --git a/osu.Game/Screens/Multi/RoomManager.cs b/osu.Game/Screens/Multi/RoomManager.cs index ad461af57f..4d6ac46c84 100644 --- a/osu.Game/Screens/Multi/RoomManager.cs +++ b/osu.Game/Screens/Multi/RoomManager.cs @@ -25,6 +25,9 @@ namespace osu.Game.Screens.Multi public event Action RoomsUpdated; private readonly BindableList rooms = new BindableList(); + + public Bindable InitialRoomsReceived { get; } = new Bindable(); + public IBindableList Rooms => rooms; public double TimeBetweenListingPolls @@ -62,7 +65,11 @@ namespace osu.Game.Screens.Multi InternalChildren = new Drawable[] { - listingPollingComponent = new ListingPollingComponent { RoomsReceived = onListingReceived }, + listingPollingComponent = new ListingPollingComponent + { + InitialRoomsReceived = { BindTarget = InitialRoomsReceived }, + RoomsReceived = onListingReceived + }, selectionPollingComponent = new SelectionPollingComponent { RoomReceived = onSelectedRoomReceived } }; } @@ -262,6 +269,8 @@ namespace osu.Game.Screens.Multi { public Action> RoomsReceived; + public readonly Bindable InitialRoomsReceived = new Bindable(); + [Resolved] private IAPIProvider api { get; set; } @@ -273,6 +282,8 @@ namespace osu.Game.Screens.Multi { currentFilter.BindValueChanged(_ => { + InitialRoomsReceived.Value = false; + if (IsLoaded) PollImmediately(); }); @@ -292,6 +303,7 @@ namespace osu.Game.Screens.Multi pollReq.Success += result => { + InitialRoomsReceived.Value = true; RoomsReceived?.Invoke(result); tcs.SetResult(true); }; From 0f78af7252a08179cce65ee47d9bbe3acbf70c00 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 6 Jun 2020 19:19:30 +0300 Subject: [PATCH 1047/1117] Remove unnecessary disabled check I have a bad memory here, til. --- osu.Game/Tests/Visual/OsuTestScene.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs index 88bd087215..e5d5442074 100644 --- a/osu.Game/Tests/Visual/OsuTestScene.cs +++ b/osu.Game/Tests/Visual/OsuTestScene.cs @@ -146,8 +146,7 @@ namespace osu.Game.Tests.Visual [BackgroundDependencyLoader] private void load(RulesetStore rulesets) { - if (!Ruleset.Disabled) - Ruleset.Value = rulesets.AvailableRulesets.First(); + Ruleset.Value = rulesets.AvailableRulesets.First(); } protected override void Dispose(bool isDisposing) From efd5e144103cfb7a06563674f23d712017550dd2 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 6 Jun 2020 19:20:06 +0300 Subject: [PATCH 1048/1117] Clarify why ruleset bindable must be set at the BDL of any base test scene --- osu.Game/Tests/Visual/PlayerTestScene.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Tests/Visual/PlayerTestScene.cs b/osu.Game/Tests/Visual/PlayerTestScene.cs index 1e267726e0..05b1eea6b3 100644 --- a/osu.Game/Tests/Visual/PlayerTestScene.cs +++ b/osu.Game/Tests/Visual/PlayerTestScene.cs @@ -34,6 +34,8 @@ namespace osu.Game.Tests.Visual [BackgroundDependencyLoader] private void load() { + // There are test scenes using current value of the ruleset bindable + // on their BDLs (example in TestSceneSliderSnaking's BDL) Ruleset.Value = ruleset.RulesetInfo; Dependencies.Cache(LocalConfig = new OsuConfigManager(LocalStorage)); From 72ada020a2515a1ed839fecbeb0af52c3ce86abc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 8 Jun 2020 13:42:16 +0900 Subject: [PATCH 1049/1117] Don't attempt to use virtual track for intro sequence clock --- osu.Game/Screens/Menu/IntroTriangles.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/IntroTriangles.cs b/osu.Game/Screens/Menu/IntroTriangles.cs index 188a49c147..cb05dcc932 100644 --- a/osu.Game/Screens/Menu/IntroTriangles.cs +++ b/osu.Game/Screens/Menu/IntroTriangles.cs @@ -7,6 +7,7 @@ using System.IO; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; +using osu.Framework.Audio.Track; using osu.Framework.Screens; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -61,7 +62,7 @@ namespace osu.Game.Screens.Menu LoadComponentAsync(new TrianglesIntroSequence(logo, background) { RelativeSizeAxes = Axes.Both, - Clock = new FramedClock(MenuMusic.Value ? Track : null), + Clock = new FramedClock(MenuMusic.Value && !(Track is TrackVirtual) ? Track : null), LoadMenu = LoadMenu }, t => { From dfed27bd4633e5b2d1268f8851fec97a698d61e6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 8 Jun 2020 14:24:21 +0900 Subject: [PATCH 1050/1117] Add back stream seeking for sanity --- osu.Game/Beatmaps/BeatmapManager.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index f11e94e63d..4e3714a582 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -201,6 +201,8 @@ namespace osu.Game.Beatmaps using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) new LegacyBeatmapEncoder(beatmapContent).Encode(sw); + stream.Seek(0, SeekOrigin.Begin); + UpdateFile(setInfo, setInfo.Files.Single(f => string.Equals(f.Filename, info.Path, StringComparison.OrdinalIgnoreCase)), stream); } From 443977aa8d71071a7566a4be643ffea72b77fee1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 8 Jun 2020 14:40:17 +0900 Subject: [PATCH 1051/1117] Remove PreUpdate, update hash in Save() --- osu.Game/Beatmaps/BeatmapManager.cs | 22 ++++++++-------------- osu.Game/Database/ArchiveModelManager.cs | 11 ----------- 2 files changed, 8 insertions(+), 25 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 4e3714a582..cbcdf51551 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -203,7 +203,14 @@ namespace osu.Game.Beatmaps stream.Seek(0, SeekOrigin.Begin); - UpdateFile(setInfo, setInfo.Files.Single(f => string.Equals(f.Filename, info.Path, StringComparison.OrdinalIgnoreCase)), stream); + using (ContextFactory.GetForWrite()) + { + var beatmapInfo = setInfo.Beatmaps.Single(b => b.ID == info.ID); + beatmapInfo.MD5Hash = stream.ComputeMD5Hash(); + + stream.Seek(0, SeekOrigin.Begin); + UpdateFile(setInfo, setInfo.Files.Single(f => string.Equals(f.Filename, info.Path, StringComparison.OrdinalIgnoreCase)), stream); + } } var working = workingCache.FirstOrDefault(w => w.BeatmapInfo?.ID == info.ID); @@ -211,19 +218,6 @@ namespace osu.Game.Beatmaps workingCache.Remove(working); } - protected override void PreUpdate(BeatmapSetInfo item) - { - base.PreUpdate(item); - - foreach (var info in item.Beatmaps) - { - var file = item.Files.SingleOrDefault(f => string.Equals(f.Filename, info.Path, StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath; - - using (var stream = Files.Store.GetStream(file)) - info.MD5Hash = stream.ComputeMD5Hash(); - } - } - private readonly WeakList workingCache = new WeakList(); /// diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index b9479af623..915d980d24 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -429,21 +429,10 @@ namespace osu.Game.Database using (ContextFactory.GetForWrite()) { item.Hash = computeHash(item); - - PreUpdate(item); - ModelStore.Update(item); } } - /// - /// Perform any final actions before the update to database executes. - /// - /// The that is being updated. - protected virtual void PreUpdate(TModel item) - { - } - /// /// Delete an item from the manager. /// Is a no-op for already deleted items. From 63003757c4fee77ca055861cb0538019509138a9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 8 Jun 2020 14:48:26 +0900 Subject: [PATCH 1052/1117] Remove WorkingBeatmap cache when deleting or updating a beatmap --- osu.Game/Beatmaps/BeatmapManager.cs | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index e7cef13c68..73e4c119e4 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -79,6 +79,8 @@ namespace osu.Game.Beatmaps beatmaps = (BeatmapStore)ModelStore; beatmaps.BeatmapHidden += b => beatmapHidden.Value = new WeakReference(b); beatmaps.BeatmapRestored += b => beatmapRestored.Value = new WeakReference(b); + beatmaps.ItemRemoved += removeWorkingCache; + beatmaps.ItemUpdated += removeWorkingCache; onlineLookupQueue = new BeatmapOnlineLookupQueue(api, storage); } @@ -206,9 +208,7 @@ namespace osu.Game.Beatmaps UpdateFile(setInfo, setInfo.Files.Single(f => string.Equals(f.Filename, info.Path, StringComparison.OrdinalIgnoreCase)), stream); } - var working = workingCache.FirstOrDefault(w => w.BeatmapInfo?.ID == info.ID); - if (working != null) - workingCache.Remove(working); + removeWorkingCache(info); } private readonly WeakList workingCache = new WeakList(); @@ -410,6 +410,24 @@ namespace osu.Game.Beatmaps return endTime - startTime; } + private void removeWorkingCache(BeatmapSetInfo info) + { + if (info.Beatmaps == null) return; + + foreach (var b in info.Beatmaps) + removeWorkingCache(b); + } + + private void removeWorkingCache(BeatmapInfo info) + { + lock (workingCache) + { + var working = workingCache.FirstOrDefault(w => w.BeatmapInfo?.ID == info.ID); + if (working != null) + workingCache.Remove(working); + } + } + public void Dispose() { onlineLookupQueue?.Dispose(); From dd61d6ed04f47aa77739e974b29949a898d79c74 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 8 Jun 2020 14:48:42 +0900 Subject: [PATCH 1053/1117] Attempt to reimport intro if a bad state is detected --- osu.Game/Screens/Menu/IntroScreen.cs | 62 ++++++++++++++++--------- osu.Game/Screens/Menu/IntroTriangles.cs | 5 +- 2 files changed, 43 insertions(+), 24 deletions(-) diff --git a/osu.Game/Screens/Menu/IntroScreen.cs b/osu.Game/Screens/Menu/IntroScreen.cs index 0d5f3d1142..b99d8ae9d1 100644 --- a/osu.Game/Screens/Menu/IntroScreen.cs +++ b/osu.Game/Screens/Menu/IntroScreen.cs @@ -41,9 +41,9 @@ namespace osu.Game.Screens.Menu protected IBindable MenuMusic { get; private set; } - private WorkingBeatmap introBeatmap; + private WorkingBeatmap initialBeatmap; - protected Track Track { get; private set; } + protected Track Track => initialBeatmap?.Track; private readonly BindableDouble exitingVolumeFade = new BindableDouble(1); @@ -58,6 +58,11 @@ namespace osu.Game.Screens.Menu [Resolved] private AudioManager audio { get; set; } + /// + /// Whether the is provided by osu! resources, rather than a user beatmap. + /// + protected bool UsingThemedIntro { get; private set; } + [BackgroundDependencyLoader] private void load(OsuConfigManager config, SkinManager skinManager, BeatmapManager beatmaps, Framework.Game game) { @@ -71,29 +76,45 @@ namespace osu.Game.Screens.Menu BeatmapSetInfo setInfo = null; + // if the user has requested not to play theme music, we should attempt to find a random beatmap from their collection. if (!MenuMusic.Value) { var sets = beatmaps.GetAllUsableBeatmapSets(IncludedDetails.Minimal); + if (sets.Count > 0) - setInfo = beatmaps.QueryBeatmapSet(s => s.ID == sets[RNG.Next(0, sets.Count - 1)].ID); - } - - if (setInfo == null) - { - setInfo = beatmaps.QueryBeatmapSet(b => b.Hash == BeatmapHash); - - if (setInfo == null) { - // we need to import the default menu background beatmap - setInfo = beatmaps.Import(new ZipArchiveReader(game.Resources.GetStream($"Tracks/{BeatmapFile}"), BeatmapFile)).Result; - - setInfo.Protected = true; - beatmaps.Update(setInfo); + setInfo = beatmaps.QueryBeatmapSet(s => s.ID == sets[RNG.Next(0, sets.Count - 1)].ID); + initialBeatmap = beatmaps.GetWorkingBeatmap(setInfo.Beatmaps[0]); } } - introBeatmap = beatmaps.GetWorkingBeatmap(setInfo.Beatmaps[0]); - Track = introBeatmap.Track; + // we generally want a song to be playing on startup, so use the intro music even if a user has specified not to if no other track is available. + if (setInfo == null) + { + if (!loadThemedIntro()) + { + // if we detect that the theme track or beatmap is unavailable this is either first startup or things are in a bad state. + // this could happen if a user has nuked their files store. for now, reimport to repair this. + var import = beatmaps.Import(new ZipArchiveReader(game.Resources.GetStream($"Tracks/{BeatmapFile}"), BeatmapFile)).Result; + import.Protected = true; + beatmaps.Update(import); + + loadThemedIntro(); + } + } + + bool loadThemedIntro() + { + setInfo = beatmaps.QueryBeatmapSet(b => b.Hash == BeatmapHash); + + if (setInfo != null) + { + initialBeatmap = beatmaps.GetWorkingBeatmap(setInfo.Beatmaps[0]); + UsingThemedIntro = !(Track is TrackVirtual); + } + + return UsingThemedIntro; + } } public override void OnResuming(IScreen last) @@ -119,7 +140,7 @@ namespace osu.Game.Screens.Menu public override void OnSuspending(IScreen next) { base.OnSuspending(next); - Track = null; + initialBeatmap = null; } protected override BackgroundScreen CreateBackground() => new BackgroundScreenBlack(); @@ -127,7 +148,7 @@ namespace osu.Game.Screens.Menu protected void StartTrack() { // Only start the current track if it is the menu music. A beatmap's track is started when entering the Main Menu. - if (MenuMusic.Value) + if (UsingThemedIntro) Track.Restart(); } @@ -141,8 +162,7 @@ namespace osu.Game.Screens.Menu if (!resuming) { - beatmap.Value = introBeatmap; - introBeatmap = null; + beatmap.Value = initialBeatmap; logo.MoveTo(new Vector2(0.5f)); logo.ScaleTo(Vector2.One); diff --git a/osu.Game/Screens/Menu/IntroTriangles.cs b/osu.Game/Screens/Menu/IntroTriangles.cs index cb05dcc932..225ad02ec4 100644 --- a/osu.Game/Screens/Menu/IntroTriangles.cs +++ b/osu.Game/Screens/Menu/IntroTriangles.cs @@ -7,7 +7,6 @@ using System.IO; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; -using osu.Framework.Audio.Track; using osu.Framework.Screens; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -47,7 +46,7 @@ namespace osu.Game.Screens.Menu [BackgroundDependencyLoader] private void load() { - if (MenuVoice.Value && !MenuMusic.Value) + if (MenuVoice.Value && !UsingThemedIntro) welcome = audio.Samples.Get(@"welcome"); } @@ -62,7 +61,7 @@ namespace osu.Game.Screens.Menu LoadComponentAsync(new TrianglesIntroSequence(logo, background) { RelativeSizeAxes = Axes.Both, - Clock = new FramedClock(MenuMusic.Value && !(Track is TrackVirtual) ? Track : null), + Clock = new FramedClock(UsingThemedIntro ? Track : null), LoadMenu = LoadMenu }, t => { From 712fd6a944cc957bcff10721451ffe613c7180c0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 8 Jun 2020 17:49:45 +0900 Subject: [PATCH 1054/1117] Fetch existing private message channels on re-joining --- .../API/Requests/CreateChannelRequest.cs | 34 +++++++++++++++++++ .../API/Requests/Responses/APIChatChannel.cs | 18 ++++++++++ osu.Game/Online/Chat/Channel.cs | 3 +- osu.Game/Online/Chat/ChannelManager.cs | 29 ++++++++++------ 4 files changed, 73 insertions(+), 11 deletions(-) create mode 100644 osu.Game/Online/API/Requests/CreateChannelRequest.cs create mode 100644 osu.Game/Online/API/Requests/Responses/APIChatChannel.cs diff --git a/osu.Game/Online/API/Requests/CreateChannelRequest.cs b/osu.Game/Online/API/Requests/CreateChannelRequest.cs new file mode 100644 index 0000000000..42cb201969 --- /dev/null +++ b/osu.Game/Online/API/Requests/CreateChannelRequest.cs @@ -0,0 +1,34 @@ +// 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 System.Net.Http; +using osu.Framework.IO.Network; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Online.Chat; + +namespace osu.Game.Online.API.Requests +{ + public class CreateChannelRequest : APIRequest + { + private readonly Channel channel; + + public CreateChannelRequest(Channel channel) + { + this.channel = channel; + } + + protected override WebRequest CreateWebRequest() + { + var req = base.CreateWebRequest(); + req.Method = HttpMethod.Post; + + req.AddParameter("type", $"{ChannelType.PM}"); + req.AddParameter("target_id", $"{channel.Users.First().Id}"); + + return req; + } + + protected override string Target => @"chat/channels"; + } +} diff --git a/osu.Game/Online/API/Requests/Responses/APIChatChannel.cs b/osu.Game/Online/API/Requests/Responses/APIChatChannel.cs new file mode 100644 index 0000000000..fc3b2a8e31 --- /dev/null +++ b/osu.Game/Online/API/Requests/Responses/APIChatChannel.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.Collections.Generic; +using Newtonsoft.Json; +using osu.Game.Online.Chat; + +namespace osu.Game.Online.API.Requests.Responses +{ + public class APIChatChannel + { + [JsonProperty(@"channel_id")] + public int? ChannelID { get; set; } + + [JsonProperty(@"recent_messages")] + public List RecentMessages { get; set; } + } +} diff --git a/osu.Game/Online/Chat/Channel.cs b/osu.Game/Online/Chat/Channel.cs index dbb2da5c03..8c1e1ad128 100644 --- a/osu.Game/Online/Chat/Channel.cs +++ b/osu.Game/Online/Chat/Channel.cs @@ -84,7 +84,8 @@ namespace osu.Game.Online.Chat public long? LastReadId; /// - /// Signalles if the current user joined this channel or not. Defaults to false. + /// Signals if the current user joined this channel or not. Defaults to false. + /// Note that this does not guarantee a join has completed. Check Id > 0 for confirmation. /// public Bindable Joined = new Bindable(); diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index b17e0812da..9350887feb 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -86,7 +86,7 @@ namespace osu.Game.Online.Chat return; CurrentChannel.Value = JoinedChannels.FirstOrDefault(c => c.Type == ChannelType.PM && c.Users.Count == 1 && c.Users.Any(u => u.Id == user.Id)) - ?? new Channel(user); + ?? JoinChannel(new Channel(user)); } private void currentChannelChanged(ValueChangedEvent e) @@ -140,7 +140,7 @@ namespace osu.Game.Online.Chat target.AddLocalEcho(message); // if this is a PM and the first message, we need to do a special request to create the PM channel - if (target.Type == ChannelType.PM && !target.Joined.Value) + if (target.Type == ChannelType.PM && target.Id == 0) { var createNewPrivateMessageRequest = new CreateNewPrivateMessageRequest(target.Users.First(), message); @@ -356,26 +356,35 @@ namespace osu.Game.Online.Chat // ensure we are joined to the channel if (!channel.Joined.Value) { + channel.Joined.Value = true; + switch (channel.Type) { case ChannelType.Multiplayer: // join is implicit. happens when you join a multiplayer game. // this will probably change in the future. - channel.Joined.Value = true; joinChannel(channel, fetchInitialMessages); return channel; - case ChannelType.Private: - // can't do this yet. + case ChannelType.PM: + var createRequest = new CreateChannelRequest(channel); + createRequest.Success += resChannel => + { + if (resChannel.ChannelID.HasValue) + { + channel.Id = resChannel.ChannelID.Value; + + handleChannelMessages(resChannel.RecentMessages); + channel.MessagesLoaded = true; // this will mark the channel as having received messages even if there were none. + } + }; + + api.Queue(createRequest); break; default: var req = new JoinChannelRequest(channel, api.LocalUser.Value); - req.Success += () => - { - channel.Joined.Value = true; - joinChannel(channel, fetchInitialMessages); - }; + req.Success += () => joinChannel(channel, fetchInitialMessages); req.Failure += ex => LeaveChannel(channel); api.Queue(req); return channel; From ff555c41c6b667ebd91ba46f166cbd247ffeece3 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 8 Jun 2020 08:57:44 +0000 Subject: [PATCH 1055/1117] Bump Sentry from 2.1.1 to 2.1.3 Bumps [Sentry](https://github.com/getsentry/sentry-dotnet) from 2.1.1 to 2.1.3. - [Release notes](https://github.com/getsentry/sentry-dotnet/releases) - [Commits](https://github.com/getsentry/sentry-dotnet/compare/2.1.1...2.1.3) Signed-off-by: dependabot-preview[bot] --- 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 4d6358575b..c41d0a0cf6 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -26,7 +26,7 @@ - + From bbf8864f1478d609fe2eb7184cbd303e0cc9a14b Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 8 Jun 2020 09:45:31 +0000 Subject: [PATCH 1056/1117] Bump Microsoft.Build.Traversal from 2.0.34 to 2.0.48 Bumps [Microsoft.Build.Traversal](https://github.com/Microsoft/MSBuildSdks) from 2.0.34 to 2.0.48. - [Release notes](https://github.com/Microsoft/MSBuildSdks/releases) - [Changelog](https://github.com/microsoft/MSBuildSdks/blob/master/RELEASE.md) - [Commits](https://github.com/Microsoft/MSBuildSdks/compare/Microsoft.Build.Traversal.2.0.34...Microsoft.Build.Traversal.2.0.48) Signed-off-by: dependabot-preview[bot] --- global.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global.json b/global.json index 6c793a3f1d..bdb90eb0e9 100644 --- a/global.json +++ b/global.json @@ -5,6 +5,6 @@ "version": "3.1.100" }, "msbuild-sdks": { - "Microsoft.Build.Traversal": "2.0.34" + "Microsoft.Build.Traversal": "2.0.48" } } \ No newline at end of file From e0c94304c79637c86e9304e0471ce37bb139f223 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 8 Jun 2020 09:45:31 +0000 Subject: [PATCH 1057/1117] Bump Humanizer from 2.8.11 to 2.8.26 Bumps [Humanizer](https://github.com/Humanizr/Humanizer) from 2.8.11 to 2.8.26. - [Release notes](https://github.com/Humanizr/Humanizer/releases) - [Changelog](https://github.com/Humanizr/Humanizer/blob/master/release_notes.md) - [Commits](https://github.com/Humanizr/Humanizer/compare/v2.8.11...v2.8.26) Signed-off-by: dependabot-preview[bot] --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index c41d0a0cf6..8213719c01 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -20,7 +20,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 6b55fa51ff..fd13455c63 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -76,7 +76,7 @@ - + From 8a021e0beb39a897816d8da99983eb9de6e4b419 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 8 Jun 2020 22:35:01 +0900 Subject: [PATCH 1058/1117] Use save method in test --- .../Beatmaps/IO/ImportBeatmapTest.cs | 22 +++++-------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 55368f6676..249a8caba9 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -1,11 +1,10 @@ -// 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; using System.IO; using System.Collections.Generic; using System.Linq; -using System.Text; using System.Threading; using System.Threading.Tasks; using NUnit.Framework; @@ -15,7 +14,6 @@ using osu.Framework.Allocation; using osu.Framework.Extensions; using osu.Framework.Logging; using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Formats; using osu.Game.IO; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Tests.Resources; @@ -730,25 +728,17 @@ namespace osu.Game.Tests.Beatmaps.IO await osu.Dependencies.Get().Import(temp); BeatmapSetInfo setToUpdate = manager.GetAllUsableBeatmapSets()[0]; + + var beatmapInfo = setToUpdate.Beatmaps.First(b => b.RulesetID == 0); Beatmap beatmapToUpdate = (Beatmap)manager.GetWorkingBeatmap(setToUpdate.Beatmaps.First(b => b.RulesetID == 0)).Beatmap; BeatmapSetFileInfo fileToUpdate = setToUpdate.Files.First(f => beatmapToUpdate.BeatmapInfo.Path.Contains(f.Filename)); string oldMd5Hash = beatmapToUpdate.BeatmapInfo.MD5Hash; - using (var stream = new MemoryStream()) - { - using (var writer = new StreamWriter(stream, Encoding.UTF8, 1024, true)) - { - beatmapToUpdate.HitObjects.Clear(); - beatmapToUpdate.HitObjects.Add(new HitCircle { StartTime = 5000 }); + beatmapToUpdate.HitObjects.Clear(); + beatmapToUpdate.HitObjects.Add(new HitCircle { StartTime = 5000 }); - new LegacyBeatmapEncoder(beatmapToUpdate).Encode(writer); - } - - stream.Seek(0, SeekOrigin.Begin); - - manager.UpdateFile(setToUpdate, fileToUpdate, stream); - } + manager.Save(beatmapInfo, beatmapToUpdate); // Check that the old file reference has been removed Assert.That(manager.QueryBeatmapSet(s => s.ID == setToUpdate.ID).Files.All(f => f.ID != fileToUpdate.ID)); From 2a5e96002548e306ffe0837747029d0f3f62a4f1 Mon Sep 17 00:00:00 2001 From: Shivam Date: Mon, 8 Jun 2020 21:15:51 +0200 Subject: [PATCH 1059/1117] Move user and skin specific settings to a subclass --- .../Screens/Menu/BasicLogoVisualisation.cs | 229 ++++++++++++++++++ osu.Game/Screens/Menu/LogoVisualisation.cs | 216 +---------------- 2 files changed, 231 insertions(+), 214 deletions(-) create mode 100644 osu.Game/Screens/Menu/BasicLogoVisualisation.cs diff --git a/osu.Game/Screens/Menu/BasicLogoVisualisation.cs b/osu.Game/Screens/Menu/BasicLogoVisualisation.cs new file mode 100644 index 0000000000..ab86c38cb4 --- /dev/null +++ b/osu.Game/Screens/Menu/BasicLogoVisualisation.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 osuTK; +using osuTK.Graphics; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Batches; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.OpenGL.Vertices; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.Shaders; +using osu.Framework.Graphics.Textures; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using System; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Utils; + +namespace osu.Game.Screens.Menu +{ + public class BasicLogoVisualisation : Drawable, IHasAccentColour + { + private readonly IBindable beatmap = new Bindable(); + + /// + /// The number of bars to jump each update iteration. + /// + private const int index_change = 5; + + /// + /// The maximum length of each bar in the visualiser. Will be reduced when kiai is not activated. + /// + private const float bar_length = 600; + + /// + /// The number of bars in one rotation of the visualiser. + /// + private const int bars_per_visualiser = 200; + + /// + /// How many times we should stretch around the circumference (overlapping overselves). + /// + private const float visualiser_rounds = 5; + + /// + /// How much should each bar go down each millisecond (based on a full bar). + /// + private const float decay_per_milisecond = 0.0024f; + + /// + /// Number of milliseconds between each amplitude update. + /// + private const float time_between_updates = 50; + + /// + /// The minimum amplitude to show a bar. + /// + private const float amplitude_dead_zone = 1f / bar_length; + + private int indexOffset; + + public Color4 AccentColour { get; set; } + + private readonly float[] frequencyAmplitudes = new float[256]; + + private IShader shader; + private readonly Texture texture; + + public BasicLogoVisualisation() + { + texture = Texture.WhitePixel; + Blending = BlendingParameters.Additive; + } + + [BackgroundDependencyLoader] + private void load(ShaderManager shaders, IBindable beatmap) + { + this.beatmap.BindTo(beatmap); + shader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED); + } + + private void updateAmplitudes() + { + var track = beatmap.Value.TrackLoaded ? beatmap.Value.Track : null; + var effect = beatmap.Value.BeatmapLoaded ? beatmap.Value.Beatmap?.ControlPointInfo.EffectPointAt(track?.CurrentTime ?? Time.Current) : null; + + float[] temporalAmplitudes = track?.CurrentAmplitudes.FrequencyAmplitudes; + + for (int i = 0; i < bars_per_visualiser; i++) + { + if (track?.IsRunning ?? false) + { + float targetAmplitude = (temporalAmplitudes?[(i + indexOffset) % bars_per_visualiser] ?? 0) * (effect?.KiaiMode == true ? 1 : 0.5f); + if (targetAmplitude > frequencyAmplitudes[i]) + frequencyAmplitudes[i] = targetAmplitude; + } + else + { + int index = (i + index_change) % bars_per_visualiser; + if (frequencyAmplitudes[index] > frequencyAmplitudes[i]) + frequencyAmplitudes[i] = frequencyAmplitudes[index]; + } + } + + indexOffset = (indexOffset + index_change) % bars_per_visualiser; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + var delayed = Scheduler.AddDelayed(updateAmplitudes, time_between_updates, true); + delayed.PerformRepeatCatchUpExecutions = false; + } + + protected override void Update() + { + base.Update(); + + float decayFactor = (float)Time.Elapsed * decay_per_milisecond; + + for (int i = 0; i < bars_per_visualiser; i++) + { + //3% of extra bar length to make it a little faster when bar is almost at it's minimum + frequencyAmplitudes[i] -= decayFactor * (frequencyAmplitudes[i] + 0.03f); + if (frequencyAmplitudes[i] < 0) + frequencyAmplitudes[i] = 0; + } + + Invalidate(Invalidation.DrawNode); + } + + protected override DrawNode CreateDrawNode() => new VisualisationDrawNode(this); + + private class VisualisationDrawNode : DrawNode + { + protected new BasicLogoVisualisation Source => (BasicLogoVisualisation)base.Source; + + private IShader shader; + private Texture texture; + + // Assuming the logo is a circle, we don't need a second dimension. + private float size; + + private Color4 colour; + private float[] audioData; + + private readonly QuadBatch vertexBatch = new QuadBatch(100, 10); + + public VisualisationDrawNode(BasicLogoVisualisation source) + : base(source) + { + } + + public override void ApplyState() + { + base.ApplyState(); + + shader = Source.shader; + texture = Source.texture; + size = Source.DrawSize.X; + colour = Source.AccentColour; + audioData = Source.frequencyAmplitudes; + } + + public override void Draw(Action vertexAction) + { + base.Draw(vertexAction); + + shader.Bind(); + + Vector2 inflation = DrawInfo.MatrixInverse.ExtractScale().Xy; + + ColourInfo colourInfo = DrawColourInfo.Colour; + colourInfo.ApplyChild(colour); + + if (audioData != null) + { + for (int j = 0; j < visualiser_rounds; j++) + { + for (int i = 0; i < bars_per_visualiser; i++) + { + if (audioData[i] < amplitude_dead_zone) + continue; + + float rotation = MathUtils.DegreesToRadians(i / (float)bars_per_visualiser * 360 + j * 360 / visualiser_rounds); + float rotationCos = MathF.Cos(rotation); + float rotationSin = MathF.Sin(rotation); + // taking the cos and sin to the 0..1 range + var barPosition = new Vector2(rotationCos / 2 + 0.5f, rotationSin / 2 + 0.5f) * size; + + var barSize = new Vector2(size * MathF.Sqrt(2 * (1 - MathF.Cos(MathUtils.DegreesToRadians(360f / bars_per_visualiser)))) / 2f, bar_length * audioData[i]); + // The distance between the position and the sides of the bar. + var bottomOffset = new Vector2(-rotationSin * barSize.X / 2, rotationCos * barSize.X / 2); + // The distance between the bottom side of the bar and the top side. + var amplitudeOffset = new Vector2(rotationCos * barSize.Y, rotationSin * barSize.Y); + + var rectangle = new Quad( + Vector2Extensions.Transform(barPosition - bottomOffset, DrawInfo.Matrix), + Vector2Extensions.Transform(barPosition - bottomOffset + amplitudeOffset, DrawInfo.Matrix), + Vector2Extensions.Transform(barPosition + bottomOffset, DrawInfo.Matrix), + Vector2Extensions.Transform(barPosition + bottomOffset + amplitudeOffset, DrawInfo.Matrix) + ); + + DrawQuad( + texture, + rectangle, + colourInfo, + null, + vertexBatch.AddAction, + // barSize by itself will make it smooth more in the X axis than in the Y axis, this reverts that. + Vector2.Divide(inflation, barSize.Yx)); + } + } + } + + shader.Unbind(); + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + vertexBatch.Dispose(); + } + } + } +} diff --git a/osu.Game/Screens/Menu/LogoVisualisation.cs b/osu.Game/Screens/Menu/LogoVisualisation.cs index 0db7f2a2dc..e893ef91bb 100644 --- a/osu.Game/Screens/Menu/LogoVisualisation.cs +++ b/osu.Game/Screens/Menu/LogoVisualisation.cs @@ -1,90 +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 osuTK; using osuTK.Graphics; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Batches; -using osu.Framework.Graphics.Colour; -using osu.Framework.Graphics.OpenGL.Vertices; -using osu.Framework.Graphics.Primitives; -using osu.Framework.Graphics.Shaders; -using osu.Framework.Graphics.Textures; -using osu.Game.Beatmaps; -using osu.Game.Graphics; using osu.Game.Skinning; using osu.Game.Online.API; using osu.Game.Users; -using System; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Utils; namespace osu.Game.Screens.Menu { - public class LogoVisualisation : Drawable, IHasAccentColour + public class LogoVisualisation : BasicLogoVisualisation { - private readonly IBindable beatmap = new Bindable(); - - /// - /// The number of bars to jump each update iteration. - /// - private const int index_change = 5; - - /// - /// The maximum length of each bar in the visualiser. Will be reduced when kiai is not activated. - /// - private const float bar_length = 600; - - /// - /// The number of bars in one rotation of the visualiser. - /// - private const int bars_per_visualiser = 200; - - /// - /// How many times we should stretch around the circumference (overlapping overselves). - /// - private const float visualiser_rounds = 5; - - /// - /// How much should each bar go down each millisecond (based on a full bar). - /// - private const float decay_per_milisecond = 0.0024f; - - /// - /// Number of milliseconds between each amplitude update. - /// - private const float time_between_updates = 50; - - /// - /// The minimum amplitude to show a bar. - /// - private const float amplitude_dead_zone = 1f / bar_length; - - private int indexOffset; - - public Color4 AccentColour { get; set; } - - private readonly float[] frequencyAmplitudes = new float[256]; - - private IShader shader; - private readonly Texture texture; - private Bindable user; private Bindable skin; - public LogoVisualisation() - { - texture = Texture.WhitePixel; - Blending = BlendingParameters.Additive; - } - [BackgroundDependencyLoader] - private void load(ShaderManager shaders, IBindable beatmap, IAPIProvider api, SkinManager skinManager) + private void load(IAPIProvider api, SkinManager skinManager) { - this.beatmap.BindTo(beatmap); - shader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED); user = api.LocalUser.GetBoundCopy(); skin = skinManager.CurrentSkin.GetBoundCopy(); @@ -92,32 +26,6 @@ namespace osu.Game.Screens.Menu skin.BindValueChanged(_ => updateColour(), true); } - private void updateAmplitudes() - { - var track = beatmap.Value.TrackLoaded ? beatmap.Value.Track : null; - var effect = beatmap.Value.BeatmapLoaded ? beatmap.Value.Beatmap?.ControlPointInfo.EffectPointAt(track?.CurrentTime ?? Time.Current) : null; - - float[] temporalAmplitudes = track?.CurrentAmplitudes.FrequencyAmplitudes; - - for (int i = 0; i < bars_per_visualiser; i++) - { - if (track?.IsRunning ?? false) - { - float targetAmplitude = (temporalAmplitudes?[(i + indexOffset) % bars_per_visualiser] ?? 0) * (effect?.KiaiMode == true ? 1 : 0.5f); - if (targetAmplitude > frequencyAmplitudes[i]) - frequencyAmplitudes[i] = targetAmplitude; - } - else - { - int index = (i + index_change) % bars_per_visualiser; - if (frequencyAmplitudes[index] > frequencyAmplitudes[i]) - frequencyAmplitudes[i] = frequencyAmplitudes[index]; - } - } - - indexOffset = (indexOffset + index_change) % bars_per_visualiser; - } - private void updateColour() { Color4 defaultColour = Color4.White.Opacity(0.2f); @@ -127,125 +35,5 @@ namespace osu.Game.Screens.Menu else AccentColour = defaultColour; } - - protected override void LoadComplete() - { - base.LoadComplete(); - - var delayed = Scheduler.AddDelayed(updateAmplitudes, time_between_updates, true); - delayed.PerformRepeatCatchUpExecutions = false; - } - - protected override void Update() - { - base.Update(); - - float decayFactor = (float)Time.Elapsed * decay_per_milisecond; - - for (int i = 0; i < bars_per_visualiser; i++) - { - //3% of extra bar length to make it a little faster when bar is almost at it's minimum - frequencyAmplitudes[i] -= decayFactor * (frequencyAmplitudes[i] + 0.03f); - if (frequencyAmplitudes[i] < 0) - frequencyAmplitudes[i] = 0; - } - - Invalidate(Invalidation.DrawNode); - } - - protected override DrawNode CreateDrawNode() => new VisualisationDrawNode(this); - - private class VisualisationDrawNode : DrawNode - { - protected new LogoVisualisation Source => (LogoVisualisation)base.Source; - - private IShader shader; - private Texture texture; - - // Assuming the logo is a circle, we don't need a second dimension. - private float size; - - private Color4 colour; - private float[] audioData; - - private readonly QuadBatch vertexBatch = new QuadBatch(100, 10); - - public VisualisationDrawNode(LogoVisualisation source) - : base(source) - { - } - - public override void ApplyState() - { - base.ApplyState(); - - shader = Source.shader; - texture = Source.texture; - size = Source.DrawSize.X; - colour = Source.AccentColour; - audioData = Source.frequencyAmplitudes; - } - - public override void Draw(Action vertexAction) - { - base.Draw(vertexAction); - - shader.Bind(); - - Vector2 inflation = DrawInfo.MatrixInverse.ExtractScale().Xy; - - ColourInfo colourInfo = DrawColourInfo.Colour; - colourInfo.ApplyChild(colour); - - if (audioData != null) - { - for (int j = 0; j < visualiser_rounds; j++) - { - for (int i = 0; i < bars_per_visualiser; i++) - { - if (audioData[i] < amplitude_dead_zone) - continue; - - float rotation = MathUtils.DegreesToRadians(i / (float)bars_per_visualiser * 360 + j * 360 / visualiser_rounds); - float rotationCos = MathF.Cos(rotation); - float rotationSin = MathF.Sin(rotation); - // taking the cos and sin to the 0..1 range - var barPosition = new Vector2(rotationCos / 2 + 0.5f, rotationSin / 2 + 0.5f) * size; - - var barSize = new Vector2(size * MathF.Sqrt(2 * (1 - MathF.Cos(MathUtils.DegreesToRadians(360f / bars_per_visualiser)))) / 2f, bar_length * audioData[i]); - // The distance between the position and the sides of the bar. - var bottomOffset = new Vector2(-rotationSin * barSize.X / 2, rotationCos * barSize.X / 2); - // The distance between the bottom side of the bar and the top side. - var amplitudeOffset = new Vector2(rotationCos * barSize.Y, rotationSin * barSize.Y); - - var rectangle = new Quad( - Vector2Extensions.Transform(barPosition - bottomOffset, DrawInfo.Matrix), - Vector2Extensions.Transform(barPosition - bottomOffset + amplitudeOffset, DrawInfo.Matrix), - Vector2Extensions.Transform(barPosition + bottomOffset, DrawInfo.Matrix), - Vector2Extensions.Transform(barPosition + bottomOffset + amplitudeOffset, DrawInfo.Matrix) - ); - - DrawQuad( - texture, - rectangle, - colourInfo, - null, - vertexBatch.AddAction, - // barSize by itself will make it smooth more in the X axis than in the Y axis, this reverts that. - Vector2.Divide(inflation, barSize.Yx)); - } - } - } - - shader.Unbind(); - } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - vertexBatch.Dispose(); - } - } } } From a60bb5feac2eae08be730b5def7e9a3df82c3c1d Mon Sep 17 00:00:00 2001 From: Shivam Date: Mon, 8 Jun 2020 23:45:40 +0200 Subject: [PATCH 1060/1117] Rename baseclass, add xmldoc & change access to internal --- .../Screens/Menu/BasicLogoVisualisation.cs | 229 ----------------- osu.Game/Screens/Menu/LogoVisualisation.cs | 233 ++++++++++++++++-- .../Screens/Menu/MenuLogoVisualisation.cs | 39 +++ osu.Game/Screens/Menu/OsuLogo.cs | 4 +- 4 files changed, 254 insertions(+), 251 deletions(-) delete mode 100644 osu.Game/Screens/Menu/BasicLogoVisualisation.cs create mode 100644 osu.Game/Screens/Menu/MenuLogoVisualisation.cs diff --git a/osu.Game/Screens/Menu/BasicLogoVisualisation.cs b/osu.Game/Screens/Menu/BasicLogoVisualisation.cs deleted file mode 100644 index ab86c38cb4..0000000000 --- a/osu.Game/Screens/Menu/BasicLogoVisualisation.cs +++ /dev/null @@ -1,229 +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; -using osuTK.Graphics; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Batches; -using osu.Framework.Graphics.Colour; -using osu.Framework.Graphics.OpenGL.Vertices; -using osu.Framework.Graphics.Primitives; -using osu.Framework.Graphics.Shaders; -using osu.Framework.Graphics.Textures; -using osu.Game.Beatmaps; -using osu.Game.Graphics; -using System; -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Utils; - -namespace osu.Game.Screens.Menu -{ - public class BasicLogoVisualisation : Drawable, IHasAccentColour - { - private readonly IBindable beatmap = new Bindable(); - - /// - /// The number of bars to jump each update iteration. - /// - private const int index_change = 5; - - /// - /// The maximum length of each bar in the visualiser. Will be reduced when kiai is not activated. - /// - private const float bar_length = 600; - - /// - /// The number of bars in one rotation of the visualiser. - /// - private const int bars_per_visualiser = 200; - - /// - /// How many times we should stretch around the circumference (overlapping overselves). - /// - private const float visualiser_rounds = 5; - - /// - /// How much should each bar go down each millisecond (based on a full bar). - /// - private const float decay_per_milisecond = 0.0024f; - - /// - /// Number of milliseconds between each amplitude update. - /// - private const float time_between_updates = 50; - - /// - /// The minimum amplitude to show a bar. - /// - private const float amplitude_dead_zone = 1f / bar_length; - - private int indexOffset; - - public Color4 AccentColour { get; set; } - - private readonly float[] frequencyAmplitudes = new float[256]; - - private IShader shader; - private readonly Texture texture; - - public BasicLogoVisualisation() - { - texture = Texture.WhitePixel; - Blending = BlendingParameters.Additive; - } - - [BackgroundDependencyLoader] - private void load(ShaderManager shaders, IBindable beatmap) - { - this.beatmap.BindTo(beatmap); - shader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED); - } - - private void updateAmplitudes() - { - var track = beatmap.Value.TrackLoaded ? beatmap.Value.Track : null; - var effect = beatmap.Value.BeatmapLoaded ? beatmap.Value.Beatmap?.ControlPointInfo.EffectPointAt(track?.CurrentTime ?? Time.Current) : null; - - float[] temporalAmplitudes = track?.CurrentAmplitudes.FrequencyAmplitudes; - - for (int i = 0; i < bars_per_visualiser; i++) - { - if (track?.IsRunning ?? false) - { - float targetAmplitude = (temporalAmplitudes?[(i + indexOffset) % bars_per_visualiser] ?? 0) * (effect?.KiaiMode == true ? 1 : 0.5f); - if (targetAmplitude > frequencyAmplitudes[i]) - frequencyAmplitudes[i] = targetAmplitude; - } - else - { - int index = (i + index_change) % bars_per_visualiser; - if (frequencyAmplitudes[index] > frequencyAmplitudes[i]) - frequencyAmplitudes[i] = frequencyAmplitudes[index]; - } - } - - indexOffset = (indexOffset + index_change) % bars_per_visualiser; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - var delayed = Scheduler.AddDelayed(updateAmplitudes, time_between_updates, true); - delayed.PerformRepeatCatchUpExecutions = false; - } - - protected override void Update() - { - base.Update(); - - float decayFactor = (float)Time.Elapsed * decay_per_milisecond; - - for (int i = 0; i < bars_per_visualiser; i++) - { - //3% of extra bar length to make it a little faster when bar is almost at it's minimum - frequencyAmplitudes[i] -= decayFactor * (frequencyAmplitudes[i] + 0.03f); - if (frequencyAmplitudes[i] < 0) - frequencyAmplitudes[i] = 0; - } - - Invalidate(Invalidation.DrawNode); - } - - protected override DrawNode CreateDrawNode() => new VisualisationDrawNode(this); - - private class VisualisationDrawNode : DrawNode - { - protected new BasicLogoVisualisation Source => (BasicLogoVisualisation)base.Source; - - private IShader shader; - private Texture texture; - - // Assuming the logo is a circle, we don't need a second dimension. - private float size; - - private Color4 colour; - private float[] audioData; - - private readonly QuadBatch vertexBatch = new QuadBatch(100, 10); - - public VisualisationDrawNode(BasicLogoVisualisation source) - : base(source) - { - } - - public override void ApplyState() - { - base.ApplyState(); - - shader = Source.shader; - texture = Source.texture; - size = Source.DrawSize.X; - colour = Source.AccentColour; - audioData = Source.frequencyAmplitudes; - } - - public override void Draw(Action vertexAction) - { - base.Draw(vertexAction); - - shader.Bind(); - - Vector2 inflation = DrawInfo.MatrixInverse.ExtractScale().Xy; - - ColourInfo colourInfo = DrawColourInfo.Colour; - colourInfo.ApplyChild(colour); - - if (audioData != null) - { - for (int j = 0; j < visualiser_rounds; j++) - { - for (int i = 0; i < bars_per_visualiser; i++) - { - if (audioData[i] < amplitude_dead_zone) - continue; - - float rotation = MathUtils.DegreesToRadians(i / (float)bars_per_visualiser * 360 + j * 360 / visualiser_rounds); - float rotationCos = MathF.Cos(rotation); - float rotationSin = MathF.Sin(rotation); - // taking the cos and sin to the 0..1 range - var barPosition = new Vector2(rotationCos / 2 + 0.5f, rotationSin / 2 + 0.5f) * size; - - var barSize = new Vector2(size * MathF.Sqrt(2 * (1 - MathF.Cos(MathUtils.DegreesToRadians(360f / bars_per_visualiser)))) / 2f, bar_length * audioData[i]); - // The distance between the position and the sides of the bar. - var bottomOffset = new Vector2(-rotationSin * barSize.X / 2, rotationCos * barSize.X / 2); - // The distance between the bottom side of the bar and the top side. - var amplitudeOffset = new Vector2(rotationCos * barSize.Y, rotationSin * barSize.Y); - - var rectangle = new Quad( - Vector2Extensions.Transform(barPosition - bottomOffset, DrawInfo.Matrix), - Vector2Extensions.Transform(barPosition - bottomOffset + amplitudeOffset, DrawInfo.Matrix), - Vector2Extensions.Transform(barPosition + bottomOffset, DrawInfo.Matrix), - Vector2Extensions.Transform(barPosition + bottomOffset + amplitudeOffset, DrawInfo.Matrix) - ); - - DrawQuad( - texture, - rectangle, - colourInfo, - null, - vertexBatch.AddAction, - // barSize by itself will make it smooth more in the X axis than in the Y axis, this reverts that. - Vector2.Divide(inflation, barSize.Yx)); - } - } - } - - shader.Unbind(); - } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - vertexBatch.Dispose(); - } - } - } -} diff --git a/osu.Game/Screens/Menu/LogoVisualisation.cs b/osu.Game/Screens/Menu/LogoVisualisation.cs index e893ef91bb..6a28740d4e 100644 --- a/osu.Game/Screens/Menu/LogoVisualisation.cs +++ b/osu.Game/Screens/Menu/LogoVisualisation.cs @@ -1,39 +1,232 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osuTK; using osuTK.Graphics; -using osu.Game.Skinning; -using osu.Game.Online.API; -using osu.Game.Users; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Batches; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.OpenGL.Vertices; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.Shaders; +using osu.Framework.Graphics.Textures; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using System; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Utils; namespace osu.Game.Screens.Menu { - public class LogoVisualisation : BasicLogoVisualisation + /// + /// A visualiser that reacts to music coming from beatmaps. + /// + public class LogoVisualisation : Drawable, IHasAccentColour { - private Bindable user; - private Bindable skin; + private readonly IBindable beatmap = new Bindable(); - [BackgroundDependencyLoader] - private void load(IAPIProvider api, SkinManager skinManager) + /// + /// The number of bars to jump each update iteration. + /// + private const int index_change = 5; + + /// + /// The maximum length of each bar in the visualiser. Will be reduced when kiai is not activated. + /// + private const float bar_length = 600; + + /// + /// The number of bars in one rotation of the visualiser. + /// + private const int bars_per_visualiser = 200; + + /// + /// How many times we should stretch around the circumference (overlapping overselves). + /// + private const float visualiser_rounds = 5; + + /// + /// How much should each bar go down each millisecond (based on a full bar). + /// + private const float decay_per_milisecond = 0.0024f; + + /// + /// Number of milliseconds between each amplitude update. + /// + private const float time_between_updates = 50; + + /// + /// The minimum amplitude to show a bar. + /// + private const float amplitude_dead_zone = 1f / bar_length; + + private int indexOffset; + + public Color4 AccentColour { get; set; } + + private readonly float[] frequencyAmplitudes = new float[256]; + + private IShader shader; + private readonly Texture texture; + + public LogoVisualisation() { - user = api.LocalUser.GetBoundCopy(); - skin = skinManager.CurrentSkin.GetBoundCopy(); - - user.ValueChanged += _ => updateColour(); - skin.BindValueChanged(_ => updateColour(), true); + texture = Texture.WhitePixel; + Blending = BlendingParameters.Additive; } - private void updateColour() + [BackgroundDependencyLoader] + private void load(ShaderManager shaders, IBindable beatmap) { - Color4 defaultColour = Color4.White.Opacity(0.2f); + this.beatmap.BindTo(beatmap); + shader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED); + } - if (user.Value?.IsSupporter ?? false) - AccentColour = skin.Value.GetConfig(GlobalSkinColours.MenuGlow)?.Value ?? defaultColour; - else - AccentColour = defaultColour; + private void updateAmplitudes() + { + var track = beatmap.Value.TrackLoaded ? beatmap.Value.Track : null; + var effect = beatmap.Value.BeatmapLoaded ? beatmap.Value.Beatmap?.ControlPointInfo.EffectPointAt(track?.CurrentTime ?? Time.Current) : null; + + float[] temporalAmplitudes = track?.CurrentAmplitudes.FrequencyAmplitudes; + + for (int i = 0; i < bars_per_visualiser; i++) + { + if (track?.IsRunning ?? false) + { + float targetAmplitude = (temporalAmplitudes?[(i + indexOffset) % bars_per_visualiser] ?? 0) * (effect?.KiaiMode == true ? 1 : 0.5f); + if (targetAmplitude > frequencyAmplitudes[i]) + frequencyAmplitudes[i] = targetAmplitude; + } + else + { + int index = (i + index_change) % bars_per_visualiser; + if (frequencyAmplitudes[index] > frequencyAmplitudes[i]) + frequencyAmplitudes[i] = frequencyAmplitudes[index]; + } + } + + indexOffset = (indexOffset + index_change) % bars_per_visualiser; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + var delayed = Scheduler.AddDelayed(updateAmplitudes, time_between_updates, true); + delayed.PerformRepeatCatchUpExecutions = false; + } + + protected override void Update() + { + base.Update(); + + float decayFactor = (float)Time.Elapsed * decay_per_milisecond; + + for (int i = 0; i < bars_per_visualiser; i++) + { + //3% of extra bar length to make it a little faster when bar is almost at it's minimum + frequencyAmplitudes[i] -= decayFactor * (frequencyAmplitudes[i] + 0.03f); + if (frequencyAmplitudes[i] < 0) + frequencyAmplitudes[i] = 0; + } + + Invalidate(Invalidation.DrawNode); + } + + protected override DrawNode CreateDrawNode() => new VisualisationDrawNode(this); + + private class VisualisationDrawNode : DrawNode + { + protected new LogoVisualisation Source => (LogoVisualisation)base.Source; + + private IShader shader; + private Texture texture; + + // Assuming the logo is a circle, we don't need a second dimension. + private float size; + + private Color4 colour; + private float[] audioData; + + private readonly QuadBatch vertexBatch = new QuadBatch(100, 10); + + public VisualisationDrawNode(LogoVisualisation source) + : base(source) + { + } + + public override void ApplyState() + { + base.ApplyState(); + + shader = Source.shader; + texture = Source.texture; + size = Source.DrawSize.X; + colour = Source.AccentColour; + audioData = Source.frequencyAmplitudes; + } + + public override void Draw(Action vertexAction) + { + base.Draw(vertexAction); + + shader.Bind(); + + Vector2 inflation = DrawInfo.MatrixInverse.ExtractScale().Xy; + + ColourInfo colourInfo = DrawColourInfo.Colour; + colourInfo.ApplyChild(colour); + + if (audioData != null) + { + for (int j = 0; j < visualiser_rounds; j++) + { + for (int i = 0; i < bars_per_visualiser; i++) + { + if (audioData[i] < amplitude_dead_zone) + continue; + + float rotation = MathUtils.DegreesToRadians(i / (float)bars_per_visualiser * 360 + j * 360 / visualiser_rounds); + float rotationCos = MathF.Cos(rotation); + float rotationSin = MathF.Sin(rotation); + // taking the cos and sin to the 0..1 range + var barPosition = new Vector2(rotationCos / 2 + 0.5f, rotationSin / 2 + 0.5f) * size; + + var barSize = new Vector2(size * MathF.Sqrt(2 * (1 - MathF.Cos(MathUtils.DegreesToRadians(360f / bars_per_visualiser)))) / 2f, bar_length * audioData[i]); + // The distance between the position and the sides of the bar. + var bottomOffset = new Vector2(-rotationSin * barSize.X / 2, rotationCos * barSize.X / 2); + // The distance between the bottom side of the bar and the top side. + var amplitudeOffset = new Vector2(rotationCos * barSize.Y, rotationSin * barSize.Y); + + var rectangle = new Quad( + Vector2Extensions.Transform(barPosition - bottomOffset, DrawInfo.Matrix), + Vector2Extensions.Transform(barPosition - bottomOffset + amplitudeOffset, DrawInfo.Matrix), + Vector2Extensions.Transform(barPosition + bottomOffset, DrawInfo.Matrix), + Vector2Extensions.Transform(barPosition + bottomOffset + amplitudeOffset, DrawInfo.Matrix) + ); + + DrawQuad( + texture, + rectangle, + colourInfo, + null, + vertexBatch.AddAction, + // barSize by itself will make it smooth more in the X axis than in the Y axis, this reverts that. + Vector2.Divide(inflation, barSize.Yx)); + } + } + } + + shader.Unbind(); + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + vertexBatch.Dispose(); + } } } } diff --git a/osu.Game/Screens/Menu/MenuLogoVisualisation.cs b/osu.Game/Screens/Menu/MenuLogoVisualisation.cs new file mode 100644 index 0000000000..5eb3f1efa0 --- /dev/null +++ b/osu.Game/Screens/Menu/MenuLogoVisualisation.cs @@ -0,0 +1,39 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osuTK.Graphics; +using osu.Game.Skinning; +using osu.Game.Online.API; +using osu.Game.Users; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; + +namespace osu.Game.Screens.Menu +{ + internal class MenuLogoVisualisation : LogoVisualisation + { + private Bindable user; + private Bindable skin; + + [BackgroundDependencyLoader] + private void load(IAPIProvider api, SkinManager skinManager) + { + user = api.LocalUser.GetBoundCopy(); + skin = skinManager.CurrentSkin.GetBoundCopy(); + + user.ValueChanged += _ => updateColour(); + skin.BindValueChanged(_ => updateColour(), true); + } + + private void updateColour() + { + Color4 defaultColour = Color4.White.Opacity(0.2f); + + if (user.Value?.IsSupporter ?? false) + AccentColour = skin.Value.GetConfig(GlobalSkinColours.MenuGlow)?.Value ?? defaultColour; + else + AccentColour = defaultColour; + } + } +} diff --git a/osu.Game/Screens/Menu/OsuLogo.cs b/osu.Game/Screens/Menu/OsuLogo.cs index 800520100e..9cadfd7df6 100644 --- a/osu.Game/Screens/Menu/OsuLogo.cs +++ b/osu.Game/Screens/Menu/OsuLogo.cs @@ -38,7 +38,7 @@ namespace osu.Game.Screens.Menu private readonly Container logoBeatContainer; private readonly Container logoAmplitudeContainer; private readonly Container logoHoverContainer; - private readonly LogoVisualisation visualizer; + private readonly MenuLogoVisualisation visualizer; private readonly IntroSequence intro; @@ -139,7 +139,7 @@ namespace osu.Game.Screens.Menu AutoSizeAxes = Axes.Both, Children = new Drawable[] { - visualizer = new LogoVisualisation + visualizer = new MenuLogoVisualisation { RelativeSizeAxes = Axes.Both, Origin = Anchor.Centre, From 44dd7d65bee35d52cce4de57ab79080decb3dce9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 9 Jun 2020 18:21:37 +0900 Subject: [PATCH 1061/1117] Fix duplicate scores showing --- osu.Game/Online/API/Requests/SubmitRoomScoreRequest.cs | 2 +- osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs | 10 +++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/osu.Game/Online/API/Requests/SubmitRoomScoreRequest.cs b/osu.Game/Online/API/Requests/SubmitRoomScoreRequest.cs index 50b62cd6ed..8eb2952159 100644 --- a/osu.Game/Online/API/Requests/SubmitRoomScoreRequest.cs +++ b/osu.Game/Online/API/Requests/SubmitRoomScoreRequest.cs @@ -8,7 +8,7 @@ using osu.Game.Scoring; namespace osu.Game.Online.API.Requests { - public class SubmitRoomScoreRequest : APIRequest + public class SubmitRoomScoreRequest : APIRequest { private readonly int scoreId; private readonly int roomId; diff --git a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs index fbe9e3480f..cf0197d26b 100644 --- a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs +++ b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs @@ -97,22 +97,18 @@ namespace osu.Game.Screens.Multi.Play } protected override ScoreInfo CreateScore() - { - submitScore(); - return base.CreateScore(); - } - - private void submitScore() { var score = base.CreateScore(); - score.TotalScore = (int)Math.Round(ScoreProcessor.GetStandardisedScore()); Debug.Assert(token != null); var request = new SubmitRoomScoreRequest(token.Value, roomId.Value ?? 0, playlistItem.ID, score); + request.Success += s => score.OnlineScoreID = s.ID; request.Failure += e => Logger.Error(e, "Failed to submit score"); api.Queue(request); + + return score; } protected override void Dispose(bool isDisposing) From 4fd5ff61eb1b6a01543689c04b2f84daf9160dd0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 9 Jun 2020 18:53:55 +0900 Subject: [PATCH 1062/1117] Add loading spinner --- .../TestSceneTimeshiftResultsScreen.cs | 67 +++++++++++++++---- .../Multi/Ranking/TimeshiftResultsScreen.cs | 29 +++++++- 2 files changed, 81 insertions(+), 15 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs index 8559e7e2f4..66ebf9abda 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Threading.Tasks; using NUnit.Framework; using osu.Game.Online.API; using osu.Game.Online.API.Requests; @@ -18,19 +19,52 @@ namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneTimeshiftResultsScreen : ScreenTestScene { + private bool roomsReceived; + + [SetUp] + public void Setup() => Schedule(() => + { + roomsReceived = false; + bindHandler(); + }); + [Test] public void TestShowResultsWithScore() { createResults(new TestScoreInfo(new OsuRuleset().RulesetInfo)); + AddWaitStep("wait for display", 5); } [Test] public void TestShowResultsNullScore() { createResults(null); + AddWaitStep("wait for display", 5); + } + + [Test] + public void TestShowResultsNullScoreWithDelay() + { + AddStep("bind delayed handler", () => bindHandler(3000)); + createResults(null); + AddUntilStep("wait for rooms to be received", () => roomsReceived); + AddWaitStep("wait for display", 5); } private void createResults(ScoreInfo score) + { + AddStep("load results", () => + { + LoadScreen(new TimeshiftResultsScreen(score, 1, new PlaylistItem + { + Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo }, + Ruleset = { Value = new OsuRuleset().RulesetInfo } + })); + }); + + } + + private void bindHandler(double delay = 0) { var roomScores = new List(); @@ -61,26 +95,31 @@ namespace osu.Game.Tests.Visual.Multiplayer }); } - AddStep("bind request handler", () => ((DummyAPIAccess)API).HandleRequest = request => + ((DummyAPIAccess)API).HandleRequest = request => { switch (request) { case GetRoomPlaylistScoresRequest r: - r.TriggerSuccess(new RoomPlaylistScores { Scores = roomScores }); + if (delay == 0) + success(); + else + { + Task.Run(async () => + { + await Task.Delay(TimeSpan.FromMilliseconds(delay)); + Schedule(success); + }); + } + + void success() + { + r.TriggerSuccess(new RoomPlaylistScores { Scores = roomScores }); + roomsReceived = true; + } + break; } - }); - - AddStep("load results", () => - { - LoadScreen(new TimeshiftResultsScreen(score, 1, new PlaylistItem - { - Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo }, - Ruleset = { Value = new OsuRuleset().RulesetInfo } - })); - }); - - AddWaitStep("wait for display", 10); + }; } } } diff --git a/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs index d95cee2ab8..5cafc974f1 100644 --- a/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs +++ b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs @@ -4,6 +4,10 @@ using System; using System.Collections.Generic; using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.Multiplayer; @@ -17,6 +21,8 @@ namespace osu.Game.Screens.Multi.Ranking private readonly int roomId; private readonly PlaylistItem playlistItem; + private LoadingSpinner loadingLayer; + public TimeshiftResultsScreen(ScoreInfo score, int roomId, PlaylistItem playlistItem, bool allowRetry = true) : base(score, allowRetry) { @@ -24,10 +30,31 @@ namespace osu.Game.Screens.Multi.Ranking this.playlistItem = playlistItem; } + [BackgroundDependencyLoader] + private void load() + { + AddInternal(loadingLayer = new LoadingLayer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + X = -10, + State = { Value = Score == null ? Visibility.Visible : Visibility.Hidden }, + Padding = new MarginPadding { Bottom = TwoLayerButton.SIZE_EXTENDED.Y } + }); + } + protected override APIRequest FetchScores(Action> scoresCallback) { var req = new GetRoomPlaylistScoresRequest(roomId, playlistItem.ID); - req.Success += r => scoresCallback?.Invoke(r.Scores.Where(s => s.ID != Score?.OnlineScoreID).Select(s => s.CreateScoreInfo(playlistItem))); + + req.Success += r => + { + scoresCallback?.Invoke(r.Scores.Where(s => s.ID != Score?.OnlineScoreID).Select(s => s.CreateScoreInfo(playlistItem))); + loadingLayer.Hide(); + }; + + req.Failure += _ => loadingLayer.Hide(); + return req; } } From 05b1edb9d88135667cf9893a414fa08e4cc4dad6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 9 Jun 2020 19:01:02 +0900 Subject: [PATCH 1063/1117] Fix incorrect beatmap showing --- .../Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs | 1 - .../Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs | 6 ++---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs index 66ebf9abda..9fc7c336cb 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs @@ -61,7 +61,6 @@ namespace osu.Game.Tests.Visual.Multiplayer Ruleset = { Value = new OsuRuleset().RulesetInfo } })); }); - } private void bindHandler(double delay = 0) diff --git a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs index 81d5d113ae..b06ef8ae83 100644 --- a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs @@ -4,11 +4,9 @@ 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.Localisation; -using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -52,9 +50,9 @@ namespace osu.Game.Screens.Ranking.Expanded } [BackgroundDependencyLoader] - private void load(Bindable working) + private void load() { - var beatmap = working.Value.BeatmapInfo; + var beatmap = score.Beatmap; var metadata = beatmap.Metadata; var creator = metadata.Author?.Username; From ab10732a788c8dbe09d658e19f35c2707c70c8cd Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 9 Jun 2020 22:13:48 +0900 Subject: [PATCH 1064/1117] Remove usages of null-forgiving operator --- osu.Game.Tests/Chat/MessageFormatterTests.cs | 15 ++++++++++----- .../Visual/UserInterface/TestSceneOsuIcon.cs | 8 +++++++- osu.Game/Online/API/APIAccess.cs | 3 ++- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Chat/MessageFormatterTests.cs b/osu.Game.Tests/Chat/MessageFormatterTests.cs index d1a859c84b..600c820ce1 100644 --- a/osu.Game.Tests/Chat/MessageFormatterTests.cs +++ b/osu.Game.Tests/Chat/MessageFormatterTests.cs @@ -428,23 +428,28 @@ namespace osu.Game.Tests.Chat Assert.AreEqual(5, result.Links.Count); Link f = result.Links.Find(l => l.Url == "https://osu.ppy.sh/wiki/wiki links"); - Assert.AreEqual(44, f!.Index); + Assert.That(f, Is.Not.Null); + Assert.AreEqual(44, f.Index); Assert.AreEqual(10, f.Length); f = result.Links.Find(l => l.Url == "http://www.simple-test.com"); - Assert.AreEqual(10, f!.Index); + Assert.That(f, Is.Not.Null); + Assert.AreEqual(10, f.Index); Assert.AreEqual(11, f.Length); f = result.Links.Find(l => l.Url == "http://google.com"); - Assert.AreEqual(97, f!.Index); + Assert.That(f, Is.Not.Null); + Assert.AreEqual(97, f.Index); Assert.AreEqual(4, f.Length); f = result.Links.Find(l => l.Url == "https://osu.ppy.sh"); - Assert.AreEqual(78, f!.Index); + Assert.That(f, Is.Not.Null); + Assert.AreEqual(78, f.Index); Assert.AreEqual(18, f.Length); f = result.Links.Find(l => l.Url == "\uD83D\uDE12"); - Assert.AreEqual(101, f!.Index); + Assert.That(f, Is.Not.Null); + Assert.AreEqual(101, f.Index); Assert.AreEqual(3, f.Length); } diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuIcon.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuIcon.cs index 246eb119e8..c5374d50ab 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuIcon.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuIcon.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.Diagnostics; using System.Reflection; using NUnit.Framework; using osu.Framework.Extensions.IEnumerableExtensions; @@ -45,7 +46,12 @@ namespace osu.Game.Tests.Visual.UserInterface }); foreach (var p in typeof(OsuIcon).GetProperties(BindingFlags.Public | BindingFlags.Static)) - flow.Add(new Icon($"{nameof(OsuIcon)}.{p.Name}", (IconUsage)p.GetValue(null)!)); + { + var propValue = p.GetValue(null); + Debug.Assert(propValue != null); + + flow.Add(new Icon($"{nameof(OsuIcon)}.{p.Name}", (IconUsage)propValue)); + } AddStep("toggle shadows", () => flow.Children.ForEach(i => i.SpriteIcon.Shadow = !i.SpriteIcon.Shadow)); AddStep("change icons", () => flow.Children.ForEach(i => i.SpriteIcon.Icon = new IconUsage((char)(i.SpriteIcon.Icon.Icon + 1)))); diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index f9e2da9af8..4ea5c192fe 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -11,6 +11,7 @@ using System.Threading.Tasks; using Newtonsoft.Json.Linq; using osu.Framework.Bindables; using osu.Framework.Extensions.ExceptionExtensions; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Logging; using osu.Game.Configuration; @@ -250,7 +251,7 @@ namespace osu.Game.Online.API { try { - return JObject.Parse(req.GetResponseString()).SelectToken("form_error", true)!.ToObject(); + return JObject.Parse(req.GetResponseString()).SelectToken("form_error", true).AsNonNull().ToObject(); } catch { From 7274213cce3d5a1776bcc715d0f90e73b235a808 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 9 Jun 2020 23:30:42 +0900 Subject: [PATCH 1065/1117] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 07be3ab0d2..596e5bfa8b 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 8213719c01..1d3bafbfd6 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -24,7 +24,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index fd13455c63..ad7850599b 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -80,7 +80,7 @@ - + From 7dc19220e51947f92b9c1dfe381d96098ffe637e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 9 Jun 2020 23:38:54 +0900 Subject: [PATCH 1066/1117] Apply new resharper formatting fixes --- osu.Game.Rulesets.Osu.Tests/TestSceneSliderSnaking.cs | 11 ++++++----- .../Screens/Gameplay/Components/TeamScoreDisplay.cs | 5 ++++- osu.Game/Rulesets/Mods/ModEasy.cs | 4 +++- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSnaking.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSnaking.cs index f5b20fd1c5..a69646507a 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSnaking.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSnaking.cs @@ -61,7 +61,9 @@ namespace osu.Game.Rulesets.Osu.Tests private DrawableSlider slider; [SetUpSteps] - public override void SetUpSteps() { } + public override void SetUpSteps() + { + } [TestCase(0)] [TestCase(1)] @@ -132,10 +134,9 @@ namespace osu.Game.Rulesets.Osu.Tests checkPositionChange(16600, sliderRepeat, positionDecreased); } - private void retrieveDrawableSlider(int index) => AddStep($"retrieve {(index + 1).ToOrdinalWords()} slider", () => - { - slider = (DrawableSlider)Player.DrawableRuleset.Playfield.AllHitObjects.ElementAt(index); - }); + private void retrieveDrawableSlider(int index) => + AddStep($"retrieve {(index + 1).ToOrdinalWords()} slider", () => + slider = (DrawableSlider)Player.DrawableRuleset.Playfield.AllHitObjects.ElementAt(index)); private void ensureSnakingIn(double startTime) => checkPositionChange(startTime, sliderEnd, positionIncreased); private void ensureNoSnakingIn(double startTime) => checkPositionChange(startTime, sliderEnd, positionRemainsSame); diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/TeamScoreDisplay.cs b/osu.Game.Tournament/Screens/Gameplay/Components/TeamScoreDisplay.cs index 3e60a03f92..da55ba53ea 100644 --- a/osu.Game.Tournament/Screens/Gameplay/Components/TeamScoreDisplay.cs +++ b/osu.Game.Tournament/Screens/Gameplay/Components/TeamScoreDisplay.cs @@ -21,7 +21,10 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components private TeamDisplay teamDisplay; - public bool ShowScore { set => teamDisplay.ShowScore = value; } + public bool ShowScore + { + set => teamDisplay.ShowScore = value; + } public TeamScoreDisplay(TeamColour teamColour) { diff --git a/osu.Game/Rulesets/Mods/ModEasy.cs b/osu.Game/Rulesets/Mods/ModEasy.cs index 7cf9656810..c6f3930029 100644 --- a/osu.Game/Rulesets/Mods/ModEasy.cs +++ b/osu.Game/Rulesets/Mods/ModEasy.cs @@ -35,7 +35,9 @@ namespace osu.Game.Rulesets.Mods private BindableNumber health; - public void ReadFromDifficulty(BeatmapDifficulty difficulty) { } + public void ReadFromDifficulty(BeatmapDifficulty difficulty) + { + } public void ApplyToDifficulty(BeatmapDifficulty difficulty) { From 880a1272288d04cf7eea92b94b39ca7037375e48 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 10 Jun 2020 00:08:48 +0900 Subject: [PATCH 1067/1117] Use async overload --- osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index f145d90356..0151678db3 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -174,7 +174,7 @@ namespace osu.Game.Tests.Beatmaps.IO // arbitrary write to non-hashed file using (var sw = new FileInfo(Directory.GetFiles(extractedFolder, "*.mp3").First()).AppendText()) - sw.WriteLine("text"); + await sw.WriteLineAsync("text"); using (var zip = ZipArchive.Create()) { From 3ae1df07b0c13acefc6a700f07f7a9b74da4a102 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 10 Jun 2020 00:09:29 +0900 Subject: [PATCH 1068/1117] Fix a couple more new formatting issues --- .../Screens/Gameplay/Components/TeamDisplay.cs | 5 ++++- osu.Game/Rulesets/Mods/ModHardRock.cs | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/TeamDisplay.cs b/osu.Game.Tournament/Screens/Gameplay/Components/TeamDisplay.cs index 29908e8e7c..b01c93ae03 100644 --- a/osu.Game.Tournament/Screens/Gameplay/Components/TeamDisplay.cs +++ b/osu.Game.Tournament/Screens/Gameplay/Components/TeamDisplay.cs @@ -14,7 +14,10 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components { private readonly TeamScore score; - public bool ShowScore { set => score.FadeTo(value ? 1 : 0, 200); } + public bool ShowScore + { + set => score.FadeTo(value ? 1 : 0, 200); + } public TeamDisplay(TournamentTeam team, TeamColour colour, Bindable currentTeamScore, int pointsToWin) : base(team) diff --git a/osu.Game/Rulesets/Mods/ModHardRock.cs b/osu.Game/Rulesets/Mods/ModHardRock.cs index 58c9a58408..0e589735c1 100644 --- a/osu.Game/Rulesets/Mods/ModHardRock.cs +++ b/osu.Game/Rulesets/Mods/ModHardRock.cs @@ -17,7 +17,9 @@ namespace osu.Game.Rulesets.Mods public override string Description => "Everything just got a bit harder..."; public override Type[] IncompatibleMods => new[] { typeof(ModEasy), typeof(ModDifficultyAdjust) }; - public void ReadFromDifficulty(BeatmapDifficulty difficulty) { } + public void ReadFromDifficulty(BeatmapDifficulty difficulty) + { + } public void ApplyToDifficulty(BeatmapDifficulty difficulty) { From e57a2294743e216415aeffa3cfdc12514721dd49 Mon Sep 17 00:00:00 2001 From: Shivam Date: Tue, 9 Jun 2020 20:22:30 +0200 Subject: [PATCH 1069/1117] Move all the graphics related code to TournamentGame --- osu.Game.Tournament/TournamentGame.cs | 97 +++++++++++++++++++++-- osu.Game.Tournament/TournamentGameBase.cs | 94 +--------------------- 2 files changed, 92 insertions(+), 99 deletions(-) diff --git a/osu.Game.Tournament/TournamentGame.cs b/osu.Game.Tournament/TournamentGame.cs index 78bb66d553..8a0190b902 100644 --- a/osu.Game.Tournament/TournamentGame.cs +++ b/osu.Game.Tournament/TournamentGame.cs @@ -1,11 +1,19 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Drawing; using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Configuration; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Colour; using osu.Game.Graphics.Cursor; using osu.Game.Tournament.Models; +using osu.Game.Graphics; +using osuTK; using osuTK.Graphics; namespace osu.Game.Tournament @@ -21,17 +29,94 @@ namespace osu.Game.Tournament public static readonly Color4 ELEMENT_FOREGROUND_COLOUR = Color4Extensions.FromHex("#000"); public static readonly Color4 TEXT_COLOUR = Color4Extensions.FromHex("#fff"); + private Drawable heightWarning; + private Bindable windowSize; + + [BackgroundDependencyLoader] + private void load(FrameworkConfigManager frameworkConfig) + { + windowSize = frameworkConfig.GetBindable(FrameworkSetting.WindowedSize); + windowSize.BindValueChanged(size => ScheduleAfterChildren(() => + { + var minWidth = (int)(size.NewValue.Height / 768f * TournamentSceneManager.REQUIRED_WIDTH) - 1; + + heightWarning.Alpha = size.NewValue.Width < minWidth ? 1 : 0; + }), true); + + AddRange(new[] + { + new Container + { + CornerRadius = 10, + Depth = float.MinValue, + Position = new Vector2(5), + Masking = true, + AutoSizeAxes = Axes.Both, + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + Children = new Drawable[] + { + new Box + { + Colour = OsuColour.Gray(0.2f), + RelativeSizeAxes = Axes.Both, + }, + new TourneyButton + { + Text = "Save Changes", + Width = 140, + Height = 50, + Padding = new MarginPadding + { + Top = 10, + Left = 10, + }, + Margin = new MarginPadding + { + Right = 10, + Bottom = 10, + }, + Action = SaveChanges, + }, + } + }, + heightWarning = new Container + { + Masking = true, + CornerRadius = 5, + Depth = float.MinValue, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + Colour = Color4.Red, + RelativeSizeAxes = Axes.Both, + }, + new TournamentSpriteText + { + Text = "Please make the window wider", + Font = OsuFont.Torus.With(weight: FontWeight.Bold), + Colour = Color4.White, + Padding = new MarginPadding(20) + } + } + }, + new OsuContextMenuContainer + { + RelativeSizeAxes = Axes.Both, + Child = new TournamentSceneManager() + } + }); + } protected override void LoadComplete() { base.LoadComplete(); - Add(new OsuContextMenuContainer - { - RelativeSizeAxes = Axes.Both, - Child = new TournamentSceneManager() - }); - + MenuCursorContainer.Cursor.AlwaysPresent = true; // required for tooltip display // we don't want to show the menu cursor as it would appear on stream output. MenuCursorContainer.Cursor.Alpha = 0; } diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index 85db9e61fb..cc7bb863ed 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -2,28 +2,19 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Drawing; using System.IO; using System.Linq; using Newtonsoft.Json; using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Textures; using osu.Framework.Input; using osu.Framework.IO.Stores; using osu.Framework.Platform; using osu.Game.Beatmaps; -using osu.Game.Graphics; using osu.Game.Online.API.Requests; using osu.Game.Tournament.IPC; using osu.Game.Tournament.Models; using osu.Game.Users; -using osuTK; -using osuTK.Graphics; using osuTK.Input; namespace osu.Game.Tournament @@ -40,19 +31,15 @@ namespace osu.Game.Tournament private TournamentStorage tournamentStorage; private DependencyContainer dependencies; - - private Bindable windowSize; private FileBasedIPC ipc; - private Drawable heightWarning; - protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { return dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); } [BackgroundDependencyLoader] - private void load(Storage storage, FrameworkConfigManager frameworkConfig) + private void load(Storage storage) { Resources.AddStore(new DllResourceStore(typeof(TournamentGameBase).Assembly)); @@ -62,83 +49,12 @@ namespace osu.Game.Tournament this.storage = storage; - windowSize = frameworkConfig.GetBindable(FrameworkSetting.WindowedSize); - windowSize.BindValueChanged(size => ScheduleAfterChildren(() => - { - var minWidth = (int)(size.NewValue.Height / 768f * TournamentSceneManager.REQUIRED_WIDTH) - 1; - - heightWarning.Alpha = size.NewValue.Width < minWidth ? 1 : 0; - }), true); - readBracket(); ladder.CurrentMatch.Value = ladder.Matches.FirstOrDefault(p => p.Current.Value); dependencies.CacheAs(ipc = new FileBasedIPC()); Add(ipc); - - AddRange(new[] - { - new Container - { - CornerRadius = 10, - Depth = float.MinValue, - Position = new Vector2(5), - Masking = true, - AutoSizeAxes = Axes.Both, - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - Children = new Drawable[] - { - new Box - { - Colour = OsuColour.Gray(0.2f), - RelativeSizeAxes = Axes.Both, - }, - new TourneyButton - { - Text = "Save Changes", - Width = 140, - Height = 50, - Padding = new MarginPadding - { - Top = 10, - Left = 10, - }, - Margin = new MarginPadding - { - Right = 10, - Bottom = 10, - }, - Action = SaveChanges, - }, - } - }, - heightWarning = new Container - { - Masking = true, - CornerRadius = 5, - Depth = float.MinValue, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - AutoSizeAxes = Axes.Both, - Children = new Drawable[] - { - new Box - { - Colour = Color4.Red, - RelativeSizeAxes = Axes.Both, - }, - new TournamentSpriteText - { - Text = "Please make the window wider", - Font = OsuFont.Torus.With(weight: FontWeight.Bold), - Colour = Color4.White, - Padding = new MarginPadding(20) - } - } - }, - }); } private void readBracket() @@ -313,14 +229,6 @@ namespace osu.Game.Tournament API.Queue(req); } - protected override void LoadComplete() - { - MenuCursorContainer.Cursor.AlwaysPresent = true; // required for tooltip display - MenuCursorContainer.Cursor.Alpha = 0; - - base.LoadComplete(); - } - protected virtual void SaveChanges() { foreach (var r in ladder.Rounds) From af05ee67cbe67e0577a9cdb8bad11aa01dbfd22a Mon Sep 17 00:00:00 2001 From: Shivam Date: Tue, 9 Jun 2020 20:30:15 +0200 Subject: [PATCH 1070/1117] move base.loadcomplete to the bottom --- osu.Game.Tournament/TournamentGame.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tournament/TournamentGame.cs b/osu.Game.Tournament/TournamentGame.cs index 8a0190b902..3392440902 100644 --- a/osu.Game.Tournament/TournamentGame.cs +++ b/osu.Game.Tournament/TournamentGame.cs @@ -114,11 +114,12 @@ namespace osu.Game.Tournament protected override void LoadComplete() { - base.LoadComplete(); - MenuCursorContainer.Cursor.AlwaysPresent = true; // required for tooltip display + // we don't want to show the menu cursor as it would appear on stream output. MenuCursorContainer.Cursor.Alpha = 0; + + base.LoadComplete(); } } } From c9b4fa92f57b6c6023cbedb9c43aa846b1c7b855 Mon Sep 17 00:00:00 2001 From: Shivam Date: Tue, 9 Jun 2020 20:40:54 +0200 Subject: [PATCH 1071/1117] Hide in-game cursor manually in the testbrowser --- osu.Game.Tournament.Tests/TournamentTestBrowser.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Tournament.Tests/TournamentTestBrowser.cs b/osu.Game.Tournament.Tests/TournamentTestBrowser.cs index f7ad757926..3bc719be7c 100644 --- a/osu.Game.Tournament.Tests/TournamentTestBrowser.cs +++ b/osu.Game.Tournament.Tests/TournamentTestBrowser.cs @@ -19,6 +19,9 @@ namespace osu.Game.Tournament.Tests Depth = 10 }, AddInternal); + MenuCursorContainer.Cursor.AlwaysPresent = true; + MenuCursorContainer.Cursor.Alpha = 0; + // Have to construct this here, rather than in the constructor, because // we depend on some dependencies to be loaded within OsuGameBase.load(). Add(new TestBrowser()); From aacacd75f08f289fa77cc5d9be211145b8378b7d Mon Sep 17 00:00:00 2001 From: Shivam Date: Tue, 9 Jun 2020 21:14:05 +0200 Subject: [PATCH 1072/1117] Remove abstract from the class --- osu.Game.Tournament/TournamentGameBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index cc7bb863ed..bb8c134ecb 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -20,7 +20,7 @@ using osuTK.Input; namespace osu.Game.Tournament { [Cached(typeof(TournamentGameBase))] - public abstract class TournamentGameBase : OsuGameBase + public class TournamentGameBase : OsuGameBase { private const string bracket_filename = "bracket.json"; From 0f39558da2aa09555c0a50e5bb74b32325d07a16 Mon Sep 17 00:00:00 2001 From: Shivam Date: Wed, 10 Jun 2020 08:04:34 +0200 Subject: [PATCH 1073/1117] Apply review comment --- osu.Game.Tournament.Tests/TournamentTestBrowser.cs | 3 --- osu.Game.Tournament/TournamentGame.cs | 10 ---------- osu.Game.Tournament/TournamentGameBase.cs | 9 +++++++++ 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tournament.Tests/TournamentTestBrowser.cs b/osu.Game.Tournament.Tests/TournamentTestBrowser.cs index 3bc719be7c..f7ad757926 100644 --- a/osu.Game.Tournament.Tests/TournamentTestBrowser.cs +++ b/osu.Game.Tournament.Tests/TournamentTestBrowser.cs @@ -19,9 +19,6 @@ namespace osu.Game.Tournament.Tests Depth = 10 }, AddInternal); - MenuCursorContainer.Cursor.AlwaysPresent = true; - MenuCursorContainer.Cursor.Alpha = 0; - // Have to construct this here, rather than in the constructor, because // we depend on some dependencies to be loaded within OsuGameBase.load(). Add(new TestBrowser()); diff --git a/osu.Game.Tournament/TournamentGame.cs b/osu.Game.Tournament/TournamentGame.cs index 3392440902..7b1a174c1e 100644 --- a/osu.Game.Tournament/TournamentGame.cs +++ b/osu.Game.Tournament/TournamentGame.cs @@ -111,15 +111,5 @@ namespace osu.Game.Tournament } }); } - - protected override void LoadComplete() - { - MenuCursorContainer.Cursor.AlwaysPresent = true; // required for tooltip display - - // we don't want to show the menu cursor as it would appear on stream output. - MenuCursorContainer.Cursor.Alpha = 0; - - base.LoadComplete(); - } } } diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index bb8c134ecb..0160065cc4 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -228,6 +228,15 @@ namespace osu.Game.Tournament API.Queue(req); } + protected override void LoadComplete() + { + MenuCursorContainer.Cursor.AlwaysPresent = true; // required for tooltip display + + // we don't want to show the menu cursor as it would appear on stream output. + MenuCursorContainer.Cursor.Alpha = 0; + + base.LoadComplete(); + } protected virtual void SaveChanges() { From a43e1a0ae345722b922cd7b3bb0b533969657103 Mon Sep 17 00:00:00 2001 From: Shivam Date: Wed, 10 Jun 2020 08:41:13 +0200 Subject: [PATCH 1074/1117] Remove whitespace --- osu.Game.Tournament/TournamentGameBase.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index 0160065cc4..d17b93bf5d 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -228,6 +228,7 @@ namespace osu.Game.Tournament API.Queue(req); } + protected override void LoadComplete() { MenuCursorContainer.Cursor.AlwaysPresent = true; // required for tooltip display From 4fb71eeb20dcf96a46a54d025eca6689489bf2a5 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 10 Jun 2020 18:23:31 +0300 Subject: [PATCH 1075/1117] Move setting up the ruleset bindable to top-base test scene --- osu.Game/Tests/Visual/EditorTestScene.cs | 1 - osu.Game/Tests/Visual/OsuTestScene.cs | 2 +- osu.Game/Tests/Visual/PlayerTestScene.cs | 14 ++------------ osu.Game/Tests/Visual/SkinnableTestScene.cs | 7 +------ 4 files changed, 4 insertions(+), 20 deletions(-) diff --git a/osu.Game/Tests/Visual/EditorTestScene.cs b/osu.Game/Tests/Visual/EditorTestScene.cs index 4f9a5b53b8..cd08f4712a 100644 --- a/osu.Game/Tests/Visual/EditorTestScene.cs +++ b/osu.Game/Tests/Visual/EditorTestScene.cs @@ -19,7 +19,6 @@ namespace osu.Game.Tests.Visual [BackgroundDependencyLoader] private void load() { - Ruleset.Value = CreateEditorRuleset().RulesetInfo; Beatmap.Value = CreateWorkingBeatmap(Ruleset.Value); } diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs index e5d5442074..6d0fc199c4 100644 --- a/osu.Game/Tests/Visual/OsuTestScene.cs +++ b/osu.Game/Tests/Visual/OsuTestScene.cs @@ -146,7 +146,7 @@ namespace osu.Game.Tests.Visual [BackgroundDependencyLoader] private void load(RulesetStore rulesets) { - Ruleset.Value = rulesets.AvailableRulesets.First(); + Ruleset.Value = CreateRuleset()?.RulesetInfo ?? rulesets.AvailableRulesets.First(); } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Tests/Visual/PlayerTestScene.cs b/osu.Game/Tests/Visual/PlayerTestScene.cs index 05b1eea6b3..2c46e7f6d3 100644 --- a/osu.Game/Tests/Visual/PlayerTestScene.cs +++ b/osu.Game/Tests/Visual/PlayerTestScene.cs @@ -24,20 +24,9 @@ namespace osu.Game.Tests.Visual protected OsuConfigManager LocalConfig; - private readonly Ruleset ruleset; - - protected PlayerTestScene() - { - ruleset = CreatePlayerRuleset(); - } - [BackgroundDependencyLoader] private void load() { - // There are test scenes using current value of the ruleset bindable - // on their BDLs (example in TestSceneSliderSnaking's BDL) - Ruleset.Value = ruleset.RulesetInfo; - Dependencies.Cache(LocalConfig = new OsuConfigManager(LocalStorage)); LocalConfig.GetBindable(OsuSetting.DimLevel).Value = 1.0; } @@ -58,7 +47,7 @@ namespace osu.Game.Tests.Visual action?.Invoke(); - AddStep(ruleset.Description, LoadPlayer); + AddStep(CreatePlayerRuleset().Description, LoadPlayer); AddUntilStep("player loaded", () => Player.IsLoaded && Player.Alpha == 1); } @@ -68,6 +57,7 @@ namespace osu.Game.Tests.Visual protected void LoadPlayer() { + var ruleset = Ruleset.Value.CreateInstance(); var beatmap = CreateBeatmap(ruleset.RulesetInfo); Beatmap.Value = CreateWorkingBeatmap(beatmap); diff --git a/osu.Game/Tests/Visual/SkinnableTestScene.cs b/osu.Game/Tests/Visual/SkinnableTestScene.cs index 41147d3768..ea7cdaaac6 100644 --- a/osu.Game/Tests/Visual/SkinnableTestScene.cs +++ b/osu.Game/Tests/Visual/SkinnableTestScene.cs @@ -23,8 +23,6 @@ namespace osu.Game.Tests.Visual { public abstract class SkinnableTestScene : OsuGridTestScene { - private readonly Ruleset ruleset; - private Skin metricsSkin; private Skin defaultSkin; private Skin specialSkin; @@ -33,14 +31,11 @@ namespace osu.Game.Tests.Visual protected SkinnableTestScene() : base(2, 3) { - ruleset = CreateRulesetForSkinProvider(); } [BackgroundDependencyLoader] private void load(AudioManager audio, SkinManager skinManager) { - Ruleset.Value = ruleset.RulesetInfo; - var dllStore = new DllResourceStore(DynamicCompilationOriginal.GetType().Assembly); metricsSkin = new TestLegacySkin(new SkinInfo { Name = "metrics-skin" }, new NamespacedResourceStore(dllStore, "Resources/metrics_skin"), audio, true); @@ -110,7 +105,7 @@ namespace osu.Game.Tests.Visual { new OutlineBox { Alpha = autoSize ? 1 : 0 }, mainProvider.WithChild( - new SkinProvidingContainer(ruleset.CreateLegacySkinProvider(mainProvider, beatmap)) + new SkinProvidingContainer(Ruleset.Value.CreateInstance().CreateLegacySkinProvider(mainProvider, beatmap)) { Child = created, RelativeSizeAxes = !autoSize ? Axes.Both : Axes.None, From b89dcb6a77de715a84faa28fd1b91fc37b167e5b Mon Sep 17 00:00:00 2001 From: Shane Woolcock Date: Thu, 11 Jun 2020 13:02:47 +0930 Subject: [PATCH 1076/1117] Fix cursor not hiding with SDL2 backend --- osu.Desktop/OsuGameDesktop.cs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 5f74883803..bca30f3f9e 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -122,14 +122,22 @@ namespace osu.Desktop { base.SetHost(host); - if (host.Window is DesktopGameWindow desktopWindow) + switch (host.Window) { - desktopWindow.CursorState |= CursorState.Hidden; + // Legacy osuTK DesktopGameWindow + case DesktopGameWindow desktopGameWindow: + desktopGameWindow.CursorState |= CursorState.Hidden; + desktopGameWindow.SetIconFromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico")); + desktopGameWindow.Title = Name; + desktopGameWindow.FileDrop += fileDrop; + break; - desktopWindow.SetIconFromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico")); - desktopWindow.Title = Name; - - desktopWindow.FileDrop += fileDrop; + // SDL2 DesktopWindow + case DesktopWindow desktopWindow: + desktopWindow.CursorState.Value |= CursorState.Hidden; + desktopWindow.Title = Name; + desktopWindow.FileDrop += fileDrop; + break; } } From 702bd2b65d4c4546a57d83b4decaee64304013af Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 11 Jun 2020 13:41:53 +0900 Subject: [PATCH 1077/1117] Fix potential nullref in test --- .../SongSelect/TestScenePlaySongSelect.cs | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index a7e2dbeccb..f7d66ca5cf 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -38,13 +38,9 @@ namespace osu.Game.Tests.Visual.SongSelect public class TestScenePlaySongSelect : ScreenTestScene { private BeatmapManager manager; - private RulesetStore rulesets; - private MusicController music; - private WorkingBeatmap defaultBeatmap; - private TestSongSelect songSelect; [BackgroundDependencyLoader] @@ -308,15 +304,13 @@ namespace osu.Game.Tests.Visual.SongSelect AddAssert("random map selected", () => songSelect.CurrentBeatmap != defaultBeatmap); - var sortMode = config.GetBindable(OsuSetting.SongSelectSortingMode); - - AddStep(@"Sort by Artist", delegate { sortMode.Value = SortMode.Artist; }); - AddStep(@"Sort by Title", delegate { sortMode.Value = SortMode.Title; }); - AddStep(@"Sort by Author", delegate { sortMode.Value = SortMode.Author; }); - AddStep(@"Sort by DateAdded", delegate { sortMode.Value = SortMode.DateAdded; }); - AddStep(@"Sort by BPM", delegate { sortMode.Value = SortMode.BPM; }); - AddStep(@"Sort by Length", delegate { sortMode.Value = SortMode.Length; }); - AddStep(@"Sort by Difficulty", delegate { sortMode.Value = SortMode.Difficulty; }); + AddStep(@"Sort by Artist", () => config.Set(OsuSetting.SongSelectSortingMode, SortMode.Artist)); + AddStep(@"Sort by Title", () => config.Set(OsuSetting.SongSelectSortingMode, SortMode.Title)); + AddStep(@"Sort by Author", () => config.Set(OsuSetting.SongSelectSortingMode, SortMode.Author)); + AddStep(@"Sort by DateAdded", () => config.Set(OsuSetting.SongSelectSortingMode, SortMode.DateAdded)); + AddStep(@"Sort by BPM", () => config.Set(OsuSetting.SongSelectSortingMode, SortMode.BPM)); + AddStep(@"Sort by Length", () => config.Set(OsuSetting.SongSelectSortingMode, SortMode.Length)); + AddStep(@"Sort by Difficulty", () => config.Set(OsuSetting.SongSelectSortingMode, SortMode.Difficulty)); } [Test] From 7b012f1def0eb2c0e8e1d0af48ff86184224a802 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 11 Jun 2020 14:55:49 +0900 Subject: [PATCH 1078/1117] Fix test failures --- .../Background/TestSceneUserDimBackgrounds.cs | 12 ++++++-- .../TestSceneExpandedPanelMiddleContent.cs | 30 +++++++++---------- .../Expanded/ExpandedPanelMiddleContent.cs | 2 +- 3 files changed, 25 insertions(+), 19 deletions(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs index d601f40afe..19294d12fc 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs @@ -19,6 +19,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets; +using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Scoring; using osu.Game.Screens; @@ -27,6 +28,7 @@ using osu.Game.Screens.Play; using osu.Game.Screens.Play.PlayerSettings; using osu.Game.Screens.Ranking; using osu.Game.Screens.Select; +using osu.Game.Tests.Beatmaps; using osu.Game.Tests.Resources; using osu.Game.Users; using osuTK; @@ -186,9 +188,15 @@ namespace osu.Game.Tests.Visual.Background public void TestTransition() { performFullSetup(); + FadeAccessibleResults results = null; - AddStep("Transition to Results", () => player.Push(results = - new FadeAccessibleResults(new ScoreInfo { User = new User { Username = "osu!" } }))); + + AddStep("Transition to Results", () => player.Push(results = new FadeAccessibleResults(new ScoreInfo + { + User = new User { Username = "osu!" }, + Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo + }))); + AddUntilStep("Wait for results is current", () => results.IsCurrentScreen()); AddUntilStep("Screen is undimmed, original background retained", () => songSelect.IsBackgroundUndimmed() && songSelect.IsBackgroundCurrent() && results.IsBlurCorrect()); diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs index 69511b85c0..7be44a62de 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs @@ -4,7 +4,6 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -33,7 +32,10 @@ namespace osu.Game.Tests.Visual.Ranking { var author = new User { Username = "mapper_name" }; - AddStep("show example score", () => showPanel(createTestBeatmap(author), new TestScoreInfo(new OsuRuleset().RulesetInfo))); + AddStep("show example score", () => showPanel(new TestScoreInfo(new OsuRuleset().RulesetInfo) + { + Beatmap = createTestBeatmap(author) + })); AddAssert("mapper name present", () => this.ChildrenOfType().Any(spriteText => spriteText.Text == "mapper_name")); } @@ -41,38 +43,34 @@ namespace osu.Game.Tests.Visual.Ranking [Test] public void TestMapWithUnknownMapper() { - AddStep("show example score", () => showPanel(createTestBeatmap(null), new TestScoreInfo(new OsuRuleset().RulesetInfo))); + AddStep("show example score", () => showPanel(new TestScoreInfo(new OsuRuleset().RulesetInfo) + { + Beatmap = createTestBeatmap(null) + })); AddAssert("mapped by text not present", () => this.ChildrenOfType().All(spriteText => !containsAny(spriteText.Text, "mapped", "by"))); } - private void showPanel(WorkingBeatmap workingBeatmap, ScoreInfo score) - { - Child = new ExpandedPanelMiddleContentContainer(workingBeatmap, score); - } + private void showPanel(ScoreInfo score) => Child = new ExpandedPanelMiddleContentContainer(score); - private WorkingBeatmap createTestBeatmap(User author) + private BeatmapInfo createTestBeatmap(User author) { - var beatmap = new TestBeatmap(rulesetStore.GetRuleset(0)); + var beatmap = new TestBeatmap(rulesetStore.GetRuleset(0)).BeatmapInfo; + beatmap.Metadata.Author = author; beatmap.Metadata.Title = "Verrrrrrrrrrrrrrrrrrry looooooooooooooooooooooooong beatmap title"; beatmap.Metadata.Artist = "Verrrrrrrrrrrrrrrrrrry looooooooooooooooooooooooong beatmap artist"; - return new TestWorkingBeatmap(beatmap); + return beatmap; } private bool containsAny(string text, params string[] stringsToMatch) => stringsToMatch.Any(text.Contains); private class ExpandedPanelMiddleContentContainer : Container { - [Cached] - private Bindable workingBeatmap { get; set; } - - public ExpandedPanelMiddleContentContainer(WorkingBeatmap beatmap, ScoreInfo score) + public ExpandedPanelMiddleContentContainer(ScoreInfo score) { - workingBeatmap = new Bindable(beatmap); - Anchor = Anchor.Centre; Origin = Anchor.Centre; Size = new Vector2(ScorePanel.EXPANDED_WIDTH, 700); diff --git a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs index b06ef8ae83..01502c0913 100644 --- a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs @@ -53,7 +53,7 @@ namespace osu.Game.Screens.Ranking.Expanded private void load() { var beatmap = score.Beatmap; - var metadata = beatmap.Metadata; + var metadata = beatmap.BeatmapSet?.Metadata ?? beatmap.Metadata; var creator = metadata.Author?.Username; var topStatistics = new List From b7c1cfbe6306b262db5eb1880c74fe105147df78 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 11 Jun 2020 15:07:14 +0900 Subject: [PATCH 1079/1117] Adjust display to avoid overlaps --- osu.Game/Screens/Multi/Components/OverlinedDisplay.cs | 2 +- osu.Game/Screens/Multi/Match/MatchSubScreen.cs | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Multi/Components/OverlinedDisplay.cs b/osu.Game/Screens/Multi/Components/OverlinedDisplay.cs index 71cabd8b50..8d8d4cc404 100644 --- a/osu.Game/Screens/Multi/Components/OverlinedDisplay.cs +++ b/osu.Game/Screens/Multi/Components/OverlinedDisplay.cs @@ -80,7 +80,7 @@ namespace osu.Game.Screens.Multi.Components }, new Drawable[] { - Content = new Container { Margin = new MarginPadding { Top = 5 } } + Content = new Container { Padding = new MarginPadding { Top = 5 } } } } }; diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs index 9296fe81bd..f837a407a5 100644 --- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs @@ -130,6 +130,7 @@ namespace osu.Game.Screens.Multi.Match SelectedItem = { BindTarget = SelectedItem } } }, + null, new Drawable[] { new TriangleButton @@ -139,6 +140,12 @@ namespace osu.Game.Screens.Multi.Match Action = showBeatmapResults } } + }, + RowDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.Absolute, 5), + new Dimension(GridSizeMode.AutoSize) } } }, From fca6a6d69f3724091c2f72b1f4ea7034614633d7 Mon Sep 17 00:00:00 2001 From: Shane Woolcock Date: Fri, 12 Jun 2020 09:46:21 +0930 Subject: [PATCH 1080/1117] Implement file drop with DragDrop event --- osu.Desktop/OsuGameDesktop.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index bca30f3f9e..cd31df316a 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -10,7 +10,6 @@ using Microsoft.Win32; using osu.Desktop.Overlays; using osu.Framework.Platform; using osu.Game; -using osuTK.Input; using osu.Desktop.Updater; using osu.Framework; using osu.Framework.Logging; @@ -129,22 +128,20 @@ namespace osu.Desktop desktopGameWindow.CursorState |= CursorState.Hidden; desktopGameWindow.SetIconFromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico")); desktopGameWindow.Title = Name; - desktopGameWindow.FileDrop += fileDrop; + desktopGameWindow.FileDrop += (_, e) => fileDrop(e.FileNames); break; // SDL2 DesktopWindow case DesktopWindow desktopWindow: desktopWindow.CursorState.Value |= CursorState.Hidden; desktopWindow.Title = Name; - desktopWindow.FileDrop += fileDrop; + desktopWindow.DragDrop += f => fileDrop(new[] { f }); break; } } - private void fileDrop(object sender, FileDropEventArgs e) + private void fileDrop(string[] filePaths) { - var filePaths = e.FileNames; - var firstExtension = Path.GetExtension(filePaths.First()); if (filePaths.Any(f => Path.GetExtension(f) != firstExtension)) return; From a48e36fd31d5e4850a3f132883173a794a457c25 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 12 Jun 2020 12:58:33 +0900 Subject: [PATCH 1081/1117] Fix dotnet publish with runtime specification not working --- osu.Desktop/osu.Desktop.csproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index c34e1e1221..7a99c70999 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -30,6 +30,10 @@ + + + + From 91b6979c970cce7f6eadc11533b34848add6b8a8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 12 Jun 2020 13:38:20 +0900 Subject: [PATCH 1082/1117] Fix LoadingSpinner not always playing fade in animation --- osu.Game/Graphics/UserInterface/LoadingSpinner.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Graphics/UserInterface/LoadingSpinner.cs b/osu.Game/Graphics/UserInterface/LoadingSpinner.cs index 4f4607c114..8174c4d5fe 100644 --- a/osu.Game/Graphics/UserInterface/LoadingSpinner.cs +++ b/osu.Game/Graphics/UserInterface/LoadingSpinner.cs @@ -17,6 +17,8 @@ namespace osu.Game.Graphics.UserInterface { private readonly SpriteIcon spinner; + protected override bool StartHidden => true; + protected Container MainContents; public const float TRANSITION_DURATION = 500; From 9746e24d1efb5e85e22053e867629fc77910c4e7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 12 Jun 2020 19:40:54 +0900 Subject: [PATCH 1083/1117] Rename abstract TestScene --- osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs | 2 +- .../Gameplay/{TestPlayerTestScene.cs => OsuPlayerTestScene.cs} | 2 +- .../Visual/Gameplay/TestSceneCompletionCancellation.cs | 2 +- osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs | 2 +- osu.Game.Tests/Visual/Gameplay/TestScenePause.cs | 2 +- osu.Game.Tests/Visual/Gameplay/TestScenePauseWhenInactive.cs | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) rename osu.Game.Tests/Visual/Gameplay/{TestPlayerTestScene.cs => OsuPlayerTestScene.cs} (87%) diff --git a/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs b/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs index 7d3d8b7f16..acefaa006a 100644 --- a/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs +++ b/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs @@ -25,7 +25,7 @@ using osu.Game.Users; namespace osu.Game.Tests.Gameplay { [HeadlessTest] - public class TestSceneHitObjectSamples : TestPlayerTestScene + public class TestSceneHitObjectSamples : OsuPlayerTestScene { private readonly SkinInfo userSkinInfo = new SkinInfo(); diff --git a/osu.Game.Tests/Visual/Gameplay/TestPlayerTestScene.cs b/osu.Game.Tests/Visual/Gameplay/OsuPlayerTestScene.cs similarity index 87% rename from osu.Game.Tests/Visual/Gameplay/TestPlayerTestScene.cs rename to osu.Game.Tests/Visual/Gameplay/OsuPlayerTestScene.cs index bbf0136b00..cbf8515567 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestPlayerTestScene.cs +++ b/osu.Game.Tests/Visual/Gameplay/OsuPlayerTestScene.cs @@ -9,7 +9,7 @@ namespace osu.Game.Tests.Visual.Gameplay /// /// A with an arbitrary ruleset value to test with. /// - public abstract class TestPlayerTestScene : PlayerTestScene + public abstract class OsuPlayerTestScene : PlayerTestScene { protected override Ruleset CreatePlayerRuleset() => new OsuRuleset(); } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs index f87999ae61..79275d70a7 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs @@ -16,7 +16,7 @@ using osuTK; namespace osu.Game.Tests.Visual.Gameplay { - public class TestSceneCompletionCancellation : TestPlayerTestScene + public class TestSceneCompletionCancellation : OsuPlayerTestScene { private Track track; diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs index 744eeed022..2a119f5199 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs @@ -16,7 +16,7 @@ using osuTK; namespace osu.Game.Tests.Visual.Gameplay { - public class TestSceneGameplayRewinding : TestPlayerTestScene + public class TestSceneGameplayRewinding : OsuPlayerTestScene { [Resolved] private AudioManager audioManager { get; set; } diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs index 411265d600..387ac42f67 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs @@ -16,7 +16,7 @@ using osuTK.Input; namespace osu.Game.Tests.Visual.Gameplay { - public class TestScenePause : TestPlayerTestScene + public class TestScenePause : OsuPlayerTestScene { protected new PausePlayer Player => (PausePlayer)base.Player; diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePauseWhenInactive.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePauseWhenInactive.cs index 20911bfa4d..e43e5ba3ce 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePauseWhenInactive.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePauseWhenInactive.cs @@ -12,7 +12,7 @@ using osu.Game.Rulesets; namespace osu.Game.Tests.Visual.Gameplay { [HeadlessTest] // we alter unsafe properties on the game host to test inactive window state. - public class TestScenePauseWhenInactive : TestPlayerTestScene + public class TestScenePauseWhenInactive : OsuPlayerTestScene { protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) { From 7c3e7b65a820365799af5ebb590276263d28bb2a Mon Sep 17 00:00:00 2001 From: mcendu Date: Fri, 12 Jun 2020 21:22:22 +0800 Subject: [PATCH 1084/1117] add custom file path support for osu\!mania judgement sprite --- .../Skinning/ManiaLegacySkinTransformer.cs | 66 +++++++++++-------- .../LegacyManiaSkinConfigurationLookup.cs | 8 ++- osu.Game/Skinning/LegacySkin.cs | 18 +++++ 3 files changed, 65 insertions(+), 27 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/ManiaLegacySkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/ManiaLegacySkinTransformer.cs index e64178083a..9ba544ed59 100644 --- a/osu.Game.Rulesets.Mania/Skinning/ManiaLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/ManiaLegacySkinTransformer.cs @@ -11,6 +11,7 @@ using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Skinning; +using System.Collections.Generic; namespace osu.Game.Rulesets.Mania.Skinning { @@ -19,6 +20,36 @@ namespace osu.Game.Rulesets.Mania.Skinning private readonly ISkin source; private readonly ManiaBeatmap beatmap; + /// + /// Mapping of to ther corresponding + /// value. + /// + private static readonly IReadOnlyDictionary componentMapping + = new Dictionary + { + { HitResult.Perfect, LegacyManiaSkinConfigurationLookups.Hit300g }, + { HitResult.Great, LegacyManiaSkinConfigurationLookups.Hit300 }, + { HitResult.Good, LegacyManiaSkinConfigurationLookups.Hit200 }, + { HitResult.Ok, LegacyManiaSkinConfigurationLookups.Hit100 }, + { HitResult.Meh, LegacyManiaSkinConfigurationLookups.Hit50 }, + { HitResult.Miss, LegacyManiaSkinConfigurationLookups.Hit0 } + }; + + /// + /// Mapping of to their corresponding + /// default filenames. + /// + private static readonly IReadOnlyDictionary defaultName + = new Dictionary + { + { HitResult.Perfect, "mania-hit300g" }, + { HitResult.Great, "mania-hit300" }, + { HitResult.Good, "mania-hit200" }, + { HitResult.Ok, "mania-hit100" }, + { HitResult.Meh, "mania-hit50" }, + { HitResult.Miss, "mania-hit0" } + }; + private Lazy isLegacySkin; /// @@ -47,15 +78,15 @@ namespace osu.Game.Rulesets.Mania.Skinning public Drawable GetDrawableComponent(ISkinComponent component) { + if (!isLegacySkin.Value || !hasKeyTexture.Value) + return null; + switch (component) { case GameplaySkinComponent resultComponent: - return getResult(resultComponent); + return getResult(resultComponent.Component); case ManiaSkinComponent maniaComponent: - if (!isLegacySkin.Value || !hasKeyTexture.Value) - return null; - switch (maniaComponent.Component) { case ManiaSkinComponents.ColumnBackground: @@ -95,30 +126,13 @@ namespace osu.Game.Rulesets.Mania.Skinning return null; } - private Drawable getResult(GameplaySkinComponent resultComponent) + private Drawable getResult(HitResult result) { - switch (resultComponent.Component) - { - case HitResult.Miss: - return this.GetAnimation("mania-hit0", true, true); + string image = GetConfig( + new ManiaSkinConfigurationLookup(componentMapping[result]) + )?.Value ?? defaultName[result]; - case HitResult.Meh: - return this.GetAnimation("mania-hit50", true, true); - - case HitResult.Ok: - return this.GetAnimation("mania-hit100", true, true); - - case HitResult.Good: - return this.GetAnimation("mania-hit200", true, true); - - case HitResult.Great: - return this.GetAnimation("mania-hit300", true, true); - - case HitResult.Perfect: - return this.GetAnimation("mania-hit300g", true, true); - } - - return null; + return this.GetAnimation(image, true, true); } public Texture GetTexture(string componentName) => source.GetTexture(componentName); diff --git a/osu.Game/Skinning/LegacyManiaSkinConfigurationLookup.cs b/osu.Game/Skinning/LegacyManiaSkinConfigurationLookup.cs index c76d5c8784..4990ca8e60 100644 --- a/osu.Game/Skinning/LegacyManiaSkinConfigurationLookup.cs +++ b/osu.Game/Skinning/LegacyManiaSkinConfigurationLookup.cs @@ -43,6 +43,12 @@ namespace osu.Game.Skinning MinimumColumnWidth, LeftStageImage, RightStageImage, - BottomStageImage + BottomStageImage, + Hit300g, + Hit300, + Hit200, + Hit100, + Hit50, + Hit0, } } diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 003fa24d5b..390dc871e4 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -257,6 +257,24 @@ namespace osu.Game.Skinning case LegacyManiaSkinConfigurationLookups.RightLineWidth: Debug.Assert(maniaLookup.TargetColumn != null); return SkinUtils.As(new Bindable(existing.ColumnLineWidth[maniaLookup.TargetColumn.Value + 1])); + + case LegacyManiaSkinConfigurationLookups.Hit0: + return SkinUtils.As(getManiaImage(existing, "Hit0")); + + case LegacyManiaSkinConfigurationLookups.Hit50: + return SkinUtils.As(getManiaImage(existing, "Hit50")); + + case LegacyManiaSkinConfigurationLookups.Hit100: + return SkinUtils.As(getManiaImage(existing, "Hit100")); + + case LegacyManiaSkinConfigurationLookups.Hit200: + return SkinUtils.As(getManiaImage(existing, "Hit200")); + + case LegacyManiaSkinConfigurationLookups.Hit300: + return SkinUtils.As(getManiaImage(existing, "Hit300")); + + case LegacyManiaSkinConfigurationLookups.Hit300g: + return SkinUtils.As(getManiaImage(existing, "Hit300g")); } return null; From 8a9d01119753923a0230e0c5bea40f93b4586cdf Mon Sep 17 00:00:00 2001 From: mcendu Date: Fri, 12 Jun 2020 22:09:58 +0800 Subject: [PATCH 1085/1117] add test files --- .../Resources/special-skin/mania/hit0@2x.png | Bin 0 -> 56621 bytes .../Resources/special-skin/mania/hit100@2x.png | Bin 0 -> 66535 bytes .../Resources/special-skin/mania/hit200@2x.png | Bin 0 -> 82190 bytes .../Resources/special-skin/mania/hit300@2x.png | Bin 0 -> 92906 bytes .../special-skin/mania/hit300g-0@2x.png | Bin 0 -> 56026 bytes .../special-skin/mania/hit300g-1@2x.png | Bin 0 -> 57038 bytes .../Resources/special-skin/mania/hit50@2x.png | Bin 0 -> 57215 bytes .../special-skin/mania/stage-bottom@2x.png | Bin 0 -> 1965 bytes .../Resources/special-skin/skin.ini | 11 +++++++++-- 9 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 osu.Game.Rulesets.Mania.Tests/Resources/special-skin/mania/hit0@2x.png create mode 100644 osu.Game.Rulesets.Mania.Tests/Resources/special-skin/mania/hit100@2x.png create mode 100644 osu.Game.Rulesets.Mania.Tests/Resources/special-skin/mania/hit200@2x.png create mode 100644 osu.Game.Rulesets.Mania.Tests/Resources/special-skin/mania/hit300@2x.png create mode 100644 osu.Game.Rulesets.Mania.Tests/Resources/special-skin/mania/hit300g-0@2x.png create mode 100644 osu.Game.Rulesets.Mania.Tests/Resources/special-skin/mania/hit300g-1@2x.png create mode 100644 osu.Game.Rulesets.Mania.Tests/Resources/special-skin/mania/hit50@2x.png create mode 100644 osu.Game.Rulesets.Mania.Tests/Resources/special-skin/mania/stage-bottom@2x.png diff --git a/osu.Game.Rulesets.Mania.Tests/Resources/special-skin/mania/hit0@2x.png b/osu.Game.Rulesets.Mania.Tests/Resources/special-skin/mania/hit0@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..2e7b9bc34f3ea66e5d4e687adb312f36d7c5a4bd GIT binary patch literal 56621 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

    ~<2&bFepI2$u~| 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 FV1mC)D$D=? literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Mania.Tests/Resources/special-skin/mania/hit200@2x.png b/osu.Game.Rulesets.Mania.Tests/Resources/special-skin/mania/hit200@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..24ad926375e8c88df664bdd9555ddddc1a748be9 GIT binary patch literal 82190 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 IM6N<$f=61fdH?_b literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Mania.Tests/Resources/special-skin/mania/hit300@2x.png b/osu.Game.Rulesets.Mania.Tests/Resources/special-skin/mania/hit300@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..098561f980ddc8a17308c29a62a249fd2812e370 GIT binary patch literal 92906 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

    E=Rmmw{ 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­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 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Mania.Tests/Resources/special-skin/mania/hit300g-1@2x.png b/osu.Game.Rulesets.Mania.Tests/Resources/special-skin/mania/hit300g-1@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..f17b2b1e737a4d9b1c88247a738dadec3d9dfd1a GIT binary patch literal 57038 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 Ks#k-08_-AT>ABZ45&7U3%MLFgSfv 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

    }+-2g^TWTo$yAMT?d2TCHI=II`n0mRKNHw#M7` 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 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Mania.Tests/Resources/special-skin/mania/stage-bottom@2x.png b/osu.Game.Rulesets.Mania.Tests/Resources/special-skin/mania/stage-bottom@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..ca590eaf08e65dc1ae40bce9f9f74e69c3a0f8a4 GIT binary patch literal 1965 zcmV;e2U7TnP)3L?K*_rKGQL~x> z08mZsB?AB;00{&D5QGE*00=??0RRLcfdBx4kU#(cK}a9~fFL9g06-8D2ml}m2?PKT zgaiTr2ton@00ep2fXKN$&0jO!YxaDt*~2%@9=&BYwcLXAPMQr~G21k5w&6kJxjb8A z_D|IAZ~bOB28!G08;c&-tz|}co88}PcJ#Q}%BR*6wH~u$-xc>$EBeCeshNF!$ZXkE z{4@Pc`rGCr-6ySR6WyVQ-FPAY_E=?Chs?hHsuAqU`YZ}YpPY$;++^|Vo<)i3z{N(e z%4_H7h_!?Q{lQCS*LIoxvfXU+xV41Z-S{|4x@)q3->SZFltBJiXSV8T{Ikhd%-($* zp6p+HV)pyGz&!{aR!1JHReDt%hW5g z1K;zRuk~Pe(Cp;lMj{afL)Gwe%S|Ve0rQ)YM(Ha}HGe(sZ;8)r6ciOiZ}ulkWz);! zV4G#u@vkXPAZ6QC-xPgLGxO0G&FJ%#jMQEgnrBKU55PwW3zcakeCk+SS65)``!)Nu zs-X{Fj-OxMlN~U98rf^a?Aq5z_{3rBc&+M-vwN)+|NrxT#S0Gfo_*)y=MO6;A1nFH7I=uowik4k0!)k7a$v5vLSeW3((Z%h21DwFB;V?Aq$W35s$!_q+l zK`=jQWYyQ}lkPL4SOv73A6O*{ewqUi1+`6gtYa-kU+8n(nTy`@@i{9+$LiMDsEw{LB_v=Mv^2@D zpOsnbgQbVdcrlBv6w-umnnt|}B>sf?4^fHa%hTEItb7)KKOGP-B|T4|1QxogdNKKZ zQT5W_y!R=IG;6D#1dhe?A3AfqFr43q>B;kZTMWoW0>LZ_Hk(VlE^iIPd|K9_31l(p z3`BkA7K7^ln_eg?S(E2VP(3U)Mh;3?2=@6%W92jDjrOCU?>0azJDHxIJRjX*W-2aC<`i!vK1W|>Q0q$ktUI|pP`0cSxKJ0lB-ksec|RJs+vn*xXE-!7fSfYz~nhXkU$VjH#_pBS30K`7rs$(9fwB6 z{(Gu!2G~y0>pnvIV&z2qJftsl4DPH^$-}K?&zn34njwK8SUSioOO;p@m}!+!y2V_aS|uvyf{;@iFMEQPPv={(K2ggam?M=`zf8#+-+LJJy{Q_p(w+ zrP*rtPBPcN@V7zC8pJa@9nXoW_&wPpoYO-EPqieNo>|zCSrBU*!f@fI|YoEM>|n zPSIwHzK3s{ZT~a5YF%eAdh8g>e$U-)X1jk5--xkYwL@Ph;U763PoC?HgiCi(6(uZG zq9CJSb1EuXz5b$H@^Ccz-uNWB6-t)7Tg?V;H%98+mOLE4FN&^unyfTq&~sl z1tmf(yq*;`W3Npt3XQlm0(WyM3ZzOb-I_Ft&==@HH4+E_AViH4762d!2?PKTgaiTr z2ton@00bd{004rJKmY(iScwh*)v^2`>Vy9Q*Vy#8=R%=B00000NkvXXu0mjflsBGC literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Mania.Tests/Resources/special-skin/skin.ini b/osu.Game.Rulesets.Mania.Tests/Resources/special-skin/skin.ini index 56564776b3..eaa50d1a61 100644 --- a/osu.Game.Rulesets.Mania.Tests/Resources/special-skin/skin.ini +++ b/osu.Game.Rulesets.Mania.Tests/Resources/special-skin/skin.ini @@ -1,6 +1,13 @@ [General] -Version: 2.4 +Version: 2.5 [Mania] Keys: 4 -ColumnLineWidth: 3,1,3,1,1 \ No newline at end of file +ColumnLineWidth: 3,1,3,1,1 +Hit0: mania/hit0 +Hit50: mania/hit50 +Hit100: mania/hit100 +Hit200: mania/hit200 +Hit300: mania/hit300 +Hit300g: mania/hit300g +BottomStageImage: mania/stage-bottom \ No newline at end of file From 586e3d405c8d7863137b542b914b044ffbe4fde2 Mon Sep 17 00:00:00 2001 From: mcendu Date: Fri, 12 Jun 2020 22:48:18 +0800 Subject: [PATCH 1086/1117] add proper decoding support? --- osu.Game/Skinning/LegacyManiaSkinDecoder.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Skinning/LegacyManiaSkinDecoder.cs b/osu.Game/Skinning/LegacyManiaSkinDecoder.cs index a988bd589f..cbd6aa17dc 100644 --- a/osu.Game/Skinning/LegacyManiaSkinDecoder.cs +++ b/osu.Game/Skinning/LegacyManiaSkinDecoder.cs @@ -111,11 +111,11 @@ namespace osu.Game.Skinning HandleColours(currentConfig, line); break; + // Custom sprite paths case string _ when pair.Key.StartsWith("NoteImage"): - currentConfig.ImageLookups[pair.Key] = pair.Value; - break; - case string _ when pair.Key.StartsWith("KeyImage"): + case string _ when pair.Key.StartsWith("Hit"): + case "BottomStageImage": currentConfig.ImageLookups[pair.Key] = pair.Value; break; } From 7def6a91812670529b671b5e89abf243d8a8c30b Mon Sep 17 00:00:00 2001 From: mcendu Date: Fri, 12 Jun 2020 23:06:19 +0800 Subject: [PATCH 1087/1117] fix tests incorrectly showing judgements not present in mania --- .../Skinning/TestSceneDrawableJudgement.cs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneDrawableJudgement.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneDrawableJudgement.cs index 497b80950a..540bf82e1f 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneDrawableJudgement.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneDrawableJudgement.cs @@ -6,6 +6,7 @@ using System.Linq; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Mania.Scoring; using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; @@ -16,14 +17,17 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning { public TestSceneDrawableJudgement() { + var HitWindows = new ManiaHitWindows(); + foreach (HitResult result in Enum.GetValues(typeof(HitResult)).OfType().Skip(1)) { - AddStep("Show " + result.GetDescription(), () => SetContents(() => - new DrawableManiaJudgement(new JudgementResult(new HitObject(), new Judgement()) { Type = result }, null) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - })); + if (HitWindows.IsHitResultAllowed(result)) + AddStep("Show " + result.GetDescription(), () => SetContents(() => + new DrawableManiaJudgement(new JudgementResult(new HitObject(), new Judgement()) { Type = result }, null) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + })); } } } From c6e087b9947aa46a8bed3abe3928ebd1d2a0ba04 Mon Sep 17 00:00:00 2001 From: mcendu Date: Fri, 12 Jun 2020 23:11:50 +0800 Subject: [PATCH 1088/1117] remove incorrectly added key --- osu.Game/Skinning/LegacyManiaSkinDecoder.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Skinning/LegacyManiaSkinDecoder.cs b/osu.Game/Skinning/LegacyManiaSkinDecoder.cs index cbd6aa17dc..0806676fde 100644 --- a/osu.Game/Skinning/LegacyManiaSkinDecoder.cs +++ b/osu.Game/Skinning/LegacyManiaSkinDecoder.cs @@ -115,7 +115,6 @@ namespace osu.Game.Skinning case string _ when pair.Key.StartsWith("NoteImage"): case string _ when pair.Key.StartsWith("KeyImage"): case string _ when pair.Key.StartsWith("Hit"): - case "BottomStageImage": currentConfig.ImageLookups[pair.Key] = pair.Value; break; } From da46288ef09489d5d98a163f5ada7e292aed091b Mon Sep 17 00:00:00 2001 From: mcendu Date: Fri, 12 Jun 2020 23:20:04 +0800 Subject: [PATCH 1089/1117] remove stage bottom image --- .../special-skin/mania/stage-bottom@2x.png | Bin 1965 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 osu.Game.Rulesets.Mania.Tests/Resources/special-skin/mania/stage-bottom@2x.png diff --git a/osu.Game.Rulesets.Mania.Tests/Resources/special-skin/mania/stage-bottom@2x.png b/osu.Game.Rulesets.Mania.Tests/Resources/special-skin/mania/stage-bottom@2x.png deleted file mode 100644 index ca590eaf08e65dc1ae40bce9f9f74e69c3a0f8a4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1965 zcmV;e2U7TnP)3L?K*_rKGQL~x> z08mZsB?AB;00{&D5QGE*00=??0RRLcfdBx4kU#(cK}a9~fFL9g06-8D2ml}m2?PKT zgaiTr2ton@00ep2fXKN$&0jO!YxaDt*~2%@9=&BYwcLXAPMQr~G21k5w&6kJxjb8A z_D|IAZ~bOB28!G08;c&-tz|}co88}PcJ#Q}%BR*6wH~u$-xc>$EBeCeshNF!$ZXkE z{4@Pc`rGCr-6ySR6WyVQ-FPAY_E=?Chs?hHsuAqU`YZ}YpPY$;++^|Vo<)i3z{N(e z%4_H7h_!?Q{lQCS*LIoxvfXU+xV41Z-S{|4x@)q3->SZFltBJiXSV8T{Ikhd%-($* zp6p+HV)pyGz&!{aR!1JHReDt%hW5g z1K;zRuk~Pe(Cp;lMj{afL)Gwe%S|Ve0rQ)YM(Ha}HGe(sZ;8)r6ciOiZ}ulkWz);! zV4G#u@vkXPAZ6QC-xPgLGxO0G&FJ%#jMQEgnrBKU55PwW3zcakeCk+SS65)``!)Nu zs-X{Fj-OxMlN~U98rf^a?Aq5z_{3rBc&+M-vwN)+|NrxT#S0Gfo_*)y=MO6;A1nFH7I=uowik4k0!)k7a$v5vLSeW3((Z%h21DwFB;V?Aq$W35s$!_q+l zK`=jQWYyQ}lkPL4SOv73A6O*{ewqUi1+`6gtYa-kU+8n(nTy`@@i{9+$LiMDsEw{LB_v=Mv^2@D zpOsnbgQbVdcrlBv6w-umnnt|}B>sf?4^fHa%hTEItb7)KKOGP-B|T4|1QxogdNKKZ zQT5W_y!R=IG;6D#1dhe?A3AfqFr43q>B;kZTMWoW0>LZ_Hk(VlE^iIPd|K9_31l(p z3`BkA7K7^ln_eg?S(E2VP(3U)Mh;3?2=@6%W92jDjrOCU?>0azJDHxIJRjX*W-2aC<`i!vK1W|>Q0q$ktUI|pP`0cSxKJ0lB-ksec|RJs+vn*xXE-!7fSfYz~nhXkU$VjH#_pBS30K`7rs$(9fwB6 z{(Gu!2G~y0>pnvIV&z2qJftsl4DPH^$-}K?&zn34njwK8SUSioOO;p@m}!+!y2V_aS|uvyf{;@iFMEQPPv={(K2ggam?M=`zf8#+-+LJJy{Q_p(w+ zrP*rtPBPcN@V7zC8pJa@9nXoW_&wPpoYO-EPqieNo>|zCSrBU*!f@fI|YoEM>|n zPSIwHzK3s{ZT~a5YF%eAdh8g>e$U-)X1jk5--xkYwL@Ph;U763PoC?HgiCi(6(uZG zq9CJSb1EuXz5b$H@^Ccz-uNWB6-t)7Tg?V;H%98+mOLE4FN&^unyfTq&~sl z1tmf(yq*;`W3Npt3XQlm0(WyM3ZzOb-I_Ft&==@HH4+E_AViH4762d!2?PKTgaiTr z2ton@00bd{004rJKmY(iScwh*)v^2`>Vy9Q*Vy#8=R%=B00000NkvXXu0mjflsBGC From a42bfcb5aba1910cc975fed0d420864917d9069f Mon Sep 17 00:00:00 2001 From: mcendu Date: Fri, 12 Jun 2020 23:23:57 +0800 Subject: [PATCH 1090/1117] remove reference to stage bottom from skin ini --- osu.Game.Rulesets.Mania.Tests/Resources/special-skin/skin.ini | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/Resources/special-skin/skin.ini b/osu.Game.Rulesets.Mania.Tests/Resources/special-skin/skin.ini index eaa50d1a61..941abac1da 100644 --- a/osu.Game.Rulesets.Mania.Tests/Resources/special-skin/skin.ini +++ b/osu.Game.Rulesets.Mania.Tests/Resources/special-skin/skin.ini @@ -9,5 +9,4 @@ Hit50: mania/hit50 Hit100: mania/hit100 Hit200: mania/hit200 Hit300: mania/hit300 -Hit300g: mania/hit300g -BottomStageImage: mania/stage-bottom \ No newline at end of file +Hit300g: mania/hit300g \ No newline at end of file From aa476835e7b657b414093fb3dcf6b19c3d935d9d Mon Sep 17 00:00:00 2001 From: mcendu Date: Sat, 13 Jun 2020 11:31:34 +0800 Subject: [PATCH 1091/1117] tidy up code --- .../Skinning/TestSceneDrawableJudgement.cs | 6 ++- .../Skinning/ManiaLegacySkinTransformer.cs | 40 +++++++++---------- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneDrawableJudgement.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneDrawableJudgement.cs index 540bf82e1f..a4d4ec50f8 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneDrawableJudgement.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneDrawableJudgement.cs @@ -17,17 +17,19 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning { public TestSceneDrawableJudgement() { - var HitWindows = new ManiaHitWindows(); + var hitWindows = new ManiaHitWindows(); foreach (HitResult result in Enum.GetValues(typeof(HitResult)).OfType().Skip(1)) { - if (HitWindows.IsHitResultAllowed(result)) + if (hitWindows.IsHitResultAllowed(result)) + { AddStep("Show " + result.GetDescription(), () => SetContents(() => new DrawableManiaJudgement(new JudgementResult(new HitObject(), new Judgement()) { Type = result }, null) { Anchor = Anchor.Centre, Origin = Anchor.Centre, })); + } } } } diff --git a/osu.Game.Rulesets.Mania/Skinning/ManiaLegacySkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/ManiaLegacySkinTransformer.cs index 9ba544ed59..3304330233 100644 --- a/osu.Game.Rulesets.Mania/Skinning/ManiaLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/ManiaLegacySkinTransformer.cs @@ -24,31 +24,31 @@ namespace osu.Game.Rulesets.Mania.Skinning /// Mapping of to ther corresponding /// value. ///

    - private static readonly IReadOnlyDictionary componentMapping + private static readonly IReadOnlyDictionary hitresult_mapping = new Dictionary - { - { HitResult.Perfect, LegacyManiaSkinConfigurationLookups.Hit300g }, - { HitResult.Great, LegacyManiaSkinConfigurationLookups.Hit300 }, - { HitResult.Good, LegacyManiaSkinConfigurationLookups.Hit200 }, - { HitResult.Ok, LegacyManiaSkinConfigurationLookups.Hit100 }, - { HitResult.Meh, LegacyManiaSkinConfigurationLookups.Hit50 }, - { HitResult.Miss, LegacyManiaSkinConfigurationLookups.Hit0 } - }; + { + { HitResult.Perfect, LegacyManiaSkinConfigurationLookups.Hit300g }, + { HitResult.Great, LegacyManiaSkinConfigurationLookups.Hit300 }, + { HitResult.Good, LegacyManiaSkinConfigurationLookups.Hit200 }, + { HitResult.Ok, LegacyManiaSkinConfigurationLookups.Hit100 }, + { HitResult.Meh, LegacyManiaSkinConfigurationLookups.Hit50 }, + { HitResult.Miss, LegacyManiaSkinConfigurationLookups.Hit0 } + }; /// /// Mapping of to their corresponding /// default filenames. /// - private static readonly IReadOnlyDictionary defaultName + private static readonly IReadOnlyDictionary default_hitresult_skin_filenames = new Dictionary - { - { HitResult.Perfect, "mania-hit300g" }, - { HitResult.Great, "mania-hit300" }, - { HitResult.Good, "mania-hit200" }, - { HitResult.Ok, "mania-hit100" }, - { HitResult.Meh, "mania-hit50" }, - { HitResult.Miss, "mania-hit0" } - }; + { + { HitResult.Perfect, "mania-hit300g" }, + { HitResult.Great, "mania-hit300" }, + { HitResult.Good, "mania-hit200" }, + { HitResult.Ok, "mania-hit100" }, + { HitResult.Meh, "mania-hit50" }, + { HitResult.Miss, "mania-hit0" } + }; private Lazy isLegacySkin; @@ -129,8 +129,8 @@ namespace osu.Game.Rulesets.Mania.Skinning private Drawable getResult(HitResult result) { string image = GetConfig( - new ManiaSkinConfigurationLookup(componentMapping[result]) - )?.Value ?? defaultName[result]; + new ManiaSkinConfigurationLookup(hitresult_mapping[result]) + )?.Value ?? default_hitresult_skin_filenames[result]; return this.GetAnimation(image, true, true); } From 9d98adee1e0b0b806f4d678e3310a5fb3cc3edc3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 13 Jun 2020 16:10:41 +0900 Subject: [PATCH 1092/1117] Update fastlane to fix upload failure for iOS releases --- Gemfile.lock | 72 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 27 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index e3954c2681..bf971d2c22 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -5,6 +5,22 @@ GEM addressable (2.7.0) public_suffix (>= 2.0.2, < 5.0) atomos (0.1.3) + aws-eventstream (1.1.0) + aws-partitions (1.329.0) + aws-sdk-core (3.99.2) + aws-eventstream (~> 1, >= 1.0.2) + aws-partitions (~> 1, >= 1.239.0) + aws-sigv4 (~> 1.1) + jmespath (~> 1.0) + aws-sdk-kms (1.34.1) + aws-sdk-core (~> 3, >= 3.99.0) + aws-sigv4 (~> 1.1) + aws-sdk-s3 (1.68.1) + aws-sdk-core (~> 3, >= 3.99.0) + aws-sdk-kms (~> 1) + aws-sigv4 (~> 1.1) + aws-sigv4 (1.1.4) + aws-eventstream (~> 1.0, >= 1.0.2) babosa (1.0.3) claide (1.0.3) colored (1.2) @@ -13,23 +29,24 @@ GEM highline (~> 1.7.2) declarative (0.0.10) declarative-option (0.1.0) - digest-crc (0.4.1) + digest-crc (0.5.1) domain_name (0.5.20190701) unf (>= 0.0.5, < 1.0.0) dotenv (2.7.5) emoji_regex (1.0.1) - excon (0.71.1) - faraday (0.17.3) + excon (0.74.0) + faraday (1.0.1) multipart-post (>= 1.2, < 3) faraday-cookie_jar (0.0.6) faraday (>= 0.7.4) http-cookie (~> 1.0.0) - faraday_middleware (0.13.1) - faraday (>= 0.7.4, < 1.0) + faraday_middleware (1.0.0) + faraday (~> 1.0) fastimage (2.1.7) - fastlane (2.140.0) + fastlane (2.149.1) CFPropertyList (>= 2.3, < 4.0.0) addressable (>= 2.3, < 3.0.0) + aws-sdk-s3 (~> 1.0) babosa (>= 1.0.2, < 2.0.0) bundler (>= 1.12.0, < 3.0.0) colored @@ -37,12 +54,12 @@ GEM dotenv (>= 2.1.1, < 3.0.0) emoji_regex (>= 0.1, < 2.0) excon (>= 0.71.0, < 1.0.0) - faraday (~> 0.17) + faraday (>= 0.17, < 2.0) faraday-cookie_jar (~> 0.0.6) - faraday_middleware (~> 0.13.1) + faraday_middleware (>= 0.13.1, < 2.0) fastimage (>= 2.1.0, < 3.0.0) gh_inspector (>= 1.1.2, < 2.0.0) - google-api-client (>= 0.29.2, < 0.37.0) + google-api-client (>= 0.37.0, < 0.39.0) google-cloud-storage (>= 1.15.0, < 2.0.0) highline (>= 1.7.2, < 2.0.0) json (< 3.0.0) @@ -69,7 +86,7 @@ GEM souyuz (= 0.9.1) fastlane-plugin-xamarin (0.6.3) gh_inspector (1.1.3) - google-api-client (0.36.4) + google-api-client (0.38.0) addressable (~> 2.5, >= 2.5.1) googleauth (~> 0.9) httpclient (>= 2.8.1, < 3.0) @@ -80,27 +97,28 @@ GEM google-cloud-core (1.5.0) google-cloud-env (~> 1.0) google-cloud-errors (~> 1.0) - google-cloud-env (1.3.0) - faraday (~> 0.11) - google-cloud-errors (1.0.0) - google-cloud-storage (1.25.1) + google-cloud-env (1.3.2) + faraday (>= 0.17.3, < 2.0) + google-cloud-errors (1.0.1) + google-cloud-storage (1.26.2) addressable (~> 2.5) digest-crc (~> 0.4) google-api-client (~> 0.33) google-cloud-core (~> 1.2) googleauth (~> 0.9) mini_mime (~> 1.0) - googleauth (0.10.0) - faraday (~> 0.12) + googleauth (0.12.0) + faraday (>= 0.17.3, < 2.0) jwt (>= 1.4, < 3.0) memoist (~> 0.16) multi_json (~> 1.11) os (>= 0.9, < 2.0) - signet (~> 0.12) + signet (~> 0.14) highline (1.7.10) http-cookie (1.0.3) domain_name (~> 0.5) httpclient (2.8.3) + jmespath (1.4.0) json (2.3.0) jwt (2.1.0) memoist (0.16.2) @@ -114,7 +132,7 @@ GEM naturally (2.2.0) nokogiri (1.10.7) mini_portile2 (~> 2.4.0) - os (1.0.1) + os (1.1.0) plist (3.5.0) public_suffix (2.0.5) representable (3.0.4) @@ -125,12 +143,12 @@ GEM rouge (2.0.7) rubyzip (1.3.0) security (0.1.3) - signet (0.12.0) + signet (0.14.0) addressable (~> 2.3) - faraday (~> 0.9) + faraday (>= 0.17.3, < 2.0) jwt (>= 1.5, < 3.0) multi_json (~> 1.10) - simctl (1.6.7) + simctl (1.6.8) CFPropertyList naturally slack-notifier (2.3.2) @@ -141,17 +159,17 @@ GEM terminal-notifier (2.0.0) terminal-table (1.8.0) unicode-display_width (~> 1.1, >= 1.1.1) - tty-cursor (0.7.0) - tty-screen (0.7.0) - tty-spinner (0.9.2) + tty-cursor (0.7.1) + tty-screen (0.8.0) + tty-spinner (0.9.3) tty-cursor (~> 0.7) uber (0.1.0) unf (0.1.4) unf_ext - unf_ext (0.0.7.6) - unicode-display_width (1.6.1) + unf_ext (0.0.7.7) + unicode-display_width (1.7.0) word_wrap (1.0.0) - xcodeproj (1.14.0) + xcodeproj (1.16.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) From c490dba7b3e0e22202ab861e0132a5779d818f0f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 13 Jun 2020 18:18:46 +0900 Subject: [PATCH 1093/1117] Fix crash on local score display --- osu.Game/Scoring/ScoreStore.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Scoring/ScoreStore.cs b/osu.Game/Scoring/ScoreStore.cs index 9627481f4d..f5c5cd5dad 100644 --- a/osu.Game/Scoring/ScoreStore.cs +++ b/osu.Game/Scoring/ScoreStore.cs @@ -18,6 +18,8 @@ namespace osu.Game.Scoring protected override IQueryable AddIncludesForConsumption(IQueryable query) => base.AddIncludesForConsumption(query) .Include(s => s.Beatmap) + .Include(s => s.Beatmap).ThenInclude(b => b.Metadata) + .Include(s => s.Beatmap).ThenInclude(b => b.BeatmapSet).ThenInclude(s => s.Metadata) .Include(s => s.Ruleset); } } From 619c541cf559f546c5718673c4acf279f98ce320 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 13 Jun 2020 12:41:00 +0200 Subject: [PATCH 1094/1117] Rewrite test to use dummy API --- .../Online/TestSceneCommentsContainer.cs | 117 +++++++++++++----- 1 file changed, 89 insertions(+), 28 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs index 42e6b9087c..26ad0b0d3f 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs @@ -1,52 +1,113 @@ // 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.Game.Online.API.Requests; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics; -using osu.Game.Overlays.Comments; using osu.Game.Overlays; using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Game.Users; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Overlays.Comments; namespace osu.Game.Tests.Visual.Online { [TestFixture] public class TestSceneCommentsContainer : OsuTestScene { - protected override bool UseOnlineAPI => true; - [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); - public TestSceneCommentsContainer() - { - BasicScrollContainer scroll; - TestCommentsContainer comments; + private DummyAPIAccess dummyAPI => (DummyAPIAccess)API; - Add(scroll = new BasicScrollContainer + private CommentsContainer commentsContainer; + + [SetUp] + public void SetUp() => Schedule(() => + Child = new BasicScrollContainer { RelativeSizeAxes = Axes.Both, - Child = comments = new TestCommentsContainer() + Child = commentsContainer = new CommentsContainer() }); - AddStep("Big Black comments", () => comments.ShowComments(CommentableType.Beatmapset, 41823)); - AddStep("Airman comments", () => comments.ShowComments(CommentableType.Beatmapset, 24313)); - AddStep("Lazer build comments", () => comments.ShowComments(CommentableType.Build, 4772)); - AddStep("News comments", () => comments.ShowComments(CommentableType.NewsPost, 715)); - AddStep("Trigger user change", comments.User.TriggerChange); - AddStep("Idle state", () => - { - scroll.Clear(); - scroll.Add(comments = new TestCommentsContainer()); - }); - } - - private class TestCommentsContainer : CommentsContainer + [Test] + public void TestIdleState() { - public new Bindable User => base.User; + AddUntilStep("loading spinner shown", + () => commentsContainer.ChildrenOfType().Single().IsLoading); } + + [Test] + public void TestSingleCommentsPage() + { + setUpCommentsResponse(exampleComments); + AddStep("show comments", () => commentsContainer.ShowComments(CommentableType.Beatmapset, 123)); + AddUntilStep("show more button hidden", + () => commentsContainer.ChildrenOfType().Single().Alpha == 0); + } + + [Test] + public void TestMultipleCommentPages() + { + var comments = exampleComments; + comments.HasMore = true; + comments.TopLevelCount = 10; + + setUpCommentsResponse(comments); + AddStep("show comments", () => commentsContainer.ShowComments(CommentableType.Beatmapset, 123)); + AddUntilStep("show more button visible", + () => commentsContainer.ChildrenOfType().Single().Alpha == 1); + } + + private void setUpCommentsResponse(CommentBundle commentBundle) + => AddStep("set up response", () => + { + dummyAPI.HandleRequest = request => + { + if (!(request is GetCommentsRequest getCommentsRequest)) + return; + + getCommentsRequest.TriggerSuccess(commentBundle); + }; + }); + + private CommentBundle exampleComments => new CommentBundle + { + Comments = new List + { + new Comment + { + Id = 1, + Message = "This is a comment", + LegacyName = "FirstUser", + CreatedAt = DateTimeOffset.Now, + VotesCount = 19, + RepliesCount = 1 + }, + new Comment + { + Id = 5, + ParentId = 1, + Message = "This is a child comment", + LegacyName = "SecondUser", + CreatedAt = DateTimeOffset.Now, + VotesCount = 4, + }, + new Comment + { + Id = 10, + Message = "This is another comment", + LegacyName = "ThirdUser", + CreatedAt = DateTimeOffset.Now, + VotesCount = 0 + }, + }, + IncludedComments = new List(), + }; } } From 5655e090d1e1102aea9c82b461ffec7402538c65 Mon Sep 17 00:00:00 2001 From: mcendu Date: Sat, 13 Jun 2020 18:45:06 +0800 Subject: [PATCH 1095/1117] revert movement of is mania skin check statements --- .../Skinning/ManiaLegacySkinTransformer.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/ManiaLegacySkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/ManiaLegacySkinTransformer.cs index 3304330233..f386712222 100644 --- a/osu.Game.Rulesets.Mania/Skinning/ManiaLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/ManiaLegacySkinTransformer.cs @@ -78,8 +78,6 @@ namespace osu.Game.Rulesets.Mania.Skinning public Drawable GetDrawableComponent(ISkinComponent component) { - if (!isLegacySkin.Value || !hasKeyTexture.Value) - return null; switch (component) { @@ -87,6 +85,9 @@ namespace osu.Game.Rulesets.Mania.Skinning return getResult(resultComponent.Component); case ManiaSkinComponent maniaComponent: + if (!isLegacySkin.Value || !hasKeyTexture.Value) + return null; + switch (maniaComponent.Component) { case ManiaSkinComponents.ColumnBackground: From 4eeb22ca18e4598ab693c7beb0fd5309188f8ee8 Mon Sep 17 00:00:00 2001 From: mcendu Date: Sat, 13 Jun 2020 18:47:40 +0800 Subject: [PATCH 1096/1117] rename a few variables and fix typo --- .../Skinning/ManiaLegacySkinTransformer.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/ManiaLegacySkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/ManiaLegacySkinTransformer.cs index f386712222..07d0ce66e2 100644 --- a/osu.Game.Rulesets.Mania/Skinning/ManiaLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/ManiaLegacySkinTransformer.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Mania.Skinning private readonly ManiaBeatmap beatmap; /// - /// Mapping of to ther corresponding + /// Mapping of to their corresponding /// value. /// private static readonly IReadOnlyDictionary hitresult_mapping @@ -129,11 +129,11 @@ namespace osu.Game.Rulesets.Mania.Skinning private Drawable getResult(HitResult result) { - string image = GetConfig( + string filename = GetConfig( new ManiaSkinConfigurationLookup(hitresult_mapping[result]) )?.Value ?? default_hitresult_skin_filenames[result]; - return this.GetAnimation(image, true, true); + return this.GetAnimation(filename, true, true); } public Texture GetTexture(string componentName) => source.GetTexture(componentName); From e8046654c8da91496a72c341e8f2f68bd4be66da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 13 Jun 2020 12:48:16 +0200 Subject: [PATCH 1097/1117] Add failing test case --- .../Visual/Online/TestSceneCommentsContainer.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs index 26ad0b0d3f..08130e60db 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs @@ -64,6 +64,21 @@ namespace osu.Game.Tests.Visual.Online () => commentsContainer.ChildrenOfType().Single().Alpha == 1); } + [Test] + public void TestMultipleLoads() + { + var comments = exampleComments; + int topLevelCommentCount = exampleComments.Comments.Count(comment => comment.IsTopLevel); + + AddStep("hide container", () => commentsContainer.Hide()); + setUpCommentsResponse(comments); + AddRepeatStep("show comments multiple times", + () => commentsContainer.ShowComments(CommentableType.Beatmapset, 456), 2); + AddStep("show container", () => commentsContainer.Show()); + AddUntilStep("comment count is correct", + () => commentsContainer.ChildrenOfType().Count() == topLevelCommentCount); + } + private void setUpCommentsResponse(CommentBundle commentBundle) => AddStep("set up response", () => { From aab606b237953d1d9844fd36245fe8b7e42fca08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 13 Jun 2020 13:00:05 +0200 Subject: [PATCH 1098/1117] Cancel scheduled asynchronous load of comments --- osu.Game/Overlays/Comments/CommentsContainer.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Comments/CommentsContainer.cs b/osu.Game/Overlays/Comments/CommentsContainer.cs index e7bfeaf968..f71808ba89 100644 --- a/osu.Game/Overlays/Comments/CommentsContainer.cs +++ b/osu.Game/Overlays/Comments/CommentsContainer.cs @@ -12,6 +12,7 @@ using osu.Game.Online.API.Requests.Responses; using System.Threading; using System.Linq; using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Threading; using osu.Game.Users; namespace osu.Game.Overlays.Comments @@ -30,6 +31,7 @@ namespace osu.Game.Overlays.Comments private IAPIProvider api { get; set; } private GetCommentsRequest request; + private ScheduledDelegate scheduledCommentsLoad; private CancellationTokenSource loadCancellation; private int currentPage; @@ -152,8 +154,9 @@ namespace osu.Game.Overlays.Comments request?.Cancel(); loadCancellation?.Cancel(); + scheduledCommentsLoad?.Cancel(); request = new GetCommentsRequest(id.Value, type.Value, Sort.Value, currentPage++, 0); - request.Success += res => Schedule(() => onSuccess(res)); + request.Success += res => scheduledCommentsLoad = Schedule(() => onSuccess(res)); api.PerformAsync(request); } From 8402d4a5f3c2e2cac8e9b5c4948a961f0e5d1b50 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 13 Jun 2020 21:18:56 +0900 Subject: [PATCH 1099/1117] Remove newline --- osu.Game.Rulesets.Mania/Skinning/ManiaLegacySkinTransformer.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/ManiaLegacySkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/ManiaLegacySkinTransformer.cs index 07d0ce66e2..74a983fac8 100644 --- a/osu.Game.Rulesets.Mania/Skinning/ManiaLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/ManiaLegacySkinTransformer.cs @@ -78,7 +78,6 @@ namespace osu.Game.Rulesets.Mania.Skinning public Drawable GetDrawableComponent(ISkinComponent component) { - switch (component) { case GameplaySkinComponent resultComponent: From b9e247da8f3d4eeccf3448bda6d0c4554666cd55 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 13 Jun 2020 21:19:06 +0900 Subject: [PATCH 1100/1117] Simplify lookup code --- osu.Game/Skinning/LegacySkin.cs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 390dc871e4..0b2b723440 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -259,22 +259,12 @@ namespace osu.Game.Skinning return SkinUtils.As(new Bindable(existing.ColumnLineWidth[maniaLookup.TargetColumn.Value + 1])); case LegacyManiaSkinConfigurationLookups.Hit0: - return SkinUtils.As(getManiaImage(existing, "Hit0")); - case LegacyManiaSkinConfigurationLookups.Hit50: - return SkinUtils.As(getManiaImage(existing, "Hit50")); - case LegacyManiaSkinConfigurationLookups.Hit100: - return SkinUtils.As(getManiaImage(existing, "Hit100")); - case LegacyManiaSkinConfigurationLookups.Hit200: - return SkinUtils.As(getManiaImage(existing, "Hit200")); - case LegacyManiaSkinConfigurationLookups.Hit300: - return SkinUtils.As(getManiaImage(existing, "Hit300")); - case LegacyManiaSkinConfigurationLookups.Hit300g: - return SkinUtils.As(getManiaImage(existing, "Hit300g")); + return SkinUtils.As(getManiaImage(existing, maniaLookup.Lookup.ToString())); } return null; From 201bfda3382931077cc8acc26082ce740004f123 Mon Sep 17 00:00:00 2001 From: Ronnie Moir <7267697+H2n9@users.noreply.github.com> Date: Sat, 13 Jun 2020 15:16:27 +0100 Subject: [PATCH 1101/1117] Give ModTimeRamp an adjust pitch setting. Implement in ModWindDown and ModWindUp --- osu.Game/Rulesets/Mods/ModTimeRamp.cs | 16 +++++++++++++++- osu.Game/Rulesets/Mods/ModWindDown.cs | 7 +++++++ osu.Game/Rulesets/Mods/ModWindUp.cs | 7 +++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/ModTimeRamp.cs b/osu.Game/Rulesets/Mods/ModTimeRamp.cs index c1f3e357a1..a38aa2bac6 100644 --- a/osu.Game/Rulesets/Mods/ModTimeRamp.cs +++ b/osu.Game/Rulesets/Mods/ModTimeRamp.cs @@ -26,6 +26,9 @@ namespace osu.Game.Rulesets.Mods [SettingSource("Final rate", "The final speed to ramp to")] public abstract BindableNumber FinalRate { get; } + [SettingSource("Adjust Pitch", "Should pitch be adjusted with speed")] + public abstract BindableBool AdjustPitch { get; } + public override string SettingDescription => $"{InitialRate.Value:N2}x to {FinalRate.Value:N2}x"; private double finalRateTime; @@ -44,12 +47,15 @@ namespace osu.Game.Rulesets.Mods { // for preview purpose at song select. eventually we'll want to be able to update every frame. FinalRate.BindValueChanged(val => applyAdjustment(1), true); + + AdjustPitch.BindValueChanged(updatePitchAdjustment, false); } public void ApplyToTrack(Track track) { this.track = track; - track.AddAdjustment(AdjustableProperty.Frequency, SpeedChange); + + track.AddAdjustment(AdjustPitch.Value ? AdjustableProperty.Frequency : AdjustableProperty.Tempo, SpeedChange); FinalRate.TriggerChange(); } @@ -75,5 +81,13 @@ namespace osu.Game.Rulesets.Mods /// The amount of adjustment to apply (from 0..1). private void applyAdjustment(double amount) => SpeedChange.Value = InitialRate.Value + (FinalRate.Value - InitialRate.Value) * Math.Clamp(amount, 0, 1); + + private void updatePitchAdjustment(ValueChangedEvent value) + { + // remove existing old adjustment + track.RemoveAdjustment(value.OldValue ? AdjustableProperty.Frequency : AdjustableProperty.Tempo, SpeedChange); + + track.AddAdjustment(value.NewValue ? AdjustableProperty.Frequency : AdjustableProperty.Tempo, SpeedChange); + } } } diff --git a/osu.Game/Rulesets/Mods/ModWindDown.cs b/osu.Game/Rulesets/Mods/ModWindDown.cs index 5e634ac434..e46b4eff2e 100644 --- a/osu.Game/Rulesets/Mods/ModWindDown.cs +++ b/osu.Game/Rulesets/Mods/ModWindDown.cs @@ -37,6 +37,13 @@ namespace osu.Game.Rulesets.Mods Precision = 0.01, }; + [SettingSource("Adjust Pitch", "Should pitch be adjusted with speed")] + public override BindableBool AdjustPitch { get; } = new BindableBool + { + Default = true, + Value = true + }; + public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModWindUp)).ToArray(); } } diff --git a/osu.Game/Rulesets/Mods/ModWindUp.cs b/osu.Game/Rulesets/Mods/ModWindUp.cs index 74c6fc22d3..02203a474d 100644 --- a/osu.Game/Rulesets/Mods/ModWindUp.cs +++ b/osu.Game/Rulesets/Mods/ModWindUp.cs @@ -37,6 +37,13 @@ namespace osu.Game.Rulesets.Mods Precision = 0.01, }; + [SettingSource("Adjust Pitch", "Should pitch be adjusted with speed")] + public override BindableBool AdjustPitch { get; } = new BindableBool + { + Default = true, + Value = true + }; + public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModWindDown)).ToArray(); } } From 77eb428184473b09e0cb370adaf11e45e0b9ed9e Mon Sep 17 00:00:00 2001 From: Ronnie Moir <7267697+H2n9@users.noreply.github.com> Date: Sat, 13 Jun 2020 16:30:21 +0100 Subject: [PATCH 1102/1117] Use consistent setting casing --- osu.Game/Rulesets/Mods/ModWindDown.cs | 2 +- osu.Game/Rulesets/Mods/ModWindUp.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModWindDown.cs b/osu.Game/Rulesets/Mods/ModWindDown.cs index e46b4eff2e..679b50057b 100644 --- a/osu.Game/Rulesets/Mods/ModWindDown.cs +++ b/osu.Game/Rulesets/Mods/ModWindDown.cs @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Mods Precision = 0.01, }; - [SettingSource("Adjust Pitch", "Should pitch be adjusted with speed")] + [SettingSource("Adjust pitch", "Should pitch be adjusted with speed")] public override BindableBool AdjustPitch { get; } = new BindableBool { Default = true, diff --git a/osu.Game/Rulesets/Mods/ModWindUp.cs b/osu.Game/Rulesets/Mods/ModWindUp.cs index 02203a474d..b733bf423e 100644 --- a/osu.Game/Rulesets/Mods/ModWindUp.cs +++ b/osu.Game/Rulesets/Mods/ModWindUp.cs @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Mods Precision = 0.01, }; - [SettingSource("Adjust Pitch", "Should pitch be adjusted with speed")] + [SettingSource("Adjust pitch", "Should pitch be adjusted with speed")] public override BindableBool AdjustPitch { get; } = new BindableBool { Default = true, From dc5bb12fa8e9f6f47c14c57b8242b24f24aa37c3 Mon Sep 17 00:00:00 2001 From: Ronnie Moir <7267697+H2n9@users.noreply.github.com> Date: Sat, 13 Jun 2020 16:32:43 +0100 Subject: [PATCH 1103/1117] Use local helper for selecting adjusted property --- osu.Game/Rulesets/Mods/ModTimeRamp.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModTimeRamp.cs b/osu.Game/Rulesets/Mods/ModTimeRamp.cs index a38aa2bac6..9f30f340fd 100644 --- a/osu.Game/Rulesets/Mods/ModTimeRamp.cs +++ b/osu.Game/Rulesets/Mods/ModTimeRamp.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Mods [SettingSource("Final rate", "The final speed to ramp to")] public abstract BindableNumber FinalRate { get; } - [SettingSource("Adjust Pitch", "Should pitch be adjusted with speed")] + [SettingSource("Adjust pitch", "Should pitch be adjusted with speed")] public abstract BindableBool AdjustPitch { get; } public override string SettingDescription => $"{InitialRate.Value:N2}x to {FinalRate.Value:N2}x"; @@ -55,9 +55,8 @@ namespace osu.Game.Rulesets.Mods { this.track = track; - track.AddAdjustment(AdjustPitch.Value ? AdjustableProperty.Frequency : AdjustableProperty.Tempo, SpeedChange); - FinalRate.TriggerChange(); + AdjustPitch.TriggerChange(); } public virtual void ApplyToBeatmap(IBeatmap beatmap) @@ -85,9 +84,12 @@ namespace osu.Game.Rulesets.Mods private void updatePitchAdjustment(ValueChangedEvent value) { // remove existing old adjustment - track.RemoveAdjustment(value.OldValue ? AdjustableProperty.Frequency : AdjustableProperty.Tempo, SpeedChange); + track.RemoveAdjustment(adjustmentForPitchSetting(value.OldValue), SpeedChange); - track.AddAdjustment(value.NewValue ? AdjustableProperty.Frequency : AdjustableProperty.Tempo, SpeedChange); + track.AddAdjustment(adjustmentForPitchSetting(value.NewValue), SpeedChange); } + + private AdjustableProperty adjustmentForPitchSetting(bool value) + => value ? AdjustableProperty.Frequency : AdjustableProperty.Tempo; } } From 7b95c55afb17429a8eb1b253c62767c11f3792c9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 14 Jun 2020 11:33:59 +0900 Subject: [PATCH 1104/1117] Fix HardwareCorrectionOffsetClock breaking ElapsedTime readings --- osu.Game/Screens/Play/GameplayClockContainer.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index 2f85d6ad1e..fe1d22e987 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -251,8 +251,9 @@ namespace osu.Game.Screens.Play private class HardwareCorrectionOffsetClock : FramedOffsetClock { - // we always want to apply the same real-time offset, so it should be adjusted by the playback rate to achieve this. - public override double CurrentTime => SourceTime + 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 implementation already adds offset at 1.0 rate, so we only add the difference from that here. + public override double CurrentTime => base.CurrentTime + Offset * (1 - Rate); public HardwareCorrectionOffsetClock(IClock source, bool processSource = true) : base(source, processSource) From 1164a1048330211410a9f050176bf868c56a023d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 14 Jun 2020 11:34:07 +0900 Subject: [PATCH 1105/1117] Add test coverage --- .../TestSceneGameplayClockContainer.cs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 osu.Game.Tests/Gameplay/TestSceneGameplayClockContainer.cs diff --git a/osu.Game.Tests/Gameplay/TestSceneGameplayClockContainer.cs b/osu.Game.Tests/Gameplay/TestSceneGameplayClockContainer.cs new file mode 100644 index 0000000000..a97566ba7b --- /dev/null +++ b/osu.Game.Tests/Gameplay/TestSceneGameplayClockContainer.cs @@ -0,0 +1,25 @@ +// 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.Rulesets.Mods; +using osu.Game.Rulesets.Osu; +using osu.Game.Screens.Play; +using osu.Game.Tests.Visual; + +namespace osu.Game.Tests.Gameplay +{ + public class TestSceneGameplayClockContainer : OsuTestScene + { + [Test] + public void TestStartThenElapsedTime() + { + GameplayClockContainer gcc = null; + + AddStep("create container", () => Add(gcc = new GameplayClockContainer(CreateWorkingBeatmap(new OsuRuleset().RulesetInfo), Array.Empty(), 0))); + AddStep("start track", () => gcc.Start()); + AddUntilStep("elapsed greater than zero", () => gcc.GameplayClock.ElapsedFrameTime > 0); + } + } +} From abe07b742ebb16190fe0a4601f1fbbc8094748c7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 14 Jun 2020 13:20:58 +0900 Subject: [PATCH 1106/1117] Fix drag scroll in editor timeline no longer working correctly --- osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs | 2 -- .../Edit/Compose/Components/ComposeBlueprintContainer.cs | 3 +++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index d07cffff0c..cc417bbb10 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -44,8 +44,6 @@ namespace osu.Game.Screens.Edit.Compose.Components private readonly BindableList selectedHitObjects = new BindableList(); - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; - [Resolved(canBeNull: true)] private IPositionSnapProvider snapProvider { get; set; } diff --git a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs index 0b5d8262fd..e1f311f1b8 100644 --- a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs @@ -11,6 +11,7 @@ using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; +using osuTK; namespace osu.Game.Screens.Edit.Compose.Components { @@ -26,6 +27,8 @@ namespace osu.Game.Screens.Edit.Compose.Components private readonly Container placementBlueprintContainer; + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; + private InputManager inputManager; private readonly IEnumerable drawableHitObjects; From 0d53d0ffc8f361576a60ebda3ae1e5dece2c6144 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Jun 2020 00:46:20 +0900 Subject: [PATCH 1107/1117] Fix back-to-front math --- osu.Game/Screens/Play/GameplayClockContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index fe1d22e987..0653373c91 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -253,7 +253,7 @@ namespace osu.Game.Screens.Play { // 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 implementation already adds offset at 1.0 rate, so we only add the difference from that here. - public override double CurrentTime => base.CurrentTime + Offset * (1 - Rate); + public override double CurrentTime => base.CurrentTime + Offset * (Rate - 1); public HardwareCorrectionOffsetClock(IClock source, bool processSource = true) : base(source, processSource) From 9907b4763bb76963eee9d652640559f07e5fce1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 14 Jun 2020 18:39:41 +0200 Subject: [PATCH 1108/1117] Remove redundant default argument value --- osu.Game/Rulesets/Mods/ModTimeRamp.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/ModTimeRamp.cs b/osu.Game/Rulesets/Mods/ModTimeRamp.cs index 9f30f340fd..09c50ce115 100644 --- a/osu.Game/Rulesets/Mods/ModTimeRamp.cs +++ b/osu.Game/Rulesets/Mods/ModTimeRamp.cs @@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Mods // for preview purpose at song select. eventually we'll want to be able to update every frame. FinalRate.BindValueChanged(val => applyAdjustment(1), true); - AdjustPitch.BindValueChanged(updatePitchAdjustment, false); + AdjustPitch.BindValueChanged(updatePitchAdjustment); } public void ApplyToTrack(Track track) From 5f0a345eebd0410364a79b69aa0465490b48712a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 14 Jun 2020 18:48:49 +0200 Subject: [PATCH 1109/1117] Unify method naming --- osu.Game/Rulesets/Mods/ModTimeRamp.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModTimeRamp.cs b/osu.Game/Rulesets/Mods/ModTimeRamp.cs index 09c50ce115..edca3edf46 100644 --- a/osu.Game/Rulesets/Mods/ModTimeRamp.cs +++ b/osu.Game/Rulesets/Mods/ModTimeRamp.cs @@ -46,9 +46,8 @@ namespace osu.Game.Rulesets.Mods protected ModTimeRamp() { // for preview purpose at song select. eventually we'll want to be able to update every frame. - FinalRate.BindValueChanged(val => applyAdjustment(1), true); - - AdjustPitch.BindValueChanged(updatePitchAdjustment); + FinalRate.BindValueChanged(val => applyRateAdjustment(1), true); + AdjustPitch.BindValueChanged(applyPitchAdjustment); } public void ApplyToTrack(Track track) @@ -71,17 +70,17 @@ namespace osu.Game.Rulesets.Mods public virtual void Update(Playfield playfield) { - applyAdjustment((track.CurrentTime - beginRampTime) / finalRateTime); + applyRateAdjustment((track.CurrentTime - beginRampTime) / finalRateTime); } /// /// Adjust the rate along the specified ramp /// /// The amount of adjustment to apply (from 0..1). - private void applyAdjustment(double amount) => + private void applyRateAdjustment(double amount) => SpeedChange.Value = InitialRate.Value + (FinalRate.Value - InitialRate.Value) * Math.Clamp(amount, 0, 1); - private void updatePitchAdjustment(ValueChangedEvent value) + private void applyPitchAdjustment(ValueChangedEvent value) { // remove existing old adjustment track.RemoveAdjustment(adjustmentForPitchSetting(value.OldValue), SpeedChange); From e6ddd0380e2ee3b12aacd5a2fdf009ede4605672 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 14 Jun 2020 18:50:07 +0200 Subject: [PATCH 1110/1117] Rename bool arguments for readability --- osu.Game/Rulesets/Mods/ModTimeRamp.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModTimeRamp.cs b/osu.Game/Rulesets/Mods/ModTimeRamp.cs index edca3edf46..df059eef7d 100644 --- a/osu.Game/Rulesets/Mods/ModTimeRamp.cs +++ b/osu.Game/Rulesets/Mods/ModTimeRamp.cs @@ -80,15 +80,15 @@ namespace osu.Game.Rulesets.Mods private void applyRateAdjustment(double amount) => SpeedChange.Value = InitialRate.Value + (FinalRate.Value - InitialRate.Value) * Math.Clamp(amount, 0, 1); - private void applyPitchAdjustment(ValueChangedEvent value) + private void applyPitchAdjustment(ValueChangedEvent adjustPitchSetting) { // remove existing old adjustment - track.RemoveAdjustment(adjustmentForPitchSetting(value.OldValue), SpeedChange); + track.RemoveAdjustment(adjustmentForPitchSetting(adjustPitchSetting.OldValue), SpeedChange); - track.AddAdjustment(adjustmentForPitchSetting(value.NewValue), SpeedChange); + track.AddAdjustment(adjustmentForPitchSetting(adjustPitchSetting.NewValue), SpeedChange); } - private AdjustableProperty adjustmentForPitchSetting(bool value) - => value ? AdjustableProperty.Frequency : AdjustableProperty.Tempo; + private AdjustableProperty adjustmentForPitchSetting(bool adjustPitchSettingValue) + => adjustPitchSettingValue ? AdjustableProperty.Frequency : AdjustableProperty.Tempo; } } From b8fa1a2c41445264c0835d0600ddf378bd42948a Mon Sep 17 00:00:00 2001 From: Joehu Date: Sun, 14 Jun 2020 11:22:38 -0700 Subject: [PATCH 1111/1117] Add shortcut to go home --- .../Input/Bindings/GlobalActionContainer.cs | 5 +++++ .../Overlays/Toolbar/ToolbarHomeButton.cs | 19 ++++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 71771abede..618798a6d8 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -39,6 +39,8 @@ namespace osu.Game.Input.Bindings new KeyBinding(InputKey.Escape, GlobalAction.Back), new KeyBinding(InputKey.ExtraMouseButton1, GlobalAction.Back), + new KeyBinding(new[] { InputKey.Alt, InputKey.Home }, GlobalAction.Home), + new KeyBinding(InputKey.Up, GlobalAction.SelectPrevious), new KeyBinding(InputKey.Down, GlobalAction.SelectNext), @@ -152,5 +154,8 @@ namespace osu.Game.Input.Bindings [Description("Next Selection")] SelectNext, + + [Description("Home")] + Home, } } diff --git a/osu.Game/Overlays/Toolbar/ToolbarHomeButton.cs b/osu.Game/Overlays/Toolbar/ToolbarHomeButton.cs index 6f5e703a66..e642f0c453 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarHomeButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarHomeButton.cs @@ -2,10 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics.Sprites; +using osu.Framework.Input.Bindings; +using osu.Game.Input.Bindings; namespace osu.Game.Overlays.Toolbar { - public class ToolbarHomeButton : ToolbarButton + public class ToolbarHomeButton : ToolbarButton, IKeyBindingHandler { public ToolbarHomeButton() { @@ -13,5 +15,20 @@ namespace osu.Game.Overlays.Toolbar TooltipMain = "Home"; TooltipSub = "Return to the main menu"; } + + public bool OnPressed(GlobalAction action) + { + if (action == GlobalAction.Home) + { + Click(); + return true; + } + + return false; + } + + public void OnReleased(GlobalAction action) + { + } } } From 1f7679e829bb39c4bbc88a6599faa527e93a805d Mon Sep 17 00:00:00 2001 From: Joehu Date: Sun, 14 Jun 2020 11:24:23 -0700 Subject: [PATCH 1112/1117] Fix home button not flashing when pressing shortcut --- osu.Game/Overlays/Toolbar/ToolbarButton.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs index 3d66d3c28e..e0ea88fcf3 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs @@ -78,9 +78,8 @@ namespace osu.Game.Overlays.Toolbar HoverBackground = new Box { RelativeSizeAxes = Axes.Both, - Colour = OsuColour.Gray(80).Opacity(180), + Colour = OsuColour.Gray(80).Opacity(0), Blending = BlendingParameters.Additive, - Alpha = 0, }, Flow = new FillFlowContainer { @@ -146,14 +145,14 @@ namespace osu.Game.Overlays.Toolbar protected override bool OnHover(HoverEvent e) { - HoverBackground.FadeIn(200); + HoverBackground.FadeColour(OsuColour.Gray(80).Opacity(180), 200); tooltipContainer.FadeIn(100); return base.OnHover(e); } protected override void OnHoverLost(HoverLostEvent e) { - HoverBackground.FadeOut(200); + HoverBackground.FadeColour(OsuColour.Gray(80).Opacity(0), 200); tooltipContainer.FadeOut(100); } } From 978636b90c5f1f1ff943cbe4027543c595e52201 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Jun 2020 09:38:33 +0900 Subject: [PATCH 1113/1117] Fix storyboard sample playback failing when expected to play at 0ms --- osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs index f3f8308964..8292b02068 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs @@ -51,7 +51,7 @@ namespace osu.Game.Storyboards.Drawables LifetimeStart = sampleInfo.StartTime; LifetimeEnd = double.MaxValue; } - else if (Time.Current - Time.Elapsed < sampleInfo.StartTime) + else if (Time.Current - Time.Elapsed <= sampleInfo.StartTime) { // We've passed the start time of the sample. We only play the sample if we're within an allowable range // from the sample's start, to reduce layering if we've been fast-forwarded far into the future From fdf7c56ba28db7165d1651dcdfb2e69520866e35 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Jun 2020 11:18:12 +0900 Subject: [PATCH 1114/1117] Add test coverage --- .../Gameplay/TestSceneStoryboardSamples.cs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs b/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs index 84506739ab..2c85c4809b 100644 --- a/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs +++ b/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.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.IO; using System.Threading.Tasks; @@ -10,7 +11,12 @@ using osu.Framework.Audio.Sample; using osu.Framework.IO.Stores; using osu.Framework.Testing; using osu.Game.Audio; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu; +using osu.Game.Screens.Play; using osu.Game.Skinning; +using osu.Game.Storyboards; +using osu.Game.Storyboards.Drawables; using osu.Game.Tests.Resources; using osu.Game.Tests.Visual; @@ -43,6 +49,27 @@ namespace osu.Game.Tests.Gameplay AddAssert("sample is non-null", () => channel != null); } + [Test] + public void TestSamplePlaybackAtZero() + { + GameplayClockContainer gameplayContainer = null; + DrawableStoryboardSample sample = null; + + AddStep("create container", () => + { + Add(gameplayContainer = new GameplayClockContainer(CreateWorkingBeatmap(new OsuRuleset().RulesetInfo), Array.Empty(), 0)); + + gameplayContainer.Add(sample = new DrawableStoryboardSample(new StoryboardSampleInfo(string.Empty, 0, 1)) + { + Clock = gameplayContainer.GameplayClock + }); + }); + + AddStep("start time", () => gameplayContainer.Start()); + + AddUntilStep("sample playback succeeded", () => sample.LifetimeEnd < double.MaxValue); + } + private class TestSkin : LegacySkin { public TestSkin(string resourceName, AudioManager audioManager) From b41567c66c48f2fc679cb91518e70e7ee7799b88 Mon Sep 17 00:00:00 2001 From: Joehu Date: Sun, 14 Jun 2020 22:02:21 -0700 Subject: [PATCH 1115/1117] Split hover and flash to separate boxes --- osu.Game/Overlays/Toolbar/ToolbarButton.cs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs index e0ea88fcf3..cbcb4060a3 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs @@ -62,6 +62,7 @@ namespace osu.Game.Overlays.Toolbar protected ConstrainedIconContainer IconContainer; protected SpriteText DrawableText; protected Box HoverBackground; + private readonly Box FlashBackground; private readonly FillFlowContainer tooltipContainer; private readonly SpriteText tooltip1; private readonly SpriteText tooltip2; @@ -78,7 +79,14 @@ namespace osu.Game.Overlays.Toolbar HoverBackground = new Box { RelativeSizeAxes = Axes.Both, - Colour = OsuColour.Gray(80).Opacity(0), + Colour = OsuColour.Gray(80).Opacity(180), + Blending = BlendingParameters.Additive, + Alpha = 0, + }, + FlashBackground = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Transparent, Blending = BlendingParameters.Additive, }, Flow = new FillFlowContainer @@ -138,21 +146,21 @@ namespace osu.Game.Overlays.Toolbar protected override bool OnClick(ClickEvent e) { - HoverBackground.FlashColour(Color4.White.Opacity(100), 500, Easing.OutQuint); + FlashBackground.FlashColour(Color4.White.Opacity(100), 500, Easing.OutQuint); tooltipContainer.FadeOut(100); return base.OnClick(e); } protected override bool OnHover(HoverEvent e) { - HoverBackground.FadeColour(OsuColour.Gray(80).Opacity(180), 200); + HoverBackground.FadeIn(200); tooltipContainer.FadeIn(100); return base.OnHover(e); } protected override void OnHoverLost(HoverLostEvent e) { - HoverBackground.FadeColour(OsuColour.Gray(80).Opacity(0), 200); + HoverBackground.FadeOut(200); tooltipContainer.FadeOut(100); } } From 941fdf5e76a40d6d3400ab3b75a4869414c55aa6 Mon Sep 17 00:00:00 2001 From: Joehu Date: Sun, 14 Jun 2020 22:16:17 -0700 Subject: [PATCH 1116/1117] Fix flash background naming --- osu.Game/Overlays/Toolbar/ToolbarButton.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs index cbcb4060a3..e752516baf 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs @@ -62,7 +62,7 @@ namespace osu.Game.Overlays.Toolbar protected ConstrainedIconContainer IconContainer; protected SpriteText DrawableText; protected Box HoverBackground; - private readonly Box FlashBackground; + private readonly Box flashBackground; private readonly FillFlowContainer tooltipContainer; private readonly SpriteText tooltip1; private readonly SpriteText tooltip2; @@ -83,7 +83,7 @@ namespace osu.Game.Overlays.Toolbar Blending = BlendingParameters.Additive, Alpha = 0, }, - FlashBackground = new Box + flashBackground = new Box { RelativeSizeAxes = Axes.Both, Colour = Color4.Transparent, @@ -146,7 +146,7 @@ namespace osu.Game.Overlays.Toolbar protected override bool OnClick(ClickEvent e) { - FlashBackground.FlashColour(Color4.White.Opacity(100), 500, Easing.OutQuint); + flashBackground.FlashColour(Color4.White.Opacity(100), 500, Easing.OutQuint); tooltipContainer.FadeOut(100); return base.OnClick(e); } From 1770b70b8164442d21f79ea4b0e9116a91552403 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Jun 2020 16:14:35 +0900 Subject: [PATCH 1117/1117] Change implementation to ensure flashBackground is not present by default --- 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 e752516baf..86a3f5d8aa 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs @@ -86,7 +86,8 @@ namespace osu.Game.Overlays.Toolbar flashBackground = new Box { RelativeSizeAxes = Axes.Both, - Colour = Color4.Transparent, + Alpha = 0, + Colour = Color4.White.Opacity(100), Blending = BlendingParameters.Additive, }, Flow = new FillFlowContainer @@ -146,7 +147,7 @@ namespace osu.Game.Overlays.Toolbar protected override bool OnClick(ClickEvent e) { - flashBackground.FlashColour(Color4.White.Opacity(100), 500, Easing.OutQuint); + flashBackground.FadeOutFromOne(800, Easing.OutQuint); tooltipContainer.FadeOut(100); return base.OnClick(e); }
    /// Sets a replay to be used, overriding local input. diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 375976ea6c..acc8dc9c7c 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -184,7 +184,7 @@ namespace osu.Game.Screens.Play addGameplayComponents(GameplayClockContainer, Beatmap.Value, playableBeatmap); addOverlayComponents(GameplayClockContainer, Beatmap.Value); - if (!DrawableRuleset.DisplayHud) + if (!DrawableRuleset.AllowGameplayOverlays) { HUDOverlay.ShowHud.Value = false; HUDOverlay.ShowHud.Disabled = true; From a59db976d66fab80cb3035dd779c59db6070d68c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 May 2020 18:05:06 +0900 Subject: [PATCH 0623/1117] Fix loading a ruleset with a new version specification causing a crash --- osu.Game/Rulesets/RulesetInfo.cs | 16 +++++++++++++++- osu.Game/Rulesets/RulesetStore.cs | 12 ++++-------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/osu.Game/Rulesets/RulesetInfo.cs b/osu.Game/Rulesets/RulesetInfo.cs index afd499cb9e..2e32b96084 100644 --- a/osu.Game/Rulesets/RulesetInfo.cs +++ b/osu.Game/Rulesets/RulesetInfo.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics.CodeAnalysis; +using System.Linq; using Newtonsoft.Json; namespace osu.Game.Rulesets @@ -15,7 +16,20 @@ namespace osu.Game.Rulesets public string ShortName { get; set; } - public string InstantiationInfo { get; set; } + private string instantiationInfo; + + public string InstantiationInfo + { + get => instantiationInfo; + set => instantiationInfo = abbreviateInstantiationInfo(value); + } + + private string abbreviateInstantiationInfo(string value) + { + // exclude version onwards, matching only on namespace and type. + // this is mainly to allow for new versions of already loaded rulesets to "upgrade" from old. + return string.Join(',', value.Split(',').Take(2)); + } [JsonIgnore] public bool Available { get; set; } diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index f302f8700f..b8f2abd766 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -93,7 +93,9 @@ namespace osu.Game.Rulesets // add any other modes foreach (var r in instances.Where(r => !(r is ILegacyRuleset))) { - if (context.RulesetInfo.FirstOrDefault(ri => ri.InstantiationInfo == r.RulesetInfo.InstantiationInfo) == null) + // todo: StartsWith can be changed to Equals on 2020-11-08 + // This is to give users enough time to have their database use new abbreviated info). + if (context.RulesetInfo.FirstOrDefault(ri => r.RulesetInfo.InstantiationInfo.StartsWith(ri.InstantiationInfo)) == null) context.RulesetInfo.Add(r.RulesetInfo); } @@ -104,13 +106,7 @@ namespace osu.Game.Rulesets { try { - var instanceInfo = ((Ruleset)Activator.CreateInstance(Type.GetType(r.InstantiationInfo, asm => - { - // for the time being, let's ignore the version being loaded. - // this allows for debug builds to successfully load rulesets (even though debug rulesets have a 0.0.0 version). - asm.Version = null; - return Assembly.Load(asm); - }, null))).RulesetInfo; + var instanceInfo = ((Ruleset)Activator.CreateInstance(Type.GetType(r.InstantiationInfo))).RulesetInfo; r.Name = instanceInfo.Name; r.ShortName = instanceInfo.ShortName; From dcfef6b44383c48246ab2a648b13855d13e8834c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 8 May 2020 18:46:37 +0900 Subject: [PATCH 0624/1117] Add clear method to EditorBeatmap --- osu.Game/Screens/Edit/EditorBeatmap.cs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs index 2e8e03bc73..23c8c9f605 100644 --- a/osu.Game/Screens/Edit/EditorBeatmap.cs +++ b/osu.Game/Screens/Edit/EditorBeatmap.cs @@ -4,6 +4,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -201,6 +202,25 @@ namespace osu.Game.Screens.Edit updateHitObject(null, true); } + /// + /// Clears all from this . + /// + public void Clear() + { + var removed = HitObjects.ToList(); + + mutableHitObjects.Clear(); + + foreach (var b in startTimeBindables) + b.Value.UnbindAll(); + startTimeBindables.Clear(); + + foreach (var h in removed) + HitObjectRemoved?.Invoke(h); + + updateHitObject(null, true); + } + private void trackStartTime(HitObject hitObject) { startTimeBindables[hitObject] = hitObject.StartTimeBindable.GetBoundCopy(); From efff2bf15df6d588bcb6335c6bcac54c572fe23a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 8 May 2020 18:49:19 +0900 Subject: [PATCH 0625/1117] Add HitObject to DefaultsApplied event --- osu.Game.Rulesets.Osu.Tests/TestSceneOutOfOrderHits.cs | 2 +- .../Objects/Drawables/Connections/FollowPointConnection.cs | 2 +- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 7 +++++-- osu.Game/Rulesets/Objects/HitObject.cs | 4 ++-- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOutOfOrderHits.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOutOfOrderHits.cs index a6c3be7e5a..c3b4d2625e 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneOutOfOrderHits.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOutOfOrderHits.cs @@ -399,7 +399,7 @@ namespace osu.Game.Rulesets.Osu.Tests { public TestSlider() { - DefaultsApplied += () => + DefaultsApplied += _ => { HeadCircle.HitWindows = new TestHitWindows(); TailCircle.HitWindows = new TestHitWindows(); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs index 6f09bbcd57..8a0ef22c4a 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs @@ -78,7 +78,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections private void bindEvents(DrawableOsuHitObject drawableObject) { drawableObject.HitObject.PositionBindable.BindValueChanged(_ => scheduleRefresh()); - drawableObject.HitObject.DefaultsApplied += scheduleRefresh; + drawableObject.HitObject.DefaultsApplied += _ => scheduleRefresh(); } private void scheduleRefresh() diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 0047142cbd..3838e52f9b 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -148,7 +148,7 @@ namespace osu.Game.Rulesets.Objects.Drawables samplesBindable.CollectionChanged += (_, __) => loadSamples(); updateState(ArmedState.Idle, true); - onDefaultsApplied(); + apply(HitObject); } private void loadSamples() @@ -175,7 +175,10 @@ namespace osu.Game.Rulesets.Objects.Drawables AddInternal(Samples); } - private void onDefaultsApplied() => apply(HitObject); + private void onDefaultsApplied(HitObject hitObject) + { + apply(hitObject); + } private void apply(HitObject hitObject) { diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs index 9a8efdde84..cffbdbae08 100644 --- a/osu.Game/Rulesets/Objects/HitObject.cs +++ b/osu.Game/Rulesets/Objects/HitObject.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Objects /// /// Invoked after has completed on this . /// - public event Action DefaultsApplied; + public event Action DefaultsApplied; public readonly Bindable StartTimeBindable = new BindableDouble(); @@ -124,7 +124,7 @@ namespace osu.Game.Rulesets.Objects foreach (var h in nestedHitObjects) h.ApplyDefaults(controlPointInfo, difficulty); - DefaultsApplied?.Invoke(); + DefaultsApplied?.Invoke(this); } protected virtual void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) From 22dda3fe0231dc01c66591724097aef72e3c7f1a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 8 May 2020 18:49:58 +0900 Subject: [PATCH 0626/1117] Make ScrollingHitObjectContainer respond to defaults applied events --- .../Objects/Drawables/DrawableHitObject.cs | 3 + .../Scrolling/ScrollingHitObjectContainer.cs | 69 +++++++++++++------ 2 files changed, 51 insertions(+), 21 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 3838e52f9b..ba6571fe1a 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -25,6 +25,8 @@ namespace osu.Game.Rulesets.Objects.Drawables [Cached(typeof(DrawableHitObject))] public abstract class DrawableHitObject : SkinReloadableDrawable { + public event Action DefaultsApplied; + public readonly HitObject HitObject; /// @@ -178,6 +180,7 @@ namespace osu.Game.Rulesets.Objects.Drawables private void onDefaultsApplied(HitObject hitObject) { apply(hitObject); + DefaultsApplied?.Invoke(this); } private void apply(HitObject hitObject) diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index 57f58be55a..3e01bb1d31 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -16,17 +16,23 @@ namespace osu.Game.Rulesets.UI.Scrolling { private readonly IBindable timeRange = new BindableDouble(); private readonly IBindable direction = new Bindable(); + private readonly Dictionary hitObjectInitialStateCache = new Dictionary(); [Resolved] private IScrollingInfo scrollingInfo { get; set; } - private readonly LayoutValue initialStateCache = new LayoutValue(Invalidation.RequiredParentSizeToFit | Invalidation.DrawInfo); + // Responds to changes in the layout. When the layout is changes, all hit object states must be recomputed. + private readonly LayoutValue layoutCache = new LayoutValue(Invalidation.RequiredParentSizeToFit | Invalidation.DrawInfo); + + // A combined cache across all hit object states to reduce per-update iterations. + // When invalidated, one or more (but not necessarily all) hitobject states must be re-validated. + private readonly Cached combinedObjCache = new Cached(); public ScrollingHitObjectContainer() { RelativeSizeAxes = Axes.Both; - AddLayout(initialStateCache); + AddLayout(layoutCache); } [BackgroundDependencyLoader] @@ -35,13 +41,14 @@ namespace osu.Game.Rulesets.UI.Scrolling direction.BindTo(scrollingInfo.Direction); timeRange.BindTo(scrollingInfo.TimeRange); - direction.ValueChanged += _ => initialStateCache.Invalidate(); - timeRange.ValueChanged += _ => initialStateCache.Invalidate(); + direction.ValueChanged += _ => layoutCache.Invalidate(); + timeRange.ValueChanged += _ => layoutCache.Invalidate(); } public override void Add(DrawableHitObject hitObject) { - initialStateCache.Invalidate(); + combinedObjCache.Invalidate(); + hitObject.DefaultsApplied += onDefaultsApplied; base.Add(hitObject); } @@ -51,8 +58,10 @@ namespace osu.Game.Rulesets.UI.Scrolling if (result) { - initialStateCache.Invalidate(); + combinedObjCache.Invalidate(); hitObjectInitialStateCache.Remove(hitObject); + + hitObject.DefaultsApplied -= onDefaultsApplied; } return result; @@ -60,23 +69,45 @@ namespace osu.Game.Rulesets.UI.Scrolling public override void Clear(bool disposeChildren = true) { + foreach (var h in Objects) + h.DefaultsApplied -= onDefaultsApplied; + base.Clear(disposeChildren); - initialStateCache.Invalidate(); + combinedObjCache.Invalidate(); hitObjectInitialStateCache.Clear(); } + private void onDefaultsApplied(DrawableHitObject drawableObject) + { + // The cache may not exist if the hitobject state hasn't been computed yet (e.g. if the hitobject was added + defaults applied in the same frame). + // In such a case, combinedObjCache will take care of updating the hitobject. + if (hitObjectInitialStateCache.TryGetValue(drawableObject, out var objCache)) + { + combinedObjCache.Invalidate(); + objCache.Invalidate(); + } + } + private float scrollLength; protected override void Update() { base.Update(); - if (!initialStateCache.IsValid) + if (!layoutCache.IsValid) { foreach (var cached in hitObjectInitialStateCache.Values) cached.Invalidate(); + combinedObjCache.Invalidate(); + scrollingInfo.Algorithm.Reset(); + + layoutCache.Validate(); + } + + if (!combinedObjCache.IsValid) + { switch (direction.Value) { case ScrollingDirection.Up: @@ -89,15 +120,21 @@ namespace osu.Game.Rulesets.UI.Scrolling break; } - scrollingInfo.Algorithm.Reset(); - foreach (var obj in Objects) { + if (!hitObjectInitialStateCache.TryGetValue(obj, out var objCache)) + objCache = hitObjectInitialStateCache[obj] = new Cached(); + + if (objCache.IsValid) + return; + computeLifetimeStartRecursive(obj); computeInitialStateRecursive(obj); + + objCache.Validate(); } - initialStateCache.Validate(); + combinedObjCache.Validate(); } } @@ -109,8 +146,6 @@ namespace osu.Game.Rulesets.UI.Scrolling computeLifetimeStartRecursive(obj); } - private readonly Dictionary hitObjectInitialStateCache = new Dictionary(); - private double computeOriginAdjustedLifetimeStart(DrawableHitObject hitObject) { float originAdjustment = 0.0f; @@ -142,12 +177,6 @@ namespace osu.Game.Rulesets.UI.Scrolling // Cant use AddOnce() since the delegate is re-constructed every invocation private void computeInitialStateRecursive(DrawableHitObject hitObject) => hitObject.Schedule(() => { - if (!hitObjectInitialStateCache.TryGetValue(hitObject, out var cached)) - cached = hitObjectInitialStateCache[hitObject] = new Cached(); - - if (cached.IsValid) - return; - if (hitObject.HitObject is IHasEndTime e) { switch (direction.Value) @@ -171,8 +200,6 @@ namespace osu.Game.Rulesets.UI.Scrolling // Nested hitobjects don't need to scroll, but they do need accurate positions updatePosition(obj, hitObject.HitObject.StartTime); } - - cached.Validate(); }); protected override void UpdateAfterChildrenLife() From d67facf8e4c28443840af1519ec7be669ef552d3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 8 May 2020 18:50:06 +0900 Subject: [PATCH 0627/1117] Add test scene --- .../TestSceneManiaHitObjectComposer.cs | 49 ++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs index 286e3f6e50..1554a956a2 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs @@ -10,10 +10,13 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Testing; +using osu.Framework.Utils; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Edit; +using osu.Game.Rulesets.Mania.Edit.Blueprints; using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI.Scrolling; @@ -48,6 +51,8 @@ namespace osu.Game.Rulesets.Mania.Tests DrawableHitObject lastObject = null; Vector2 originalPosition = Vector2.Zero; + setScrollStep(ScrollingDirection.Up); + AddStep("seek to last object", () => { lastObject = this.ChildrenOfType().Single(d => d.HitObject == composer.EditorBeatmap.HitObjects.Last()); @@ -81,7 +86,7 @@ namespace osu.Game.Rulesets.Mania.Tests DrawableHitObject lastObject = null; Vector2 originalPosition = Vector2.Zero; - AddStep("set down scroll", () => ((Bindable)composer.Composer.ScrollingInfo.Direction).Value = ScrollingDirection.Down); + setScrollStep(ScrollingDirection.Down); AddStep("seek to last object", () => { @@ -116,6 +121,8 @@ namespace osu.Game.Rulesets.Mania.Tests DrawableHitObject lastObject = null; Vector2 originalPosition = Vector2.Zero; + setScrollStep(ScrollingDirection.Down); + AddStep("seek to last object", () => { lastObject = this.ChildrenOfType().Single(d => d.HitObject == composer.EditorBeatmap.HitObjects.Last()); @@ -147,6 +154,46 @@ namespace osu.Game.Rulesets.Mania.Tests AddAssert("hitobjects not moved vertically", () => lastObject.DrawPosition.Y - originalPosition.Y <= DefaultNotePiece.NOTE_HEIGHT); } + [Test] + public void TestDragHoldNoteSelectionVertically() + { + setScrollStep(ScrollingDirection.Down); + + AddStep("setup beatmap", () => + { + composer.EditorBeatmap.Clear(); + composer.EditorBeatmap.Add(new HoldNote + { + Column = 1, + EndTime = 200 + }); + }); + + DrawableHoldNote holdNote = null; + + AddStep("grab hold note", () => + { + holdNote = this.ChildrenOfType().FirstOrDefault(); + InputManager.MoveMouseTo(holdNote); + InputManager.PressButton(MouseButton.Left); + }); + + AddStep("move drag upwards", () => + { + InputManager.MoveMouseTo(holdNote, new Vector2(0, -100)); + InputManager.ReleaseButton(MouseButton.Left); + }); + + AddAssert("head note positioned correctly", () => Precision.AlmostEquals(holdNote.ScreenSpaceDrawQuad.BottomLeft, holdNote.Head.ScreenSpaceDrawQuad.BottomLeft)); + AddAssert("tail note positioned correctly", () => Precision.AlmostEquals(holdNote.ScreenSpaceDrawQuad.TopLeft, holdNote.Tail.ScreenSpaceDrawQuad.BottomLeft)); + + AddAssert("head blueprint positioned correctly", () => this.ChildrenOfType().ElementAt(0).DrawPosition == holdNote.Head.DrawPosition); + AddAssert("tail blueprint positioned correctly", () => this.ChildrenOfType().ElementAt(1).DrawPosition == holdNote.Tail.DrawPosition); + } + + private void setScrollStep(ScrollingDirection direction) + => AddStep($"set scroll direction = {direction}", () => ((Bindable)composer.Composer.ScrollingInfo.Direction).Value = direction); + private class TestComposer : CompositeDrawable { [Cached(typeof(EditorBeatmap))] From abd1115c6d1dcf068e7eccd48a987dc27fe54839 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 May 2020 19:08:43 +0900 Subject: [PATCH 0628/1117] Fix test failures --- osu.Game/OsuGameBase.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index d9f9e2de42..cf39c03f9d 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -302,7 +302,8 @@ namespace osu.Game { base.SetHost(host); - Storage = new OsuStorage(host); + if (Storage == null) // may be non-null for certain tests + Storage = new OsuStorage(host); if (LocalConfig == null) LocalConfig = new OsuConfigManager(Storage); From 1a31e1f10fe91f646e18b285c6312a4d8ebf86be Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 May 2020 19:13:41 +0900 Subject: [PATCH 0629/1117] Also check for AffectsCombo to avoid too many passing switches --- osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs b/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs index 027fe1f302..8cf8b9e05d 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs @@ -35,8 +35,12 @@ namespace osu.Game.Rulesets.Taiko.Skinning LastResult.BindValueChanged(result => { + var r = result.NewValue; + + bool passing = r == null || (r.Judgement.AffectsCombo && r.Type > HitResult.Miss); + foreach (var sprite in InternalChildren.OfType()) - sprite.Passing = result.NewValue == null || result.NewValue.Type > HitResult.Miss; + sprite.Passing = passing; }, true); } From 115cbf25ae21c9f1a1d950f0783c875ff3bfd082 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 May 2020 19:15:19 +0900 Subject: [PATCH 0630/1117] Fix new sprites not getting spawned with correct passing state --- osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs b/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs index 8cf8b9e05d..9ddfe75b40 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs @@ -29,6 +29,8 @@ namespace osu.Game.Rulesets.Taiko.Skinning ((IBindable)LastResult).BindTo(gameplayBeatmap.LastJudgementResult); } + private bool passing; + protected override void LoadComplete() { base.LoadComplete(); @@ -37,7 +39,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning { var r = result.NewValue; - bool passing = r == null || (r.Judgement.AffectsCombo && r.Type > HitResult.Miss); + passing = r == null || (r.Judgement.AffectsCombo && r.Type > HitResult.Miss); foreach (var sprite in InternalChildren.OfType()) sprite.Passing = passing; @@ -71,7 +73,10 @@ namespace osu.Game.Rulesets.Taiko.Skinning if (last != null && last.ScreenSpaceDrawQuad.TopRight.X >= ScreenSpaceDrawQuad.TopRight.X) break; - AddInternal(new ScrollerSprite()); + AddInternal(new ScrollerSprite + { + Passing = passing + }); } } From be3b77cf25708ceb77d14d830a473f09d0403bfc Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 8 May 2020 20:09:59 +0900 Subject: [PATCH 0631/1117] Fix potentially skipping hitobject updates --- osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index 3e01bb1d31..ea72061216 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -126,7 +126,7 @@ namespace osu.Game.Rulesets.UI.Scrolling objCache = hitObjectInitialStateCache[obj] = new Cached(); if (objCache.IsValid) - return; + continue; computeLifetimeStartRecursive(obj); computeInitialStateRecursive(obj); From 5c2778d5f08288211a1cd57b14beb85e456a513b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 May 2020 20:33:02 +0900 Subject: [PATCH 0632/1117] Change comparison direction --- osu.Game/Rulesets/RulesetStore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index b8f2abd766..b3026bf2b7 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -95,7 +95,7 @@ namespace osu.Game.Rulesets { // todo: StartsWith can be changed to Equals on 2020-11-08 // This is to give users enough time to have their database use new abbreviated info). - if (context.RulesetInfo.FirstOrDefault(ri => r.RulesetInfo.InstantiationInfo.StartsWith(ri.InstantiationInfo)) == null) + if (context.RulesetInfo.FirstOrDefault(ri => ri.InstantiationInfo.StartsWith(r.RulesetInfo.InstantiationInfo)) == null) context.RulesetInfo.Add(r.RulesetInfo); } From d1976b194d9bd2314775eb56dcbb1125738d0075 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Sat, 9 May 2020 10:42:56 +0300 Subject: [PATCH 0633/1117] Check local availability before disabling buttons --- .../BeatmapListing/Panels/BeatmapPanelDownloadButton.cs | 2 +- osu.Game/Overlays/BeatmapSet/Header.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs index 589f2d5072..6d5862ee2b 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs @@ -50,7 +50,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels [BackgroundDependencyLoader(true)] private void load(OsuGame game, BeatmapManager beatmaps, OsuConfigManager osuConfig) { - if (BeatmapSet.Value?.OnlineInfo?.Availability?.DownloadDisabled ?? false) + if ((BeatmapSet.Value?.OnlineInfo?.Availability?.DownloadDisabled ?? false) && State.Value != DownloadState.LocallyAvailable) { button.Enabled.Value = false; button.TooltipText = "this beatmap is currently not available for download."; diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs index 1ff08aab2c..06e31277dd 100644 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ b/osu.Game/Overlays/BeatmapSet/Header.cs @@ -264,7 +264,7 @@ namespace osu.Game.Overlays.BeatmapSet { if (BeatmapSet.Value == null) return; - if (BeatmapSet.Value.OnlineInfo.Availability?.DownloadDisabled ?? false) + if ((BeatmapSet.Value.OnlineInfo.Availability?.DownloadDisabled ?? false) && State.Value != DownloadState.LocallyAvailable) { downloadButtonsContainer.Clear(); return; From 74cbe9306c6403c4bb7e99303306b1be194666c3 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Sat, 9 May 2020 16:39:46 +0800 Subject: [PATCH 0634/1117] Use strongly-typed JsonConerter. --- .../Serialization/Converters/TypedListConverter.cs | 12 ++++-------- .../Serialization/Converters/Vector2Converter.cs | 14 +++++--------- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs index 64f1ebeb1a..25fe335047 100644 --- a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs +++ b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs @@ -14,7 +14,7 @@ namespace osu.Game.IO.Serialization.Converters /// reconstruct the objects with their original types. /// /// The type of objects contained in the this attribute is attached to. - public class TypedListConverter : JsonConverter + public class TypedListConverter : JsonConverter> { private readonly bool requiresTypeVersion; @@ -36,9 +36,7 @@ namespace osu.Game.IO.Serialization.Converters this.requiresTypeVersion = requiresTypeVersion; } - public override bool CanConvert(Type objectType) => objectType == typeof(List); - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + public override List ReadJson(JsonReader reader, Type objectType, List existingValue, bool hasExistingValue, JsonSerializer serializer) { var list = new List(); @@ -59,14 +57,12 @@ namespace osu.Game.IO.Serialization.Converters return list; } - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + public override void WriteJson(JsonWriter writer, List value, JsonSerializer serializer) { - var list = (IEnumerable)value; - var lookupTable = new List(); var objects = new List(); - foreach (var item in list) + foreach (var item in value) { var type = item.GetType(); var assemblyName = type.Assembly.GetName(); diff --git a/osu.Game/IO/Serialization/Converters/Vector2Converter.cs b/osu.Game/IO/Serialization/Converters/Vector2Converter.cs index bf5edeef94..46447b607b 100644 --- a/osu.Game/IO/Serialization/Converters/Vector2Converter.cs +++ b/osu.Game/IO/Serialization/Converters/Vector2Converter.cs @@ -11,26 +11,22 @@ namespace osu.Game.IO.Serialization.Converters /// /// A type of that serializes only the X and Y coordinates of a . /// - public class Vector2Converter : JsonConverter + public class Vector2Converter : JsonConverter { - public override bool CanConvert(Type objectType) => objectType == typeof(Vector2); - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + public override Vector2 ReadJson(JsonReader reader, Type objectType, Vector2 existingValue, bool hasExistingValue, JsonSerializer serializer) { var obj = JObject.Load(reader); return new Vector2((float)obj["x"], (float)obj["y"]); } - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + public override void WriteJson(JsonWriter writer, Vector2 value, JsonSerializer serializer) { - var vector = (Vector2)value; - writer.WriteStartObject(); writer.WritePropertyName("x"); - writer.WriteValue(vector.X); + writer.WriteValue(value.X); writer.WritePropertyName("y"); - writer.WriteValue(vector.Y); + writer.WriteValue(value.Y); writer.WriteEndObject(); } From 55e0d91f37591061869917968a0897ff10ef726d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 9 May 2020 18:09:17 +0900 Subject: [PATCH 0635/1117] Fix download button being disabled after importing a download disabled beatmap --- .../Panels/BeatmapPanelDownloadButton.cs | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs index 6d5862ee2b..67782dfe3f 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs @@ -50,13 +50,6 @@ namespace osu.Game.Overlays.BeatmapListing.Panels [BackgroundDependencyLoader(true)] private void load(OsuGame game, BeatmapManager beatmaps, OsuConfigManager osuConfig) { - if ((BeatmapSet.Value?.OnlineInfo?.Availability?.DownloadDisabled ?? false) && State.Value != DownloadState.LocallyAvailable) - { - button.Enabled.Value = false; - button.TooltipText = "this beatmap is currently not available for download."; - return; - } - noVideoSetting = osuConfig.GetBindable(OsuSetting.PreferNoVideo); button.Action = () => @@ -81,6 +74,26 @@ namespace osu.Game.Overlays.BeatmapListing.Panels break; } }; + + State.BindValueChanged(state => + { + switch (state.NewValue) + { + case DownloadState.LocallyAvailable: + button.Enabled.Value = true; + button.TooltipText = string.Empty; + break; + + default: + if (BeatmapSet.Value?.OnlineInfo?.Availability?.DownloadDisabled ?? false) + { + button.Enabled.Value = false; + button.TooltipText = "this beatmap is currently not available for download."; + } + + break; + } + }, true); } } } From 80a193a61608397d3a0b01e6e115445d2e26783b Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Sat, 9 May 2020 17:34:40 +0800 Subject: [PATCH 0636/1117] Use IEnumerable for TypedListConverter. --- .../IO/Serialization/Converters/TypedListConverter.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs index 25fe335047..ec0036dae2 100644 --- a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs +++ b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs @@ -9,12 +9,12 @@ using Newtonsoft.Json.Linq; namespace osu.Game.IO.Serialization.Converters { /// - /// A type of that serializes a alongside + /// A type of that serializes an alongside /// a lookup table for the types contained. The lookup table is used in deserialization to /// reconstruct the objects with their original types. /// - /// The type of objects contained in the this attribute is attached to. - public class TypedListConverter : JsonConverter> + /// The type of objects contained in the this attribute is attached to. + public class TypedListConverter : JsonConverter> { private readonly bool requiresTypeVersion; @@ -36,7 +36,7 @@ namespace osu.Game.IO.Serialization.Converters this.requiresTypeVersion = requiresTypeVersion; } - public override List ReadJson(JsonReader reader, Type objectType, List existingValue, bool hasExistingValue, JsonSerializer serializer) + public override IEnumerable ReadJson(JsonReader reader, Type objectType, IEnumerable existingValue, bool hasExistingValue, JsonSerializer serializer) { var list = new List(); @@ -57,7 +57,7 @@ namespace osu.Game.IO.Serialization.Converters return list; } - public override void WriteJson(JsonWriter writer, List value, JsonSerializer serializer) + public override void WriteJson(JsonWriter writer, IEnumerable value, JsonSerializer serializer) { var lookupTable = new List(); var objects = new List(); From fa711a6456a4ca25eb74a3fda8da3977e13a8a63 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 9 May 2020 19:11:51 +0900 Subject: [PATCH 0637/1117] Fix null reference causing hard freeze if game is forcefully closed during disclaimer --- 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 fdc8d94352..b86be9858f 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -914,7 +914,7 @@ namespace osu.Game if (ScreenStack.CurrentScreen is Loader) return false; - if (introScreen.DidLoadMenu && !(ScreenStack.CurrentScreen is IntroScreen)) + if (introScreen?.DidLoadMenu == true && !(ScreenStack.CurrentScreen is IntroScreen)) { Scheduler.Add(introScreen.MakeCurrent); return true; From 3f78ddf482b7b74c399aa4adf0791156813860e2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 9 May 2020 19:13:18 +0900 Subject: [PATCH 0638/1117] Add CanBeNull hinting --- osu.Game/OsuGame.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index b86be9858f..3caffb6db5 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -18,6 +18,7 @@ using osu.Game.Screens.Menu; using System.Linq; using System.Threading; using System.Threading.Tasks; +using JetBrains.Annotations; using osu.Framework.Audio; using osu.Framework.Bindables; using osu.Framework.Development; @@ -97,6 +98,7 @@ namespace osu.Game private MainMenu menuScreen; + [CanBeNull] private IntroScreen introScreen; private Bindable configRuleset; From bbebd26efb46b2091a53a966c08faca896b20e15 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 9 May 2020 20:13:31 +0900 Subject: [PATCH 0639/1117] Use DirectoryInfo in more places --- osu.Game/IO/OsuStorage.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/osu.Game/IO/OsuStorage.cs b/osu.Game/IO/OsuStorage.cs index 955aae7b68..e178cb0a02 100644 --- a/osu.Game/IO/OsuStorage.cs +++ b/osu.Game/IO/OsuStorage.cs @@ -41,20 +41,18 @@ namespace osu.Game.IO public void Migrate(string newLocation) { - string oldLocation = GetFullPath("."); + var source = new DirectoryInfo(GetFullPath(".")); + var destination = new DirectoryInfo(newLocation); // ensure the new location has no files present, else hard abort - if (Directory.Exists(newLocation)) + if (destination.Exists) { - if (Directory.GetFiles(newLocation).Length > 0) + if (destination.GetFiles().Length > 0) throw new InvalidOperationException("Migration destination already has files present"); - Directory.Delete(newLocation, true); + deleteRecursive(destination); } - var source = new DirectoryInfo(oldLocation); - var destination = new DirectoryInfo(newLocation); - copyRecursive(source, destination); ChangeTargetStorage(host.GetStorage(newLocation)); From 5dda94187e54d3980507872f69947330a5bfc62d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 9 May 2020 20:13:37 +0900 Subject: [PATCH 0640/1117] Add more edge case testing --- .../NonVisual/CustomDataDirectoryTest.cs | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs index 7f08fad5be..8688ecd078 100644 --- a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs +++ b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs @@ -123,6 +123,56 @@ namespace osu.Game.Tests.NonVisual } } + [Test] + public void TestMigrationBetweenTwoTargets() + { + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestMigrationBetweenTwoTargets))) + { + try + { + var osu = loadOsu(host); + var storage = osu.Dependencies.Get(); + + string customPath2 = $"{customPath}-2"; + + const string database_filename = "client.db"; + + Assert.DoesNotThrow(() => (storage as OsuStorage)?.Migrate(customPath)); + Assert.That(File.Exists(Path.Combine(customPath, database_filename))); + + Assert.DoesNotThrow(() => (storage as OsuStorage)?.Migrate(customPath2)); + Assert.That(File.Exists(Path.Combine(customPath2, database_filename))); + + Assert.DoesNotThrow(() => (storage as OsuStorage)?.Migrate(customPath)); + Assert.That(File.Exists(Path.Combine(customPath, database_filename))); + } + finally + { + host.Exit(); + } + } + } + + [Test] + public void TestMigrationToSameTargetFails() + { + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestMigrationToSameTargetFails))) + { + try + { + var osu = loadOsu(host); + var storage = osu.Dependencies.Get(); + + Assert.DoesNotThrow(() => (storage as OsuStorage)?.Migrate(customPath)); + Assert.Throws(() => (storage as OsuStorage)?.Migrate(customPath)); + } + finally + { + host.Exit(); + } + } + } + private OsuGameBase loadOsu(GameHost host) { var osu = new OsuGameBase(); From 3565fe1cb27602557d3b6e8522feb2e792f08a67 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 10 May 2020 07:51:39 +0900 Subject: [PATCH 0641/1117] Fix incorrect passing logic --- osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs b/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs index 9ddfe75b40..8f2c25e9b2 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs @@ -39,7 +39,11 @@ namespace osu.Game.Rulesets.Taiko.Skinning { var r = result.NewValue; - passing = r == null || (r.Judgement.AffectsCombo && r.Type > HitResult.Miss); + // always ignore hitobjects that don't affect combo (drumroll ticks etc.) + if (r?.Judgement.AffectsCombo == false) + return; + + passing = r == null || r.Type > HitResult.Miss; foreach (var sprite in InternalChildren.OfType()) sprite.Passing = passing; From 5902cd81a40ed2fbf21a92f6bb041cc1d8b3bcd4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 10 May 2020 07:52:54 +0900 Subject: [PATCH 0642/1117] Move passing transforms to post-load for safety --- .../Skinning/LegacyTaikoScroller.cs | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs b/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs index 8f2c25e9b2..1ecdb839fb 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs @@ -101,16 +101,8 @@ namespace osu.Game.Rulesets.Taiko.Skinning passing = value; - if (passing) - { - passingSprite.Show(); - failingSprite.FadeOut(200); - } - else - { - failingSprite.FadeIn(200); - passingSprite.Delay(200).FadeOut(); - } + if (IsLoaded) + updatePassing(); } } @@ -127,6 +119,8 @@ namespace osu.Game.Rulesets.Taiko.Skinning passingSprite = new Sprite { Texture = skin.GetTexture("taiko-slider") }, failingSprite = new Sprite { Texture = skin.GetTexture("taiko-slider-fail"), Alpha = 0 }, }; + + updatePassing(); } protected override void Update() @@ -136,6 +130,20 @@ namespace osu.Game.Rulesets.Taiko.Skinning foreach (var c in InternalChildren) c.Scale = new Vector2(DrawHeight / c.Height); } + + private void updatePassing() + { + if (passing) + { + passingSprite.Show(); + failingSprite.FadeOut(200); + } + else + { + failingSprite.FadeIn(200); + passingSprite.Delay(200).FadeOut(); + } + } } } } From 384862d48b5e614488ca4d19354c2481aca74a8d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 10 May 2020 13:17:37 +0900 Subject: [PATCH 0643/1117] Fix incorrect relative paths when using GetStorageForDirectory --- .../NonVisual/CustomDataDirectoryTest.cs | 45 ++++++++++++++++++- osu.Game/IO/WrappedStorage.cs | 12 ++++- 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs index d741bc5de1..7c559ea6d2 100644 --- a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs +++ b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs @@ -3,6 +3,7 @@ using System; using System.IO; +using System.Linq; using System.Threading; using System.Threading.Tasks; using NUnit.Framework; @@ -36,6 +37,8 @@ namespace osu.Game.Tests.NonVisual } } + private string customPath => Path.Combine(Environment.CurrentDirectory, "custom-path"); + [Test] public void TestCustomDirectory() { @@ -49,7 +52,7 @@ namespace osu.Game.Tests.NonVisual storage.DeleteDirectory(string.Empty); using (var storageConfig = new StorageConfigManager(storage)) - storageConfig.Set(StorageConfig.FullPath, Path.Combine(Environment.CurrentDirectory, "custom-path")); + storageConfig.Set(StorageConfig.FullPath, customPath); try { @@ -58,7 +61,45 @@ namespace osu.Game.Tests.NonVisual // switch to DI'd storage storage = osu.Dependencies.Get(); - Assert.That(storage.GetFullPath("."), Is.EqualTo(Path.Combine(Environment.CurrentDirectory, "custom-path"))); + Assert.That(storage.GetFullPath("."), Is.EqualTo(customPath)); + } + finally + { + host.Exit(); + } + } + } + + [Test] + public void TestSubDirectoryLookup() + { + using (var host = new HeadlessGameHost(nameof(TestSubDirectoryLookup))) + { + string headlessPrefix = Path.Combine("headless", nameof(TestSubDirectoryLookup)); + + // need access before the game has constructed its own storage yet. + Storage storage = new DesktopStorage(headlessPrefix, host); + // manual cleaning so we can prepare a config file. + storage.DeleteDirectory(string.Empty); + + using (var storageConfig = new StorageConfigManager(storage)) + storageConfig.Set(StorageConfig.FullPath, customPath); + + try + { + var osu = loadOsu(host); + + // switch to DI'd storage + storage = osu.Dependencies.Get(); + + string actualTestFile = Path.Combine(customPath, "rulesets", "test"); + + File.WriteAllText(actualTestFile, "test"); + + var rulesetStorage = storage.GetStorageForDirectory("rulesets"); + var lookupPath = rulesetStorage.GetFiles(".").Single(); + + Assert.That(lookupPath, Is.EqualTo("test")); } finally { diff --git a/osu.Game/IO/WrappedStorage.cs b/osu.Game/IO/WrappedStorage.cs index 705bbf6840..cc59e2cc28 100644 --- a/osu.Game/IO/WrappedStorage.cs +++ b/osu.Game/IO/WrappedStorage.cs @@ -48,10 +48,18 @@ namespace osu.Game.IO UnderlyingStorage.Delete(MutatePath(path)); public override IEnumerable GetDirectories(string path) => - UnderlyingStorage.GetDirectories(MutatePath(path)); + ToLocalRelative(UnderlyingStorage.GetDirectories(MutatePath(path))); + + public IEnumerable ToLocalRelative(IEnumerable paths) + { + string localRoot = GetFullPath(string.Empty); + + foreach (var path in paths) + yield return Path.GetRelativePath(localRoot, UnderlyingStorage.GetFullPath(path)); + } public override IEnumerable GetFiles(string path, string pattern = "*") => - UnderlyingStorage.GetFiles(MutatePath(path), pattern); + ToLocalRelative(UnderlyingStorage.GetFiles(MutatePath(path), pattern)); public override Stream GetStream(string path, FileAccess access = FileAccess.Read, FileMode mode = FileMode.OpenOrCreate) => UnderlyingStorage.GetStream(MutatePath(path), access, mode); From 738c6da594f8a0838e33c7c0d8a73d3f79ef510a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 10 May 2020 13:39:20 +0900 Subject: [PATCH 0644/1117] Implement midi keybindings --- osu.Game/Overlays/KeyBinding/KeyBindingRow.cs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs index 58ca2143f9..01d5991d3e 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs @@ -250,6 +250,28 @@ namespace osu.Game.Overlays.KeyBinding finalise(); } + protected override bool OnMidiDown(MidiDownEvent e) + { + if (!HasFocus) + return false; + + bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(e.CurrentState)); + finalise(); + + return true; + } + + protected override void OnMidiUp(MidiUpEvent e) + { + if (!HasFocus) + { + base.OnMidiUp(e); + return; + } + + finalise(); + } + private void clear() { bindTarget.UpdateKeyCombination(InputKey.None); From 2f12c4126a10676982cc301c58e98ab376d2f493 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Sun, 10 May 2020 13:49:08 +0900 Subject: [PATCH 0645/1117] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- .../TestSceneManiaHitObjectComposer.cs | 2 +- osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs index 1554a956a2..48159c817d 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs @@ -173,7 +173,7 @@ namespace osu.Game.Rulesets.Mania.Tests AddStep("grab hold note", () => { - holdNote = this.ChildrenOfType().FirstOrDefault(); + holdNote = this.ChildrenOfType().Single(); InputManager.MoveMouseTo(holdNote); InputManager.PressButton(MouseButton.Left); }); diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index ea72061216..15e625872d 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.UI.Scrolling [Resolved] private IScrollingInfo scrollingInfo { get; set; } - // Responds to changes in the layout. When the layout is changes, all hit object states must be recomputed. + // Responds to changes in the layout. When the layout changes, all hit object states must be recomputed. private readonly LayoutValue layoutCache = new LayoutValue(Invalidation.RequiredParentSizeToFit | Invalidation.DrawInfo); // A combined cache across all hit object states to reduce per-update iterations. From 44fdf1e6b05b033977287647e126e4ed99b898e4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 10 May 2020 20:09:41 +0900 Subject: [PATCH 0646/1117] Reword xmldoc --- osu.Game/Rulesets/UI/DrawableRuleset.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index 5f0a4b0975..9a10b7d1b2 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -488,12 +488,8 @@ namespace osu.Game.Rulesets.UI protected virtual ResumeOverlay CreateResumeOverlay() => null; /// - /// Whether to display gameplay overlays with this ruleset. - /// Override to false to completely disable the display of gameplay overlays. + /// Whether to display gameplay overlays, such as and . /// - /// - /// Gameplay overlays refer here to in player as well as . - /// public virtual bool AllowGameplayOverlays => true; /// From 5bab53b04ce53325aab9991f2b811034580ef957 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 10 May 2020 17:05:30 +0200 Subject: [PATCH 0647/1117] Centralise trail visibility state management --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 558555af96..9cce46d730 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -74,7 +74,7 @@ namespace osu.Game.Rulesets.Catch.UI dashing = value; - trails.DisplayTrail = value || HyperDashing; + updateTrailVisibility(); } } @@ -255,10 +255,7 @@ namespace osu.Game.Rulesets.Catch.UI hyperDashDirection = 0; if (wasHyperDashing) - { - updateCatcherColour(false); - trails.DisplayTrail &= Dashing; - } + runHyperDashStateTransition(false); } else { @@ -268,16 +265,18 @@ namespace osu.Game.Rulesets.Catch.UI if (!wasHyperDashing) { - updateCatcherColour(true); - - trails.DisplayTrail = true; trails.DisplayEndGlow(); + runHyperDashStateTransition(true); } } } - private void updateCatcherColour(bool hyperDashing) + private void runHyperDashStateTransition(bool hyperDashing) { + trails.HyperDashTrailsColour = hyperDashColour; + trails.EndGlowSpritesColour = hyperDashEndGlowColour; + updateTrailVisibility(); + if (hyperDashing) { this.FadeColour(hyperDashColour, HYPER_DASH_TRANSITION_DURATION, Easing.OutQuint); @@ -288,11 +287,10 @@ namespace osu.Game.Rulesets.Catch.UI this.FadeColour(Color4.White, HYPER_DASH_TRANSITION_DURATION, Easing.OutQuint); this.FadeTo(1f, HYPER_DASH_TRANSITION_DURATION, Easing.OutQuint); } - - trails.HyperDashTrailsColour = hyperDashColour; - trails.EndGlowSpritesColour = hyperDashEndGlowColour; } + private void updateTrailVisibility() => trails.DisplayTrail = Dashing || HyperDashing; + public bool OnPressed(CatchAction action) { switch (action) @@ -393,7 +391,7 @@ namespace osu.Game.Rulesets.Catch.UI skin.GetConfig(CatchSkinColour.HyperDashAfterImage)?.Value ?? hyperDashColour; - updateCatcherColour(HyperDashing); + runHyperDashStateTransition(HyperDashing); } protected override void Update() From 1d999bb634de168c5acb1f3abbc5fd12d5596e31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 10 May 2020 18:32:38 +0200 Subject: [PATCH 0648/1117] Integrate PeriodTracker changes --- .../Scoring/DrainingHealthProcessor.cs | 56 +++++-------------- 1 file changed, 15 insertions(+), 41 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs b/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs index b36e42326c..1958efdd6f 100644 --- a/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs +++ b/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs @@ -5,9 +5,9 @@ using System; using System.Collections.Generic; 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.Utils; namespace osu.Game.Rulesets.Scoring { @@ -49,33 +49,7 @@ namespace osu.Game.Rulesets.Scoring private double targetMinimumHealth; private double drainRate = 1; - private readonly List<(double startTime, double endTime)> nonDrainSections = new List<(double, double)>(); - private int currentNonDrainSection; - - private bool isInNonDrainSection - { - get - { - if (nonDrainSections.Count == 0) - return false; - - var time = Time.Current; - - if (time > nonDrainSections[currentNonDrainSection].endTime) - { - while (time > nonDrainSections[currentNonDrainSection].endTime && currentNonDrainSection < nonDrainSections.Count - 1) - currentNonDrainSection++; - } - else - { - while (time < nonDrainSections[currentNonDrainSection].startTime && currentNonDrainSection > 0) - currentNonDrainSection--; - } - - var closestSection = nonDrainSections[currentNonDrainSection]; - return time >= closestSection.startTime && time <= closestSection.endTime; - } - } + private PeriodTracker noDrainPeriodTracker; /// /// Creates a new . @@ -90,7 +64,7 @@ namespace osu.Game.Rulesets.Scoring { base.Update(); - if (isInNonDrainSection) + 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 @@ -102,23 +76,23 @@ namespace osu.Game.Rulesets.Scoring public override void ApplyBeatmap(IBeatmap beatmap) { - nonDrainSections.Clear(); - this.beatmap = beatmap; if (beatmap.HitObjects.Count > 0) gameplayEndTime = beatmap.HitObjects[^1].GetEndTime(); - // Ranges between the end of last hit object before a break - // and the start of first hit object after a break should - // not allow HP draining. (with break periods in) - foreach (BreakPeriod b in beatmap.Breaks) - { - var startTime = beatmap.HitObjects.LastOrDefault(h => h.GetEndTime() < b.StartTime)?.GetEndTime() ?? double.MinValue; - var endTime = beatmap.HitObjects.FirstOrDefault(h => h.StartTime > b.EndTime)?.StartTime ?? double.MaxValue; - - nonDrainSections.Add((startTime, endTime)); - } + 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 = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, min_health_target, mid_health_target, max_health_target); From 916d8245e6c951a5b9d31d61a7aec3226f2c4922 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 May 2020 12:50:47 +0900 Subject: [PATCH 0649/1117] Don't timeout on long beatmap load when debugging --- osu.Game/Beatmaps/WorkingBeatmap.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index d2804bdc05..bf2b9944a4 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -84,7 +85,7 @@ namespace osu.Game.Beatmaps public IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, TimeSpan? timeout = null) { - using (var cancellationSource = new CancellationTokenSource(timeout ?? TimeSpan.FromSeconds(10))) + using (var cancellationSource = createCancellationTokenSource(timeout)) { mods ??= Array.Empty(); @@ -181,6 +182,15 @@ namespace osu.Game.Beatmaps beatmapLoadTask = null; } + private CancellationTokenSource createCancellationTokenSource(TimeSpan? timeout) + { + if (Debugger.IsAttached) + // ignore timeout when debugger is attached (may be breakpointing / debugging). + return new CancellationTokenSource(); + + return new CancellationTokenSource(timeout ?? TimeSpan.FromSeconds(10)); + } + private Task loadBeatmapAsync() => beatmapLoadTask ??= Task.Factory.StartNew(() => { // Todo: Handle cancellation during beatmap parsing From b4d790c076904a1bd24bd086fae7d159ccd06614 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 May 2020 12:53:54 +0900 Subject: [PATCH 0650/1117] Fix taiko sample mapping for strong hits --- .../Beatmaps/TaikoBeatmapConverter.cs | 30 +++++++++++++++++-- .../Objects/Drawables/DrawableHit.cs | 8 +++++ .../Drawables/DrawableTaikoHitObject.cs | 4 +-- 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index caf645d5a2..822931396a 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -167,13 +167,39 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps default: { - bool isRim = samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE); + bool isRimDefinition(HitSampleInfo s) => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE; + + bool isRim = samples.Any(isRimDefinition); + + if (isRim) + { + // consume then remove the rim definition sample types. + var updatedSamples = samples.Where(s => !isRimDefinition(s)).ToList(); + + // strong + rim always maps to whistle. + if (strong) + { + for (var i = 0; i < updatedSamples.Count; i++) + { + var s = samples[i]; + + if (s.Name != HitSampleInfo.HIT_FINISH) + continue; + + var sClone = s.Clone(); + sClone.Name = HitSampleInfo.HIT_WHISTLE; + updatedSamples[i] = sClone; + } + } + + samples = updatedSamples; + } yield return new Hit { StartTime = obj.StartTime, Type = isRim ? HitType.Rim : HitType.Centre, - Samples = obj.Samples, + Samples = samples, IsStrong = strong }; diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index d2671eadda..d4dc3316e7 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -2,9 +2,11 @@ // 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.Graphics; +using osu.Game.Audio; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; @@ -47,6 +49,12 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables ? new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.CentreHit), _ => new CentreHitCirclePiece(), confineMode: ConfineMode.ScaleToFit) : new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.RimHit), _ => new RimHitCirclePiece(), confineMode: ConfineMode.ScaleToFit); + protected override IEnumerable GetSamples() + { + // normal and claps are always handled by the drum (see DrumSampleMapping). + return HitObject.Samples.Where(s => s.Name != HitSampleInfo.HIT_NORMAL && s.Name != HitSampleInfo.HIT_CLAP); + } + protected override void CheckForResult(bool userTriggered, double timeOffset) { Debug.Assert(HitObject.HitWindows != null); diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs index 1be04f1760..90daf3950c 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs @@ -165,8 +165,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables return base.CreateNestedHitObject(hitObject); } - // Normal and clap samples are handled by the drum - protected override IEnumerable GetSamples() => HitObject.Samples.Where(s => s.Name != HitSampleInfo.HIT_NORMAL && s.Name != HitSampleInfo.HIT_CLAP); + // Most osu!taiko hitsounds are managed by the drum (see DrumSampleMapping). + protected override IEnumerable GetSamples() => Enumerable.Empty(); protected abstract SkinnableDrawable CreateMainPiece(); From f1959338446a0c98345330e61d6beb8ca8deed6c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 11 May 2020 13:03:59 +0900 Subject: [PATCH 0651/1117] Fix hardrock potentially taking a long time to load --- osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs index cf6677a55d..e0577dd464 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs @@ -28,8 +28,11 @@ namespace osu.Game.Rulesets.Osu.Mods slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y)); slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y)); - foreach (var point in slider.Path.ControlPoints) + var controlPoints = slider.Path.ControlPoints.Select(p => new PathControlPoint(p.Position.Value, p.Type.Value)).ToArray(); + foreach (var point in controlPoints) point.Position.Value = new Vector2(point.Position.Value.X, -point.Position.Value.Y); + + slider.Path = new SliderPath(controlPoints, slider.Path.ExpectedDistance.Value); } } } From 43342c57b885f3baabf0fc73212d1adf679677da Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Mon, 11 May 2020 07:13:06 +0200 Subject: [PATCH 0652/1117] Fix switch case ... caused by a poor merge --- osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs index f0d0ce05b5..1096b8db00 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs @@ -91,6 +91,8 @@ namespace osu.Game.Rulesets.Taiko.Skinning if (GetTexture("taiko-slider") != null) return new LegacyTaikoScroller(); + return null; + case TaikoSkinComponents.TaikoDon: if (GetTexture("pippidonclear0") != null) return new DrawableTaikoMascot(); From 77041bdbb571a738da0c22f10948059642502bb2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 May 2020 16:19:47 +0900 Subject: [PATCH 0653/1117] Move implementation to DrawableHit to avoid "breaking" legacy encoding --- .../Beatmaps/TaikoBeatmapConverter.cs | 24 ----------------- .../Objects/Drawables/DrawableHit.cs | 27 ++++++++++++++++++- 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index 822931396a..d324441285 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -171,30 +171,6 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps bool isRim = samples.Any(isRimDefinition); - if (isRim) - { - // consume then remove the rim definition sample types. - var updatedSamples = samples.Where(s => !isRimDefinition(s)).ToList(); - - // strong + rim always maps to whistle. - if (strong) - { - for (var i = 0; i < updatedSamples.Count; i++) - { - var s = samples[i]; - - if (s.Name != HitSampleInfo.HIT_FINISH) - continue; - - var sClone = s.Clone(); - sClone.Name = HitSampleInfo.HIT_WHISTLE; - updatedSamples[i] = sClone; - } - } - - samples = updatedSamples; - } - yield return new Hit { StartTime = obj.StartTime, diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index d4dc3316e7..d332f90cd4 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -52,7 +52,32 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables protected override IEnumerable GetSamples() { // normal and claps are always handled by the drum (see DrumSampleMapping). - return HitObject.Samples.Where(s => s.Name != HitSampleInfo.HIT_NORMAL && s.Name != HitSampleInfo.HIT_CLAP); + var samples = HitObject.Samples.Where(s => s.Name != HitSampleInfo.HIT_NORMAL && s.Name != HitSampleInfo.HIT_CLAP); + + if (HitObject.Type == HitType.Rim && HitObject.IsStrong) + { + // strong + rim always maps to whistle. + // TODO: this should really be in the legacy decoder, but can't be because legacy encoding parity would be broken. + // when we add a taiko editor, this is probably not going to play nice. + + var corrected = samples.ToList(); + + for (var i = 0; i < corrected.Count; i++) + { + var s = corrected[i]; + + if (s.Name != HitSampleInfo.HIT_FINISH) + continue; + + var sClone = s.Clone(); + sClone.Name = HitSampleInfo.HIT_WHISTLE; + corrected[i] = sClone; + } + + return corrected; + } + + return samples; } protected override void CheckForResult(bool userTriggered, double timeOffset) From 93440874db8d92e300d0e62508e5cf44e343f4bb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 May 2020 16:30:54 +0900 Subject: [PATCH 0654/1117] Refactor beatmap encoder test to be a bit easier to understand --- .../Formats/LegacyBeatmapEncoderTest.cs | 36 +++++++++---------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs index bcc873b0b7..64efe08929 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs @@ -28,14 +28,15 @@ namespace osu.Game.Tests.Beatmaps.Formats private static IEnumerable allBeatmaps => TestResources.GetStore().GetAvailableResources().Where(res => res.EndsWith(".osu")); [TestCaseSource(nameof(allBeatmaps))] - public void TestBeatmap(string name) + public void TestEncodeDecodeStability(string name) { - var decoded = decode(name, out var encoded); + var decoded = decodeFromLegacy(TestResources.GetStore().GetStream(name)); + var decodedAfterEncode = decodeFromLegacy(encodeToLegacy(decoded)); sort(decoded); - sort(encoded); + sort(decodedAfterEncode); - Assert.That(encoded.Serialize(), Is.EqualTo(decoded.Serialize())); + Assert.That(decodedAfterEncode.Serialize(), Is.EqualTo(decoded.Serialize())); } private void sort(IBeatmap beatmap) @@ -48,27 +49,22 @@ namespace osu.Game.Tests.Beatmaps.Formats } } - private IBeatmap decode(string filename, out IBeatmap encoded) + private IBeatmap decodeFromLegacy(Stream stream) { - using (var stream = TestResources.GetStore().GetStream(filename)) - using (var sr = new LineBufferedReader(stream)) - { - var legacyDecoded = convert(new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(sr)); + using (var reader = new LineBufferedReader(stream)) + return convert(new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(reader)); + } - using (var ms = new MemoryStream()) - using (var sw = new StreamWriter(ms)) - using (var sr2 = new LineBufferedReader(ms, true)) - { - new LegacyBeatmapEncoder(legacyDecoded).Encode(sw); + private Stream encodeToLegacy(IBeatmap beatmap) + { + var stream = new MemoryStream(); - sw.Flush(); - ms.Position = 0; + using (var writer = new StreamWriter(stream, leaveOpen: true)) + new LegacyBeatmapEncoder(beatmap).Encode(writer); - encoded = convert(new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(sr2)); + stream.Position = 0; - return legacyDecoded; - } - } + return stream; } private IBeatmap convert(IBeatmap beatmap) From 7f7d5e6617a127de2729e01b53b7c7b34608dfc1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 May 2020 16:37:08 +0900 Subject: [PATCH 0655/1117] Fix apparently required argument --- osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs index 64efe08929..b1782f7f08 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs @@ -6,6 +6,7 @@ using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text; using NUnit.Framework; using osu.Framework.Audio.Track; using osu.Framework.Graphics.Textures; @@ -59,7 +60,7 @@ namespace osu.Game.Tests.Beatmaps.Formats { var stream = new MemoryStream(); - using (var writer = new StreamWriter(stream, leaveOpen: true)) + using (var writer = new StreamWriter(stream, Encoding.UTF8, leaveOpen: true)) new LegacyBeatmapEncoder(beatmap).Encode(writer); stream.Position = 0; From b9e5009dd5066fb3976725592b04c63812a622c1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 May 2020 17:09:34 +0900 Subject: [PATCH 0656/1117] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index a406cdf08a..69f897128c 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 5ccfaaac9e..c6dba8da13 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -24,7 +24,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index dc83d937f7..f78fd2e4ff 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -80,7 +80,7 @@ - + From ca6e6f7496f5468df67644e47c6b1c01bd0b82c6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 11 May 2020 17:26:11 +0900 Subject: [PATCH 0657/1117] Add required parameter for android build --- osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs index b1782f7f08..30331e98d2 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs @@ -60,7 +60,7 @@ namespace osu.Game.Tests.Beatmaps.Formats { var stream = new MemoryStream(); - using (var writer = new StreamWriter(stream, Encoding.UTF8, leaveOpen: true)) + using (var writer = new StreamWriter(stream, Encoding.UTF8, 1024, true)) new LegacyBeatmapEncoder(beatmap).Encode(writer); stream.Position = 0; From 6c350db0973c74f0294cc975c2c2b3aa26bf17a1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 May 2020 21:37:07 +0900 Subject: [PATCH 0658/1117] Add connection flushing support --- .../NonVisual/CustomDataDirectoryTest.cs | 14 ++++++-------- osu.Game/Database/DatabaseContextFactory.cs | 8 ++++++++ osu.Game/OsuGameBase.cs | 8 ++++++++ 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs index 4bce5056ce..ed83ff358c 100644 --- a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs +++ b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs @@ -137,7 +137,7 @@ namespace osu.Game.Tests.NonVisual Assert.That(storage.GetFullPath("."), Is.EqualTo(defaultStorageLocation)); - (storage as OsuStorage)?.Migrate(customPath); + osu.Migrate(customPath); Assert.That(storage.GetFullPath("."), Is.EqualTo(customPath)); @@ -170,19 +170,18 @@ namespace osu.Game.Tests.NonVisual try { var osu = loadOsu(host); - var storage = osu.Dependencies.Get(); string customPath2 = $"{customPath}-2"; const string database_filename = "client.db"; - Assert.DoesNotThrow(() => (storage as OsuStorage)?.Migrate(customPath)); + Assert.DoesNotThrow(() => osu.Migrate(customPath)); Assert.That(File.Exists(Path.Combine(customPath, database_filename))); - Assert.DoesNotThrow(() => (storage as OsuStorage)?.Migrate(customPath2)); + Assert.DoesNotThrow(() => osu.Migrate(customPath2)); Assert.That(File.Exists(Path.Combine(customPath2, database_filename))); - Assert.DoesNotThrow(() => (storage as OsuStorage)?.Migrate(customPath)); + Assert.DoesNotThrow(() => osu.Migrate(customPath)); Assert.That(File.Exists(Path.Combine(customPath, database_filename))); } finally @@ -200,10 +199,9 @@ namespace osu.Game.Tests.NonVisual try { var osu = loadOsu(host); - var storage = osu.Dependencies.Get(); - Assert.DoesNotThrow(() => (storage as OsuStorage)?.Migrate(customPath)); - Assert.Throws(() => (storage as OsuStorage)?.Migrate(customPath)); + Assert.DoesNotThrow(() => osu.Migrate(customPath)); + Assert.Throws(() => osu.Migrate(customPath)); } finally { diff --git a/osu.Game/Database/DatabaseContextFactory.cs b/osu.Game/Database/DatabaseContextFactory.cs index 1ed5fb3268..1cceb59b11 100644 --- a/osu.Game/Database/DatabaseContextFactory.cs +++ b/osu.Game/Database/DatabaseContextFactory.cs @@ -160,5 +160,13 @@ namespace osu.Game.Database } } } + + public void FlushConnections() + { + foreach (var context in threadContexts.Values) + context.Dispose(); + + recycleThreadContexts(); + } } } diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 8fbde67afe..6282f5cb8b 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -328,6 +328,8 @@ namespace osu.Game { base.Dispose(isDisposing); RulesetStore?.Dispose(); + + ContextFactory.FlushConnections(); } private class OsuUserInputManager : UserInputManager @@ -355,5 +357,11 @@ namespace osu.Game public override bool ChangeFocusOnClick => false; } } + + public void Migrate(string path) + { + ContextFactory.FlushConnections(); + (Storage as OsuStorage)?.Migrate(path); + } } } From a11be07bb122b3e1c186cfc418d31a48a1f8869d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 May 2020 21:38:27 +0900 Subject: [PATCH 0659/1117] Move log storage location after migration complete --- osu.Game/IO/OsuStorage.cs | 9 ++++++--- osu.Game/IO/WrappedStorage.cs | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game/IO/OsuStorage.cs b/osu.Game/IO/OsuStorage.cs index e178cb0a02..7e1c676324 100644 --- a/osu.Game/IO/OsuStorage.cs +++ b/osu.Game/IO/OsuStorage.cs @@ -33,10 +33,13 @@ namespace osu.Game.IO var customStoragePath = storageConfig.Get(StorageConfig.FullPath); if (!string.IsNullOrEmpty(customStoragePath)) - { ChangeTargetStorage(host.GetStorage(customStoragePath)); - Logger.Storage = UnderlyingStorage.GetStorageForDirectory("logs"); - } + } + + protected override void ChangeTargetStorage(Storage newStorage) + { + base.ChangeTargetStorage(newStorage); + Logger.Storage = UnderlyingStorage.GetStorageForDirectory("logs"); } public void Migrate(string newLocation) diff --git a/osu.Game/IO/WrappedStorage.cs b/osu.Game/IO/WrappedStorage.cs index cc59e2cc28..646faba9eb 100644 --- a/osu.Game/IO/WrappedStorage.cs +++ b/osu.Game/IO/WrappedStorage.cs @@ -27,7 +27,7 @@ namespace osu.Game.IO protected virtual string MutatePath(string path) => !string.IsNullOrEmpty(subPath) ? Path.Combine(subPath, path) : path; - protected void ChangeTargetStorage(Storage newStorage) + protected virtual void ChangeTargetStorage(Storage newStorage) { UnderlyingStorage = newStorage; } From 984f27c107ed817ee677a4642e995c594b8805db Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 May 2020 21:38:41 +0900 Subject: [PATCH 0660/1117] Add simple retry logic on file copy failure (may be in use) --- osu.Game/IO/OsuStorage.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/osu.Game/IO/OsuStorage.cs b/osu.Game/IO/OsuStorage.cs index 7e1c676324..7c0b90e208 100644 --- a/osu.Game/IO/OsuStorage.cs +++ b/osu.Game/IO/OsuStorage.cs @@ -4,6 +4,7 @@ using System; using System.IO; using System.Linq; +using System.Threading; using osu.Framework.Logging; using osu.Framework.Platform; using osu.Game.Configuration; @@ -95,7 +96,20 @@ namespace osu.Game.IO if (IGNORE_FILES.Contains(fi.Name)) continue; - fi.CopyTo(Path.Combine(destination.FullName, fi.Name), true); + int tries = 5; + + while (tries-- > 0) + { + try + { + fi.CopyTo(Path.Combine(destination.FullName, fi.Name), true); + break; + } + catch (Exception) + { + Thread.Sleep(50); + } + } } foreach (DirectoryInfo dir in source.GetDirectories()) From e650b10b5e1adfb00123064e043d5768eb0e409f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 11 May 2020 19:03:13 +0200 Subject: [PATCH 0661/1117] Add test case for maximal break --- .../TestSceneDrainingHealthProcessor.cs | 75 +++++++++---------- 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs b/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs index 2f83ea4832..e50b2231bf 100644 --- a/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs +++ b/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.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. -using System.Collections.Generic; -using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Utils; @@ -43,52 +41,61 @@ namespace osu.Game.Tests.Gameplay } [Test] - public void TestHealthNotDrainedBeforeBreak() + public void TestHealthDrainBetweenBreakAndObjects() { - createProcessor(createBeatmap(0, 2000, - new BreakPeriod(400, 600), new BreakPeriod(1200, 1400))); + createProcessor(createBeatmap(0, 2000, new BreakPeriod(325, 375))); - setTime(300); + // 275 300 325 350 375 400 425 + // hitobjects o o + // break [-------------] + // no drain [---------------------------] + + setTime(285); setHealth(1); - setTime(400); - assertHealthEqualTo(1); + setTime(295); + assertHealthNotEqualTo(1); - setTime(1100); + setTime(305); setHealth(1); - setTime(1200); + setTime(315); assertHealthEqualTo(1); + + setTime(365); + assertHealthEqualTo(1); + + setTime(395); + assertHealthEqualTo(1); + + setTime(425); + assertHealthNotEqualTo(1); } [Test] - public void TestHealthNotDrainedDuringBreak() + public void TestHealthDrainDuringMaximalBreak() { - createProcessor(createBeatmap(0, 2000, new BreakPeriod(0, 1200))); + createProcessor(createBeatmap(0, 2000, new BreakPeriod(300, 400))); - setTime(700); - assertHealthEqualTo(1); - setTime(900); - assertHealthEqualTo(1); - } + // 275 300 325 350 375 400 425 + // hitobjects o o + // break [---------------------------] + // no drain [---------------------------] - [Test] - public void TestHealthNotDrainedAfterBreak() - { - createProcessor(createBeatmap(0, 2000, - new BreakPeriod(400, 600), new BreakPeriod(1200, 1400))); - - setTime(600); + setTime(285); setHealth(1); - setTime(700); - assertHealthEqualTo(1); + setTime(295); + assertHealthNotEqualTo(1); - setTime(1400); + setTime(305); setHealth(1); - setTime(1500); + setTime(395); assertHealthEqualTo(1); + + setTime(425); + assertHealthNotEqualTo(1); } [Test] @@ -154,24 +161,16 @@ namespace osu.Game.Tests.Gameplay { var beatmap = new Beatmap { - BeatmapInfo = { BaseDifficulty = { DrainRate = 5 } }, + BeatmapInfo = { BaseDifficulty = { DrainRate = 10 } }, }; - double time = startTime; - - while (time <= endTime) + for (double time = startTime; time <= endTime; time += 100) { beatmap.HitObjects.Add(new JudgeableHitObject { StartTime = time }); - - // leave a 100ms gap between the start and end of a break period. - time += (getCurrentBreak(breaks, time)?.Duration ?? 0) + 100; } beatmap.Breaks.AddRange(breaks); - static BreakPeriod getCurrentBreak(IEnumerable breaks, double time) => - breaks?.FirstOrDefault(b => time >= b.StartTime && time <= b.EndTime); - return beatmap; } From 848a3fb6d74ec3b443029cc048934c1df4ea728f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 11 May 2020 19:06:36 +0200 Subject: [PATCH 0662/1117] Take hitobject start/end times into account in drain --- osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs b/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs index 1958efdd6f..982f527517 100644 --- a/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs +++ b/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs @@ -84,12 +84,12 @@ namespace osu.Game.Rulesets.Scoring noDrainPeriodTracker = new PeriodTracker(beatmap.Breaks.Select(breakPeriod => new Period( beatmap.HitObjects .Select(hitObject => hitObject.GetEndTime()) - .Where(endTime => endTime < breakPeriod.StartTime) + .Where(endTime => endTime <= breakPeriod.StartTime) .DefaultIfEmpty(double.MinValue) .Last(), beatmap.HitObjects .Select(hitObject => hitObject.StartTime) - .Where(startTime => startTime > breakPeriod.EndTime) + .Where(startTime => startTime >= breakPeriod.EndTime) .DefaultIfEmpty(double.MaxValue) .First() ))); From 35e7cee458f66391d6bb7e705a34fdfeb2bb65a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=80=8C=E7=A9=BA=E7=99=BD=E3=80=8D?= <「空白」> Date: Tue, 12 May 2020 03:18:47 +0900 Subject: [PATCH 0663/1117] Squash commits from private fork Temporary comments left to-remove later --- .../Online/API/Requests/ResponseWithCursor.cs | 9 ++ .../API/Requests/SearchBeatmapSetsRequest.cs | 27 +++++- .../API/Requests/SearchBeatmapSetsResponse.cs | 2 +- .../BeatmapListingFilterControl.cs | 57 +++++++---- .../BeatmapListing/BeatmapListingPager.cs | 92 ++++++++++++++++++ osu.Game/Overlays/BeatmapListingOverlay.cs | 94 ++++++++++++++----- 6 files changed, 236 insertions(+), 45 deletions(-) create mode 100644 osu.Game/Overlays/BeatmapListing/BeatmapListingPager.cs diff --git a/osu.Game/Online/API/Requests/ResponseWithCursor.cs b/osu.Game/Online/API/Requests/ResponseWithCursor.cs index e38e73dd01..51e88ca52b 100644 --- a/osu.Game/Online/API/Requests/ResponseWithCursor.cs +++ b/osu.Game/Online/API/Requests/ResponseWithCursor.cs @@ -13,4 +13,13 @@ namespace osu.Game.Online.API.Requests [JsonProperty("cursor")] public dynamic CursorJson; } + + public abstract class ResponseWithCursor : ResponseWithCursor where T : class + { + /// + /// Cursor deserialized into T class type (cannot implicitly convert type to object using raw Cursor) + /// + [JsonProperty("cursor")] + public T Cursor; + } } diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs index 047496b473..fb2cc66dd8 100644 --- a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs +++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs @@ -5,11 +5,21 @@ using osu.Framework.IO.Network; using osu.Game.Overlays; using osu.Game.Overlays.BeatmapListing; using osu.Game.Rulesets; +using Newtonsoft.Json; namespace osu.Game.Online.API.Requests { public class SearchBeatmapSetsRequest : APIRequest { + public class Cursor + { + [JsonProperty("approved_date")] + public string ApprovedDate; + + [JsonProperty("_id")] + public string Id; + } + public SearchCategory SearchCategory { get; set; } public SortCriteria SortCriteria { get; set; } @@ -22,17 +32,20 @@ namespace osu.Game.Online.API.Requests private readonly string query; private readonly RulesetInfo ruleset; + private readonly Cursor cursor; private string directionString => SortDirection == SortDirection.Descending ? @"desc" : @"asc"; - public SearchBeatmapSetsRequest(string query, RulesetInfo ruleset) + public SearchBeatmapSetsRequest(string query, RulesetInfo ruleset, Cursor cursor = null, + SearchCategory searchCategory = SearchCategory.Any, SortCriteria sortCriteria = SortCriteria.Ranked, SortDirection sortDirection = SortDirection.Descending) { this.query = string.IsNullOrEmpty(query) ? string.Empty : System.Uri.EscapeDataString(query); this.ruleset = ruleset; + this.cursor = cursor; - SearchCategory = SearchCategory.Any; - SortCriteria = SortCriteria.Ranked; - SortDirection = SortDirection.Descending; + SearchCategory = searchCategory; + SortCriteria = sortCriteria; + SortDirection = sortDirection; Genre = SearchGenre.Any; Language = SearchLanguage.Any; } @@ -55,6 +68,12 @@ namespace osu.Game.Online.API.Requests req.AddParameter("sort", $"{SortCriteria.ToString().ToLowerInvariant()}_{directionString}"); + if (cursor != null) + { + req.AddParameter("cursor[_id]", cursor.Id); + req.AddParameter("cursor[approved_date]", cursor.ApprovedDate); + } + return req; } diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsResponse.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsResponse.cs index 3c4fb11ed1..2adf7004e8 100644 --- a/osu.Game/Online/API/Requests/SearchBeatmapSetsResponse.cs +++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsResponse.cs @@ -7,7 +7,7 @@ using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Online.API.Requests { - public class SearchBeatmapSetsResponse : ResponseWithCursor + public class SearchBeatmapSetsResponse : ResponseWithCursor { [JsonProperty("beatmapsets")] public IEnumerable BeatmapSets; diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs index 4dd60c7113..c3e8505ddc 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -13,7 +12,6 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Threading; using osu.Game.Beatmaps; using osu.Game.Online.API; -using osu.Game.Online.API.Requests; using osu.Game.Rulesets; using osuTK; using osuTK.Graphics; @@ -24,6 +22,8 @@ namespace osu.Game.Overlays.BeatmapListing { public Action> SearchFinished; public Action SearchStarted; + /// List of currently displayed beatmap entries + private List currentBeatmaps; [Resolved] private IAPIProvider api { get; set; } @@ -35,7 +35,7 @@ namespace osu.Game.Overlays.BeatmapListing private readonly BeatmapListingSortTabControl sortControl; private readonly Box sortControlBackground; - private SearchBeatmapSetsRequest getSetsRequest; + private BeatmapListingPager beatmapListingPager; public BeatmapListingFilterControl() { @@ -115,12 +115,13 @@ namespace osu.Game.Overlays.BeatmapListing } private ScheduledDelegate queryChangedDebounce; + private ScheduledDelegate queryPagingDebounce; private void queueUpdateSearch(bool queryTextChanged = false) { SearchStarted?.Invoke(); - getSetsRequest?.Cancel(); + beatmapListingPager?.Reset(); queryChangedDebounce?.Cancel(); queryChangedDebounce = Scheduler.AddDelayed(updateSearch, queryTextChanged ? 500 : 100); @@ -128,37 +129,55 @@ namespace osu.Game.Overlays.BeatmapListing private void updateSearch() { - getSetsRequest = new SearchBeatmapSetsRequest(searchControl.Query.Value, searchControl.Ruleset.Value) - { - SearchCategory = searchControl.Category.Value, - SortCriteria = sortControl.Current.Value, - SortDirection = sortControl.SortDirection.Value, - Genre = searchControl.Genre.Value, - Language = searchControl.Language.Value - }; + beatmapListingPager = new BeatmapListingPager( + api, + rulesets, + searchControl.Query.Value, + searchControl.Ruleset.Value, + searchControl.Category.Value, + sortControl.Current.Value, + sortControl.SortDirection.Value + ); - getSetsRequest.Success += response => Schedule(() => onSearchFinished(response)); + queryPagingDebounce?.Cancel(); + queryPagingDebounce = null; + beatmapListingPager.PageFetched += onSearchFinished; - api.Queue(getSetsRequest); + AddPageToResult(); } - private void onSearchFinished(SearchBeatmapSetsResponse response) + private void onSearchFinished(List beatmaps) { - var beatmaps = response.BeatmapSets.Select(r => r.ToBeatmapSet(rulesets)).ToList(); - - searchControl.BeatmapSet = response.Total == 0 ? null : beatmaps.First(); + queryPagingDebounce = Scheduler.AddDelayed(() => queryPagingDebounce = null, 1000); + if (currentBeatmaps == null || !beatmapListingPager.IsPastFirstPage) + currentBeatmaps = beatmaps; + else + currentBeatmaps.AddRange(beatmaps); + SearchFinished?.Invoke(beatmaps); } protected override void Dispose(bool isDisposing) { - getSetsRequest?.Cancel(); + beatmapListingPager?.Reset(); queryChangedDebounce?.Cancel(); + queryPagingDebounce?.Cancel(); base.Dispose(isDisposing); } public void TakeFocus() => searchControl.TakeFocus(); + + /// Request next 50 matches if available + public void AddPageToResult() + { + if (beatmapListingPager == null || !beatmapListingPager.CanFetchNextPage) + return; + if (queryPagingDebounce != null) + return; + + beatmapListingPager.FetchNextPage(); + } } } diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingPager.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingPager.cs new file mode 100644 index 0000000000..66faf8df7a --- /dev/null +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingPager.cs @@ -0,0 +1,92 @@ +// 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.Game.Beatmaps; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Rulesets; + +namespace osu.Game.Overlays.BeatmapListing +{ + public class BeatmapListingPager + { + private readonly IAPIProvider api; + private readonly RulesetStore rulesets; + private readonly string query; + private readonly RulesetInfo ruleset; + private readonly SearchCategory searchCategory; + private readonly SortCriteria sortCriteria; + private readonly SortDirection sortDirection; + + public event PageFetchHandler PageFetched; + private SearchBeatmapSetsRequest getSetsRequest; + private SearchBeatmapSetsResponse lastResponse; + + /// Reports end of results + private bool isLastPageFetched = false; + /// Job in process lock flag + private bool isFetching => getSetsRequest != null; + /// Whether beatmaps should be appended or replaced + public bool IsPastFirstPage { get; private set; } = false; + /// call FetchNextPage() safe-check + public bool CanFetchNextPage => !isLastPageFetched && !isFetching; + + public BeatmapListingPager(IAPIProvider api, RulesetStore rulesets, string query, RulesetInfo ruleset, SearchCategory searchCategory = SearchCategory.Any, SortCriteria sortCriteria = SortCriteria.Ranked, SortDirection sortDirection = SortDirection.Descending) + { + this.api = api; + this.rulesets = rulesets; + this.query = query; + this.ruleset = ruleset; + this.searchCategory = searchCategory; + this.sortCriteria = sortCriteria; + this.sortDirection = sortDirection; + } + + public void FetchNextPage() + { + if (isFetching) + return; + + if (lastResponse != null) + IsPastFirstPage = true; + + getSetsRequest = new SearchBeatmapSetsRequest( + query, + ruleset, + lastResponse?.Cursor, + searchCategory, + sortCriteria, + sortDirection); + + getSetsRequest.Success += response => + { + var sets = response.BeatmapSets.Select(responseJson => responseJson.ToBeatmapSet(rulesets)).ToList(); + + if (sets.Count == 0) + isLastPageFetched = true; + + lastResponse = response; + getSetsRequest = null; + + PageFetched?.Invoke(sets); + }; + + api.Queue(getSetsRequest); + } + + public void Reset() + { + isLastPageFetched = false; + IsPastFirstPage = false; + + lastResponse = null; + + getSetsRequest?.Cancel(); + getSetsRequest = null; + } + + public delegate void PageFetchHandler(List sets); + } +} diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index f680f7c67b..c495c8d21b 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -30,13 +30,21 @@ namespace osu.Game.Overlays private Drawable currentContent; private LoadingLayer loadingLayer; private Container panelTarget; + private FillFlowContainer foundContent; + private NotFoundDrawable notFoundContent; + + private OverlayScrollContainer resultScrollContainer; + /// Scroll distance threshold from results tail, higher means sooner + private const int pagination_scroll_distance = 500; + /// This is paging event flag + private bool shouldAddNextPage => resultScrollContainer.ScrollableExtent > 0 && resultScrollContainer.IsScrolledToEnd(pagination_scroll_distance); public BeatmapListingOverlay() : base(OverlayColourScheme.Blue) { } - private BeatmapListingFilterControl filterControl; + private BeatmapListingFilterControl filterControl;//actual search settings [BackgroundDependencyLoader] private void load() @@ -48,7 +56,7 @@ namespace osu.Game.Overlays RelativeSizeAxes = Axes.Both, Colour = ColourProvider.Background6 }, - new OverlayScrollContainer + resultScrollContainer = new OverlayScrollContainer { RelativeSizeAxes = Axes.Both, ScrollbarVisible = false, @@ -80,9 +88,14 @@ namespace osu.Game.Overlays { AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, - Padding = new MarginPadding { Horizontal = 20 } - }, - loadingLayer = new LoadingLayer(panelTarget) + Padding = new MarginPadding { Horizontal = 20 }, + Children = new Drawable[] + { + foundContent = new FillFlowContainer(), + notFoundContent = new NotFoundDrawable(), + loadingLayer = new LoadingLayer(panelTarget) + } + } } }, } @@ -112,27 +125,52 @@ namespace osu.Game.Overlays private void onSearchFinished(List beatmaps) { + //No matches case if (!beatmaps.Any()) { - LoadComponentAsync(new NotFoundDrawable(), addContentToPlaceholder, (cancellationToken = new CancellationTokenSource()).Token); + LoadComponentAsync(notFoundContent, addContentToPlaceholder, (cancellationToken = new CancellationTokenSource()).Token); return; } - var newPanels = new FillFlowContainer + //New query case + if (!shouldAddNextPage) { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Spacing = new Vector2(10), - Alpha = 0, - Margin = new MarginPadding { Vertical = 15 }, - ChildrenEnumerable = beatmaps.Select(b => new GridBeatmapPanel(b) + //Spawn new child + var newPanels = new FillFlowContainer { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - }) - }; + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Spacing = new Vector2(10), + Alpha = 0, + Margin = new MarginPadding { Vertical = 15 }, + ChildrenEnumerable = beatmaps.Select(b => new GridBeatmapPanel(b) + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + }) + }; - LoadComponentAsync(newPanels, addContentToPlaceholder, (cancellationToken = new CancellationTokenSource()).Token); + foundContent = newPanels; + LoadComponentAsync(foundContent, addContentToPlaceholder, (cancellationToken = new CancellationTokenSource()).Token); + } + + //Pagination case + else + { + + beatmaps.ForEach(x => + { + LoadComponentAsync(new GridBeatmapPanel(x) + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + }, loaded => + { + foundContent.Add(loaded); + loaded.FadeIn(200, Easing.OutQuint); + }); + }); + } } private void addContentToPlaceholder(Drawable content) @@ -149,13 +187,18 @@ namespace osu.Game.Overlays // If the auto-size computation is delayed until fade out completes, the background remain high for too long making the resulting transition to the smaller height look weird. // At the same time, if the last content's height is bypassed immediately, there is a period where the new content is at Alpha = 0 when the auto-sized height will be 0. // To resolve both of these issues, the bypass is delayed until a point when the content transitions (fade-in and fade-out) overlap and it looks good to do so. - lastContent.Delay(25).Schedule(() => lastContent.BypassAutoSizeAxes = Axes.Y); + lastContent.Delay(25).Schedule(() => lastContent.BypassAutoSizeAxes = Axes.Y) + .Then().Schedule(() => panelTarget.Remove(lastContent)); } - panelTarget.Add(currentContent = content); - currentContent.FadeIn(200, Easing.OutQuint); + if (!content.IsAlive) + panelTarget.Add(content); + content.FadeIn(200, Easing.OutQuint); + + currentContent = content; } + protected override void Dispose(bool isDisposing) { cancellationToken?.Cancel(); @@ -203,5 +246,14 @@ namespace osu.Game.Overlays }); } } + + protected override void Update() + { + base.Update(); + + if (shouldAddNextPage) + filterControl.AddPageToResult(); + + } } } From e5821ff2b28df51ed81585d6f2825b688fce5d2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 11 May 2020 22:53:05 +0200 Subject: [PATCH 0664/1117] Integrate GameplayBeatmap changes --- .../Skinning/TestSceneDrawableTaikoMascot.cs | 23 ++++++------- .../UI/DrawableTaikoMascot.cs | 32 +++++++++++++------ osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 9 +----- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs index 492f628482..bd3b360577 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs @@ -7,7 +7,6 @@ using System.Linq; using Humanizer; using NUnit.Framework; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Testing; @@ -76,23 +75,14 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning { AddStep("set beatmap", () => setBeatmap()); - // the bindables need to be independent for each content cell to prevent interference, - // as if some of the skins don't implement the animation they'll immediately revert to the previous state from the clear state. - var states = new List>(); + AddStep("create mascot", () => SetContents(() => new DrawableTaikoMascot { RelativeSizeAxes = Axes.Both })); - AddStep("create mascot", () => SetContents(() => - { - var state = new Bindable(TaikoMascotAnimationState.Clear); - states.Add(state); - return new DrawableTaikoMascot { State = { BindTarget = state }, RelativeSizeAxes = Axes.Both }; - })); - - AddStep("set clear state", () => states.ForEach(state => state.Value = TaikoMascotAnimationState.Clear)); - AddStep("miss", () => mascots.ForEach(mascot => mascot.OnNewResult(new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Miss }))); + AddStep("set clear state", () => mascots.ForEach(mascot => mascot.State.Value = TaikoMascotAnimationState.Clear)); + AddStep("miss", () => mascots.ForEach(mascot => mascot.LastResult.Value = new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Miss })); AddAssert("skins with animations remain in clear state", () => someMascotsIn(TaikoMascotAnimationState.Clear)); AddUntilStep("state reverts to fail", () => allMascotsIn(TaikoMascotAnimationState.Fail)); - AddStep("set clear state again", () => states.ForEach(state => state.Value = TaikoMascotAnimationState.Clear)); + AddStep("set clear state again", () => mascots.ForEach(mascot => mascot.State.Value = TaikoMascotAnimationState.Clear)); AddAssert("skins with animations change to clear", () => someMascotsIn(TaikoMascotAnimationState.Clear)); } @@ -220,6 +210,11 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning playfield.OnNewResult(hit, judgementResult); } + + foreach (var mascot in mascots) + { + mascot.LastResult.Value = judgementResult; + } } private bool allMascotsIn(TaikoMascotAnimationState state) => mascots.All(d => d.State.Value == state); diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs index 9328b607e6..105baa84cc 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs @@ -12,14 +12,15 @@ using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics.Containers; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Taiko.Judgements; +using osu.Game.Screens.Play; namespace osu.Game.Rulesets.Taiko.UI { public class DrawableTaikoMascot : BeatSyncedContainer { - public IBindable State => state; + public readonly Bindable State; + public readonly Bindable LastResult; - private readonly Bindable state; private readonly Dictionary animations; private TaikoMascotAnimation currentAnimation; @@ -30,12 +31,14 @@ namespace osu.Game.Rulesets.Taiko.UI { Origin = Anchor = Anchor.BottomLeft; - state = new Bindable(startingState); + State = new Bindable(startingState); + LastResult = new Bindable(); + animations = new Dictionary(); } - [BackgroundDependencyLoader] - private void load(TextureStore textures) + [BackgroundDependencyLoader(true)] + private void load(TextureStore textures, GameplayBeatmap gameplayBeatmap) { InternalChildren = new[] { @@ -44,6 +47,9 @@ namespace osu.Game.Rulesets.Taiko.UI animations[TaikoMascotAnimationState.Kiai] = new TaikoMascotAnimation(TaikoMascotAnimationState.Kiai), animations[TaikoMascotAnimationState.Fail] = new TaikoMascotAnimation(TaikoMascotAnimationState.Fail), }; + + if (gameplayBeatmap != null) + ((IBindable)LastResult).BindTo(gameplayBeatmap.LastJudgementResult); } protected override void LoadComplete() @@ -51,16 +57,22 @@ namespace osu.Game.Rulesets.Taiko.UI base.LoadComplete(); animations.Values.ForEach(animation => animation.Hide()); - state.BindValueChanged(mascotStateChanged, true); + + State.BindValueChanged(mascotStateChanged, true); + LastResult.BindValueChanged(onNewResult); } - public void OnNewResult(JudgementResult result) + private void onNewResult(ValueChangedEvent resultChangedEvent) { + var result = resultChangedEvent.NewValue; + if (result == null) + return; + // TODO: missing support for clear/fail state transition at end of beatmap gameplay if (triggerComboClear(result) || triggerSwellClear(result)) { - state.Value = TaikoMascotAnimationState.Clear; + State.Value = TaikoMascotAnimationState.Clear; // always consider a clear equivalent to a hit to avoid clear -> miss transitions lastObjectHit = true; } @@ -79,7 +91,7 @@ namespace osu.Game.Rulesets.Taiko.UI protected override void Update() { base.Update(); - state.Value = getNextState(); + State.Value = getNextState(); } private TaikoMascotAnimationState getNextState() @@ -87,7 +99,7 @@ namespace osu.Game.Rulesets.Taiko.UI // don't change state if current animation is playing // (used for clear state - others are manually animated on new beats) if (currentAnimation != null && !currentAnimation.Completed) - return state.Value; + return State.Value; if (!lastObjectHit) return TaikoMascotAnimationState.Fail; diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 0fe0d6165b..21676510ad 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -40,8 +40,6 @@ namespace osu.Game.Rulesets.Taiko.UI private Container hitTargetOffsetContent; - private SkinnableDrawable mascotDrawable; - public TaikoPlayfield(ControlPointInfo controlPoints) { this.controlPoints = controlPoints; @@ -127,7 +125,7 @@ namespace osu.Game.Rulesets.Taiko.UI }, } }, - mascotDrawable = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.TaikoDon), _ => Empty()) + new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.TaikoDon), _ => Empty()) { Origin = Anchor.BottomLeft, Anchor = Anchor.TopLeft, @@ -212,11 +210,6 @@ namespace osu.Game.Rulesets.Taiko.UI addExplosion(judgedObject, type); break; } - - if (mascotDrawable.Drawable is DrawableTaikoMascot mascot) - { - mascot.OnNewResult(result); - } } private void addDrumRollHit(DrawableDrumRollTick drawableTick) => From bf719f98d5c664a0282b4bd186070dfa0fed0ccc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 May 2020 11:08:30 +0900 Subject: [PATCH 0665/1117] Fix beatmap skins providing fallback version lookup, preceding user skins --- osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs | 6 +++--- osu.Game/Skinning/LegacyBeatmapSkin.cs | 6 ++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs b/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs index 685decf097..8deed75a56 100644 --- a/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs +++ b/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs @@ -152,11 +152,12 @@ namespace osu.Game.Tests.Skins } [Test] - public void TestSetBeatmapVersionNoFallback() + public void TestSetBeatmapVersionFallsBackToUserSkin() { + // completely ignoring beatmap versions for simplicity. AddStep("Set user skin version 2.3", () => userSource.Configuration.LegacyVersion = 2.3m); AddStep("Set beatmap skin version null", () => beatmapSource.Configuration.LegacyVersion = 1.7m); - AddAssert("Check legacy version lookup", () => requester.GetConfig(LegacySkinConfiguration.LegacySetting.Version)?.Value == 1.7m); + AddAssert("Check legacy version lookup", () => requester.GetConfig(LegacySkinConfiguration.LegacySetting.Version)?.Value == 2.3m); } [Test] @@ -172,7 +173,6 @@ namespace osu.Game.Tests.Skins public void TestIniWithNoVersionFallsBackTo1() { AddStep("Parse skin with no version", () => userSource.Configuration = new LegacySkinDecoder().Decode(new LineBufferedReader(new MemoryStream()))); - AddStep("Set beatmap skin version null", () => beatmapSource.Configuration.LegacyVersion = null); AddAssert("Check legacy version lookup", () => requester.GetConfig(LegacySkinConfiguration.LegacySetting.Version)?.Value == 1.0m); } diff --git a/osu.Game/Skinning/LegacyBeatmapSkin.cs b/osu.Game/Skinning/LegacyBeatmapSkin.cs index 21533e58cd..87bca856a3 100644 --- a/osu.Game/Skinning/LegacyBeatmapSkin.cs +++ b/osu.Game/Skinning/LegacyBeatmapSkin.cs @@ -27,9 +27,11 @@ namespace osu.Game.Skinning switch (lookup) { case LegacySkinConfiguration.LegacySetting s when s == LegacySkinConfiguration.LegacySetting.Version: - if (Configuration.LegacyVersion is decimal version) - return SkinUtils.As(new Bindable(version)); + // For lookup simplicity, ignore beatmap-level versioning completely. + // If it is decided that we need this due to beatmaps somehow using it, the default (1.0 specified in LegacySkinDecoder.CreateTemplateObject) + // 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. return null; } From a5c1b461f6985d4c369a8f57336de16f4e07c662 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 May 2020 11:14:51 +0900 Subject: [PATCH 0666/1117] Fix null reference in difficulty recommender --- osu.Game/Screens/Select/DifficultyRecommender.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/DifficultyRecommender.cs b/osu.Game/Screens/Select/DifficultyRecommender.cs index 20cdca858a..0dd3341a93 100644 --- a/osu.Game/Screens/Select/DifficultyRecommender.cs +++ b/osu.Game/Screens/Select/DifficultyRecommender.cs @@ -86,7 +86,7 @@ namespace osu.Game.Screens.Select { base.Dispose(isDisposing); - api.Unregister(this); + api?.Unregister(this); } } } From 3b1680583e2ff39976485b1991355daa7c9ec13e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 May 2020 11:31:08 +0900 Subject: [PATCH 0667/1117] Fix taiko scroller not following gameplay time --- osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs b/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs index 1ecdb839fb..4cf1af3b8f 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs @@ -17,6 +17,9 @@ namespace osu.Game.Rulesets.Taiko.Skinning { public class LegacyTaikoScroller : CompositeDrawable { + [Resolved(canBeNull: true)] + private GameplayClock gameplayClock { get; set; } + public LegacyTaikoScroller() { RelativeSizeAxes = Axes.Both; @@ -63,7 +66,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning foreach (var sprite in InternalChildren) { // add the x coordinates and perform re-layout on all sprites as spacing may change with gameplay scale. - sprite.X = additiveX ??= sprite.X - (float)Time.Elapsed * 0.1f; + sprite.X = additiveX ??= sprite.X - (float)(gameplayClock ?? Clock).ElapsedFrameTime * 0.1f; additiveX += sprite.DrawWidth - 1; From e9804bf11be0c535540bde81c09909ba2ecdb132 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 May 2020 11:55:12 +0900 Subject: [PATCH 0668/1117] Update resources --- 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 69f897128c..650ebde54d 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index c6dba8da13..ee6206e166 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -25,7 +25,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index f78fd2e4ff..cbf8600c62 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -71,7 +71,7 @@ - + From e4a23b3e7d7eb065578f48c2d53e7981e37c6a59 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 May 2020 12:39:04 +0900 Subject: [PATCH 0669/1117] Fix ignored excluding more than top level --- osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs | 10 ++++++++++ osu.Game/IO/OsuStorage.cs | 8 ++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs index ed83ff358c..ef2b20de64 100644 --- a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs +++ b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs @@ -133,6 +133,9 @@ namespace osu.Game.Tests.NonVisual // ensure we "use" cache host.Storage.GetStorageForDirectory("cache"); + // for testing nested files are not ignored (only top level) + host.Storage.GetStorageForDirectory("test-nested").GetStorageForDirectory("cache"); + string defaultStorageLocation = Path.Combine(Environment.CurrentDirectory, "headless", nameof(TestMigration)); Assert.That(storage.GetFullPath("."), Is.EqualTo(defaultStorageLocation)); @@ -141,6 +144,13 @@ namespace osu.Game.Tests.NonVisual Assert.That(storage.GetFullPath("."), Is.EqualTo(customPath)); + // ensure cache was not moved + Assert.That(host.Storage.ExistsDirectory("cache")); + + // ensure nested cache was moved + Assert.That(!host.Storage.ExistsDirectory(Path.Combine("test-nested", "cache"))); + Assert.That(storage.ExistsDirectory(Path.Combine("test-nested", "cache"))); + foreach (var file in OsuStorage.IGNORE_FILES) { Assert.That(host.Storage.Exists(file), Is.True); diff --git a/osu.Game/IO/OsuStorage.cs b/osu.Game/IO/OsuStorage.cs index 7c0b90e208..6dc25e871c 100644 --- a/osu.Game/IO/OsuStorage.cs +++ b/osu.Game/IO/OsuStorage.cs @@ -71,7 +71,7 @@ namespace osu.Game.IO { foreach (System.IO.FileInfo fi in target.GetFiles()) { - if (IGNORE_FILES.Contains(fi.Name)) + if (topLevelExcludes && IGNORE_FILES.Contains(fi.Name)) continue; fi.Delete(); @@ -79,7 +79,7 @@ namespace osu.Game.IO foreach (DirectoryInfo dir in target.GetDirectories()) { - if (IGNORE_DIRECTORIES.Contains(dir.Name)) + if (topLevelExcludes && IGNORE_DIRECTORIES.Contains(dir.Name)) continue; dir.Delete(true); @@ -93,7 +93,7 @@ namespace osu.Game.IO foreach (System.IO.FileInfo fi in source.GetFiles()) { - if (IGNORE_FILES.Contains(fi.Name)) + if (topLevelExcludes && IGNORE_FILES.Contains(fi.Name)) continue; int tries = 5; @@ -114,7 +114,7 @@ namespace osu.Game.IO foreach (DirectoryInfo dir in source.GetDirectories()) { - if (IGNORE_DIRECTORIES.Contains(dir.Name)) + if (topLevelExcludes && IGNORE_DIRECTORIES.Contains(dir.Name)) continue; copyRecursive(dir, destination.CreateSubdirectory(dir.Name), false); From 75a40578e85690ae62bf089ae77ad06519b4ef08 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 May 2020 12:39:52 +0900 Subject: [PATCH 0670/1117] Revert ContextFactory to private --- osu.Game/OsuGameBase.cs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 6282f5cb8b..11a3834c71 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -121,7 +121,7 @@ namespace osu.Game protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); - protected DatabaseContextFactory ContextFactory; + private DatabaseContextFactory contextFactory; protected override UserInputManager CreateUserInputManager() => new OsuUserInputManager(); @@ -130,7 +130,7 @@ namespace osu.Game { Resources.AddStore(new DllResourceStore(OsuResources.ResourceAssembly)); - dependencies.Cache(ContextFactory = new DatabaseContextFactory(Storage)); + dependencies.Cache(contextFactory = new DatabaseContextFactory(Storage)); dependencies.CacheAs(Storage); @@ -161,7 +161,7 @@ namespace osu.Game runMigrations(); - dependencies.Cache(SkinManager = new SkinManager(Storage, ContextFactory, Host, Audio, new NamespacedResourceStore(Resources, "Skins/Legacy"))); + dependencies.Cache(SkinManager = new SkinManager(Storage, contextFactory, Host, Audio, new NamespacedResourceStore(Resources, "Skins/Legacy"))); dependencies.CacheAs(SkinManager); if (API == null) API = new APIAccess(LocalConfig); @@ -170,12 +170,12 @@ namespace osu.Game var defaultBeatmap = new DummyWorkingBeatmap(Audio, Textures); - dependencies.Cache(RulesetStore = new RulesetStore(ContextFactory, Storage)); - dependencies.Cache(FileStore = new FileStore(ContextFactory, Storage)); + dependencies.Cache(RulesetStore = new RulesetStore(contextFactory, Storage)); + dependencies.Cache(FileStore = new FileStore(contextFactory, Storage)); // ordering is important here to ensure foreign keys rules are not broken in ModelStore.Cleanup() - dependencies.Cache(ScoreManager = new ScoreManager(RulesetStore, () => BeatmapManager, Storage, API, ContextFactory, Host)); - dependencies.Cache(BeatmapManager = new BeatmapManager(Storage, ContextFactory, RulesetStore, API, Audio, Host, defaultBeatmap)); + dependencies.Cache(ScoreManager = new ScoreManager(RulesetStore, () => BeatmapManager, Storage, API, contextFactory, Host)); + dependencies.Cache(BeatmapManager = new BeatmapManager(Storage, contextFactory, RulesetStore, API, Audio, Host, defaultBeatmap)); // this should likely be moved to ArchiveModelManager when another case appers where it is necessary // to have inter-dependent model managers. this could be obtained with an IHasForeign interface to @@ -189,8 +189,8 @@ namespace osu.Game BeatmapManager.ItemRemoved += i => ScoreManager.Delete(getBeatmapScores(i), true); BeatmapManager.ItemAdded += i => ScoreManager.Undelete(getBeatmapScores(i), true); - dependencies.Cache(KeyBindingStore = new KeyBindingStore(ContextFactory, RulesetStore)); - dependencies.Cache(SettingsStore = new SettingsStore(ContextFactory)); + dependencies.Cache(KeyBindingStore = new KeyBindingStore(contextFactory, RulesetStore)); + dependencies.Cache(SettingsStore = new SettingsStore(contextFactory)); dependencies.Cache(RulesetConfigCache = new RulesetConfigCache(SettingsStore)); dependencies.Cache(new SessionStatics()); dependencies.Cache(new OsuColour()); @@ -279,7 +279,7 @@ namespace osu.Game { try { - using (var db = ContextFactory.GetForWrite(false)) + using (var db = contextFactory.GetForWrite(false)) db.Context.Migrate(); } catch (Exception e) @@ -288,12 +288,12 @@ namespace osu.Game // if we failed, let's delete the database and start fresh. // todo: we probably want a better (non-destructive) migrations/recovery process at a later point than this. - ContextFactory.ResetDatabase(); + contextFactory.ResetDatabase(); Logger.Log("Database purged successfully.", LoggingTarget.Database); // only run once more, then hard bail. - using (var db = ContextFactory.GetForWrite(false)) + using (var db = contextFactory.GetForWrite(false)) db.Context.Migrate(); } } @@ -329,7 +329,7 @@ namespace osu.Game base.Dispose(isDisposing); RulesetStore?.Dispose(); - ContextFactory.FlushConnections(); + contextFactory.FlushConnections(); } private class OsuUserInputManager : UserInputManager @@ -360,7 +360,7 @@ namespace osu.Game public void Migrate(string path) { - ContextFactory.FlushConnections(); + contextFactory.FlushConnections(); (Storage as OsuStorage)?.Migrate(path); } } From a86c7f478c8b251ceec760ce860fa21658505dee Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Tue, 12 May 2020 05:49:35 +0200 Subject: [PATCH 0671/1117] Initial commit --- osu.Game/OsuGame.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 3caffb6db5..6e202b6315 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -91,7 +91,7 @@ namespace osu.Game protected BackButton BackButton; - protected SettingsPanel Settings; + protected SettingsOverlay Settings; private VolumeOverlay volume; private OsuLogo osuLogo; @@ -767,11 +767,17 @@ namespace osu.Game private Task asyncLoadStream; + /// + /// Schedules loading the provided in a single file. + /// + /// The component to load. + /// The method to invoke for adding the component. + /// Whether to cache the component as type into the game dependencies before any scheduling. private T loadComponentSingleFile(T d, Action add, bool cache = false) where T : Drawable { if (cache) - dependencies.Cache(d); + dependencies.CacheAs(d); if (d is OverlayContainer overlay) overlays.Add(overlay); From 949e17cc0e8f4666d87b9be541b5c31b3e5686a4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 May 2020 15:23:47 +0900 Subject: [PATCH 0672/1117] Rework scroller to support backwards playback --- .../Skinning/TestSceneTaikoScroller.cs | 20 ++++++- .../Skinning/LegacyTaikoScroller.cs | 52 +++++++++++-------- .../UI/DrawableTaikoRuleset.cs | 2 +- 3 files changed, 50 insertions(+), 24 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.cs index e26f410b71..9a2ada7f72 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.cs @@ -3,6 +3,7 @@ using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Testing; +using osu.Framework.Timing; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Skinning; @@ -12,11 +13,28 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning { public class TestSceneTaikoScroller : TaikoSkinnableTestScene { + private readonly ManualClock clock = new ManualClock(); + + private bool reversed; + public TestSceneTaikoScroller() { - AddStep("Load scroller", () => SetContents(() => new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.TaikoScroller), _ => Empty()))); + AddStep("Load scroller", () => SetContents(() => + new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.TaikoScroller), _ => Empty()) + { + Clock = new FramedClock(clock) + })); AddToggleStep("Toggle passing", passing => this.ChildrenOfType().ForEach(s => s.LastResult.Value = new JudgementResult(null, new Judgement()) { Type = passing ? HitResult.Perfect : HitResult.Miss })); + + AddToggleStep("toggle playback direction", reversed => this.reversed = reversed); + } + + protected override void Update() + { + base.Update(); + + clock.CurrentTime += (reversed ? -1 : 1) * Clock.ElapsedFrameTime; } } } diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs b/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs index 4cf1af3b8f..b3a325ea68 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs @@ -17,9 +17,6 @@ namespace osu.Game.Rulesets.Taiko.Skinning { public class LegacyTaikoScroller : CompositeDrawable { - [Resolved(canBeNull: true)] - private GameplayClock gameplayClock { get; set; } - public LegacyTaikoScroller() { RelativeSizeAxes = Axes.Both; @@ -59,31 +56,42 @@ namespace osu.Game.Rulesets.Taiko.Skinning { base.Update(); - while (true) + bool wideEnough() => + InternalChildren.Any() + && InternalChildren.First().ScreenSpaceDrawQuad.Width * InternalChildren.Count >= ScreenSpaceDrawQuad.Width * 2; + + // store X before checking wide enough so if we perform layout there is no positional discrepancy. + float currentX = (InternalChildren?.FirstOrDefault()?.X ?? 0) - (float)Clock.ElapsedFrameTime * 0.1f; + + // ensure we have enough sprites + if (!wideEnough()) { - float? additiveX = null; + ClearInternal(); - foreach (var sprite in InternalChildren) - { - // add the x coordinates and perform re-layout on all sprites as spacing may change with gameplay scale. - sprite.X = additiveX ??= sprite.X - (float)(gameplayClock ?? Clock).ElapsedFrameTime * 0.1f; + while (!wideEnough()) + AddInternal(new ScrollerSprite { Passing = passing }); + } - additiveX += sprite.DrawWidth - 1; + var first = InternalChildren.First(); + var last = InternalChildren.Last(); - if (sprite.X + sprite.DrawWidth < 0) - sprite.Expire(); - } + foreach (var sprite in InternalChildren) + { + // add the x coordinates and perform re-layout on all sprites as spacing may change with gameplay scale. + sprite.X = currentX; + currentX += sprite.DrawWidth - 1; + } - var last = InternalChildren.LastOrDefault(); + if (first.ScreenSpaceDrawQuad.TopLeft.X >= ScreenSpaceDrawQuad.TopLeft.X) + { + foreach (var internalChild in InternalChildren) + internalChild.X -= first.DrawWidth; + } - // only break from this loop once we have saturated horizontal space completely. - if (last != null && last.ScreenSpaceDrawQuad.TopRight.X >= ScreenSpaceDrawQuad.TopRight.X) - break; - - AddInternal(new ScrollerSprite - { - Passing = passing - }); + if (last.ScreenSpaceDrawQuad.TopRight.X <= ScreenSpaceDrawQuad.TopRight.X) + { + foreach (var internalChild in InternalChildren) + internalChild.X += first.DrawWidth; } } diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs index c0a6c4582c..21d595a97a 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs @@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Taiko.UI { new BarLineGenerator(Beatmap).BarLines.ForEach(bar => Playfield.Add(bar.Major ? new DrawableBarLineMajor(bar) : new DrawableBarLine(bar))); - AddInternal(scroller = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.TaikoScroller), _ => Empty()) + FrameStableComponents.Add(scroller = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.TaikoScroller), _ => Empty()) { RelativeSizeAxes = Axes.X, Depth = float.MaxValue From c7d8793c1d96bd817d36ad1ae918ab4c6048fa33 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 May 2020 15:44:14 +0900 Subject: [PATCH 0673/1117] Remove unnecessary overlap --- osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs b/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs index b3a325ea68..28114ac74f 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs @@ -79,7 +79,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning { // add the x coordinates and perform re-layout on all sprites as spacing may change with gameplay scale. sprite.X = currentX; - currentX += sprite.DrawWidth - 1; + currentX += sprite.DrawWidth; } if (first.ScreenSpaceDrawQuad.TopLeft.X >= ScreenSpaceDrawQuad.TopLeft.X) From c04f2b0840ded76704dc37210273285d2bf8200a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 May 2020 15:51:59 +0900 Subject: [PATCH 0674/1117] Reposition taiko playfield to be closer to the top of the screen --- .../UI/TaikoPlayfieldAdjustmentContainer.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfieldAdjustmentContainer.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfieldAdjustmentContainer.cs index 980f5ea340..1041456020 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfieldAdjustmentContainer.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfieldAdjustmentContainer.cs @@ -13,18 +13,16 @@ namespace osu.Game.Rulesets.Taiko.UI private const float default_relative_height = TaikoPlayfield.DEFAULT_HEIGHT / 768; private const float default_aspect = 16f / 9f; - public TaikoPlayfieldAdjustmentContainer() - { - Anchor = Anchor.CentreLeft; - Origin = Anchor.CentreLeft; - } - protected override void Update() { base.Update(); float aspectAdjust = Math.Clamp(Parent.ChildSize.X / Parent.ChildSize.Y, 0.4f, 4) / default_aspect; Size = new Vector2(1, default_relative_height * aspectAdjust); + + // Position the taiko playfield exactly one playfield from the top of the screen. + RelativePositionAxes = Axes.Y; + Y = Size.Y; } } } From e28e89213f07767c12b2eed7d0a5b3063a5ef82b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 May 2020 16:58:47 +0900 Subject: [PATCH 0675/1117] Fix incorrect spawning when scale adjustments are applied to child sprites --- .../Skinning/TestSceneTaikoScroller.cs | 3 ++- osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs | 9 ++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.cs index 9a2ada7f72..39e6bc2d6d 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.cs @@ -22,7 +22,8 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning AddStep("Load scroller", () => SetContents(() => new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.TaikoScroller), _ => Empty()) { - Clock = new FramedClock(clock) + Clock = new FramedClock(clock), + Height = 0.4f, })); AddToggleStep("Toggle passing", passing => this.ChildrenOfType().ForEach(s => s.LastResult.Value = new JudgementResult(null, new Judgement()) { Type = passing ? HitResult.Perfect : HitResult.Miss })); diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs b/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs index 28114ac74f..3ec6be8a6c 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs @@ -64,13 +64,8 @@ namespace osu.Game.Rulesets.Taiko.Skinning float currentX = (InternalChildren?.FirstOrDefault()?.X ?? 0) - (float)Clock.ElapsedFrameTime * 0.1f; // ensure we have enough sprites - if (!wideEnough()) - { - ClearInternal(); - - while (!wideEnough()) - AddInternal(new ScrollerSprite { Passing = passing }); - } + while (!wideEnough()) + AddInternal(new ScrollerSprite { Passing = passing }); var first = InternalChildren.First(); var last = InternalChildren.Last(); From de50b725d59be235c0be5ccee68d20f648c0c045 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 May 2020 20:08:35 +0900 Subject: [PATCH 0676/1117] Fix mod failure checks executing actual game logic --- osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs | 3 ++- osu.Game.Tests/Visual/Gameplay/TestSceneReplay.cs | 3 ++- osu.Game/Rulesets/Mods/IApplicableFailOverride.cs | 5 +++-- osu.Game/Rulesets/Mods/ModAutoplay.cs | 3 ++- osu.Game/Rulesets/Mods/ModBlockFail.cs | 2 +- osu.Game/Rulesets/Mods/ModEasy.cs | 13 +++++-------- osu.Game/Rulesets/Mods/ModSuddenDeath.cs | 3 ++- osu.Game/Screens/Play/Player.cs | 4 ++-- osu.Game/Screens/Play/ReplayPlayer.cs | 2 +- osu.Game/Tests/Visual/ModPerfectTestScene.cs | 2 +- osu.Game/Tests/Visual/ModTestScene.cs | 6 ++++-- 11 files changed, 25 insertions(+), 21 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs b/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs index fe46876050..d75f4c70d7 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs @@ -24,7 +24,8 @@ namespace osu.Game.Rulesets.Osu.Mods public override double ScoreMultiplier => 1; public override Type[] IncompatibleMods => new[] { typeof(OsuModSpunOut), typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModNoFail), typeof(ModAutoplay) }; - public bool AllowFail => false; + public bool PerformFail() => false; + public bool RestartOnFail => false; private OsuInputManager inputManager; diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplay.cs index e82722e7a2..1908988739 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplay.cs @@ -33,7 +33,8 @@ namespace osu.Game.Tests.Visual.Gameplay { public new ScoreProcessor ScoreProcessor => base.ScoreProcessor; public new HUDOverlay HUDOverlay => base.HUDOverlay; - public new bool AllowFail => base.AllowFail; + + public bool AllowFail => base.CheckModsAllowFailure(); protected override bool PauseOnFocusLost => false; diff --git a/osu.Game/Rulesets/Mods/IApplicableFailOverride.cs b/osu.Game/Rulesets/Mods/IApplicableFailOverride.cs index 120bfc9a23..8c99d739cb 100644 --- a/osu.Game/Rulesets/Mods/IApplicableFailOverride.cs +++ b/osu.Game/Rulesets/Mods/IApplicableFailOverride.cs @@ -11,10 +11,11 @@ namespace osu.Game.Rulesets.Mods /// /// Whether we should allow failing at the current point in time. /// - bool AllowFail { get; } + /// Whether the fail should be allowed to proceed. Return false to block. + bool PerformFail(); /// - /// Whether we want to restart on fail. Only used if is true. + /// Whether we want to restart on fail. Only used if returns true. /// bool RestartOnFail { get; } } diff --git a/osu.Game/Rulesets/Mods/ModAutoplay.cs b/osu.Game/Rulesets/Mods/ModAutoplay.cs index e51b8b6457..945dd444be 100644 --- a/osu.Game/Rulesets/Mods/ModAutoplay.cs +++ b/osu.Game/Rulesets/Mods/ModAutoplay.cs @@ -27,7 +27,8 @@ namespace osu.Game.Rulesets.Mods public override string Description => "Watch a perfect automated play through the song."; public override double ScoreMultiplier => 1; - public bool AllowFail => false; + public bool PerformFail() => false; + public bool RestartOnFail => false; public override Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModNoFail) }; diff --git a/osu.Game/Rulesets/Mods/ModBlockFail.cs b/osu.Game/Rulesets/Mods/ModBlockFail.cs index 7d7ecfa416..1fde5abad4 100644 --- a/osu.Game/Rulesets/Mods/ModBlockFail.cs +++ b/osu.Game/Rulesets/Mods/ModBlockFail.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Mods /// /// We never fail, 'yo. /// - public bool AllowFail => false; + public bool PerformFail() => false; public bool RestartOnFail => false; diff --git a/osu.Game/Rulesets/Mods/ModEasy.cs b/osu.Game/Rulesets/Mods/ModEasy.cs index c1c4124b98..7cf9656810 100644 --- a/osu.Game/Rulesets/Mods/ModEasy.cs +++ b/osu.Game/Rulesets/Mods/ModEasy.cs @@ -48,17 +48,14 @@ namespace osu.Game.Rulesets.Mods retries = Retries.Value; } - public bool AllowFail + public bool PerformFail() { - get - { - if (retries == 0) return true; + if (retries == 0) return true; - health.Value = health.MaxValue; - retries--; + health.Value = health.MaxValue; + retries--; - return false; - } + return false; } public bool RestartOnFail => false; diff --git a/osu.Game/Rulesets/Mods/ModSuddenDeath.cs b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs index 8799431f1d..df10262845 100644 --- a/osu.Game/Rulesets/Mods/ModSuddenDeath.cs +++ b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs @@ -20,7 +20,8 @@ namespace osu.Game.Rulesets.Mods public override bool Ranked => true; public override Type[] IncompatibleMods => new[] { typeof(ModNoFail), typeof(ModRelax), typeof(ModAutoplay) }; - public bool AllowFail => true; + public bool PerformFail() => true; + public bool RestartOnFail => true; public void ApplyToHealthProcessor(HealthProcessor healthProcessor) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index a2735c8c55..1ec3a69b24 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -105,7 +105,7 @@ namespace osu.Game.Screens.Play /// Whether failing should be allowed. /// By default, this checks whether all selected mods allow failing. /// - protected virtual bool AllowFail => Mods.Value.OfType().All(m => m.AllowFail); + protected virtual bool CheckModsAllowFailure() => Mods.Value.OfType().All(m => m.PerformFail()); private readonly bool allowPause; private readonly bool showResults; @@ -485,7 +485,7 @@ namespace osu.Game.Screens.Play private bool onFail() { - if (!AllowFail) + if (!CheckModsAllowFailure()) return false; HasFailed = true; diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs index 0d2ddb7b01..f0c76163f1 100644 --- a/osu.Game/Screens/Play/ReplayPlayer.cs +++ b/osu.Game/Screens/Play/ReplayPlayer.cs @@ -12,7 +12,7 @@ namespace osu.Game.Screens.Play private readonly Score score; // Disallow replays from failing. (see https://github.com/ppy/osu/issues/6108) - protected override bool AllowFail => false; + protected override bool CheckModsAllowFailure() => false; public ReplayPlayer(Score score, bool allowPause = true, bool showResults = true) : base(allowPause, showResults) diff --git a/osu.Game/Tests/Visual/ModPerfectTestScene.cs b/osu.Game/Tests/Visual/ModPerfectTestScene.cs index 5948283428..c16352bead 100644 --- a/osu.Game/Tests/Visual/ModPerfectTestScene.cs +++ b/osu.Game/Tests/Visual/ModPerfectTestScene.cs @@ -41,7 +41,7 @@ namespace osu.Game.Tests.Visual { } - protected override bool AllowFail => true; + protected override bool CheckModsAllowFailure() => false; public bool CheckFailed(bool failed) { diff --git a/osu.Game/Tests/Visual/ModTestScene.cs b/osu.Game/Tests/Visual/ModTestScene.cs index 8b41fb5075..b5b3084097 100644 --- a/osu.Game/Tests/Visual/ModTestScene.cs +++ b/osu.Game/Tests/Visual/ModTestScene.cs @@ -64,12 +64,14 @@ namespace osu.Game.Tests.Visual protected class ModTestPlayer : TestPlayer { - protected override bool AllowFail { get; } + private readonly bool allowFail; + + protected override bool CheckModsAllowFailure() => allowFail; public ModTestPlayer(bool allowFail) : base(false, false) { - AllowFail = allowFail; + this.allowFail = allowFail; } } From 44319c1b719c64468de0594e22c48c357c3c62ac Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 May 2020 20:26:34 +0900 Subject: [PATCH 0677/1117] Commit missed change --- osu.Game/Tests/Visual/ModPerfectTestScene.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Tests/Visual/ModPerfectTestScene.cs b/osu.Game/Tests/Visual/ModPerfectTestScene.cs index c16352bead..95a62bbf65 100644 --- a/osu.Game/Tests/Visual/ModPerfectTestScene.cs +++ b/osu.Game/Tests/Visual/ModPerfectTestScene.cs @@ -41,7 +41,7 @@ namespace osu.Game.Tests.Visual { } - protected override bool CheckModsAllowFailure() => false; + protected override bool CheckModsAllowFailure() => true; public bool CheckFailed(bool failed) { From f07d95ac59e77eb1fbfeaae58ece62b26828de46 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Tue, 12 May 2020 21:49:55 +0800 Subject: [PATCH 0678/1117] Use IReadOnlyList for TypedListConverter. --- .../IO/Serialization/Converters/TypedListConverter.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs index ec0036dae2..f98fa05821 100644 --- a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs +++ b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs @@ -9,12 +9,12 @@ using Newtonsoft.Json.Linq; namespace osu.Game.IO.Serialization.Converters { /// - /// A type of that serializes an alongside + /// A type of that serializes an alongside /// a lookup table for the types contained. The lookup table is used in deserialization to /// reconstruct the objects with their original types. /// - /// The type of objects contained in the this attribute is attached to. - public class TypedListConverter : JsonConverter> + /// The type of objects contained in the this attribute is attached to. + public class TypedListConverter : JsonConverter> { private readonly bool requiresTypeVersion; @@ -36,7 +36,7 @@ namespace osu.Game.IO.Serialization.Converters this.requiresTypeVersion = requiresTypeVersion; } - public override IEnumerable ReadJson(JsonReader reader, Type objectType, IEnumerable existingValue, bool hasExistingValue, JsonSerializer serializer) + public override IReadOnlyList ReadJson(JsonReader reader, Type objectType, IReadOnlyList existingValue, bool hasExistingValue, JsonSerializer serializer) { var list = new List(); @@ -57,7 +57,7 @@ namespace osu.Game.IO.Serialization.Converters return list; } - public override void WriteJson(JsonWriter writer, IEnumerable value, JsonSerializer serializer) + public override void WriteJson(JsonWriter writer, IReadOnlyList value, JsonSerializer serializer) { var lookupTable = new List(); var objects = new List(); From 0c60b10757b1d605cb1d15c20c510a3b9a63ddbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=80=8C=E7=A9=BA=E7=99=BD=E3=80=8D?= <「空白」> Date: Wed, 13 May 2020 01:14:11 +0900 Subject: [PATCH 0679/1117] Fix code factor issues > ran "dotnet format --check", shouldn't return whitespace errors anymore --- .../Overlays/BeatmapListing/BeatmapListingFilterControl.cs | 2 +- osu.Game/Overlays/BeatmapListingOverlay.cs | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs index c3e8505ddc..3f06bc20ed 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs @@ -154,7 +154,7 @@ namespace osu.Game.Overlays.BeatmapListing currentBeatmaps = beatmaps; else currentBeatmaps.AddRange(beatmaps); - + SearchFinished?.Invoke(beatmaps); } diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index c495c8d21b..115fe02999 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -44,7 +44,7 @@ namespace osu.Game.Overlays { } - private BeatmapListingFilterControl filterControl;//actual search settings + private BeatmapListingFilterControl filterControl; [BackgroundDependencyLoader] private void load() @@ -157,7 +157,6 @@ namespace osu.Game.Overlays //Pagination case else { - beatmaps.ForEach(x => { LoadComponentAsync(new GridBeatmapPanel(x) @@ -198,7 +197,6 @@ namespace osu.Game.Overlays currentContent = content; } - protected override void Dispose(bool isDisposing) { cancellationToken?.Cancel(); @@ -253,7 +251,6 @@ namespace osu.Game.Overlays if (shouldAddNextPage) filterControl.AddPageToResult(); - } } } From 82190a07b82f439c8e221c2420ca9b61e7d7e944 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=80=8C=E7=A9=BA=E7=99=BD=E3=80=8D?= <「空白」> Date: Wed, 13 May 2020 02:01:38 +0900 Subject: [PATCH 0680/1117] Remove temporary comments > Removes unnecessary xmldoc comments --- osu.Game/Online/API/Requests/ResponseWithCursor.cs | 3 --- .../Overlays/BeatmapListing/BeatmapListingFilterControl.cs | 2 -- osu.Game/Overlays/BeatmapListing/BeatmapListingPager.cs | 4 ---- osu.Game/Overlays/BeatmapListingOverlay.cs | 2 -- 4 files changed, 11 deletions(-) diff --git a/osu.Game/Online/API/Requests/ResponseWithCursor.cs b/osu.Game/Online/API/Requests/ResponseWithCursor.cs index 51e88ca52b..b0fe9eea28 100644 --- a/osu.Game/Online/API/Requests/ResponseWithCursor.cs +++ b/osu.Game/Online/API/Requests/ResponseWithCursor.cs @@ -16,9 +16,6 @@ namespace osu.Game.Online.API.Requests public abstract class ResponseWithCursor : ResponseWithCursor where T : class { - /// - /// Cursor deserialized into T class type (cannot implicitly convert type to object using raw Cursor) - /// [JsonProperty("cursor")] public T Cursor; } diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs index 3f06bc20ed..ac5ad96f7c 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs @@ -22,7 +22,6 @@ namespace osu.Game.Overlays.BeatmapListing { public Action> SearchFinished; public Action SearchStarted; - /// List of currently displayed beatmap entries private List currentBeatmaps; [Resolved] @@ -169,7 +168,6 @@ namespace osu.Game.Overlays.BeatmapListing public void TakeFocus() => searchControl.TakeFocus(); - /// Request next 50 matches if available public void AddPageToResult() { if (beatmapListingPager == null || !beatmapListingPager.CanFetchNextPage) diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingPager.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingPager.cs index 66faf8df7a..4c8902d314 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingPager.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingPager.cs @@ -24,13 +24,9 @@ namespace osu.Game.Overlays.BeatmapListing private SearchBeatmapSetsRequest getSetsRequest; private SearchBeatmapSetsResponse lastResponse; - /// Reports end of results private bool isLastPageFetched = false; - /// Job in process lock flag private bool isFetching => getSetsRequest != null; - /// Whether beatmaps should be appended or replaced public bool IsPastFirstPage { get; private set; } = false; - /// call FetchNextPage() safe-check public bool CanFetchNextPage => !isLastPageFetched && !isFetching; public BeatmapListingPager(IAPIProvider api, RulesetStore rulesets, string query, RulesetInfo ruleset, SearchCategory searchCategory = SearchCategory.Any, SortCriteria sortCriteria = SortCriteria.Ranked, SortDirection sortDirection = SortDirection.Descending) diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index 115fe02999..ba92181ac5 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -34,9 +34,7 @@ namespace osu.Game.Overlays private NotFoundDrawable notFoundContent; private OverlayScrollContainer resultScrollContainer; - /// Scroll distance threshold from results tail, higher means sooner private const int pagination_scroll_distance = 500; - /// This is paging event flag private bool shouldAddNextPage => resultScrollContainer.ScrollableExtent > 0 && resultScrollContainer.IsScrolledToEnd(pagination_scroll_distance); public BeatmapListingOverlay() From e321494f15c182beec3e8c911341968cbfa73417 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=80=8C=E7=A9=BA=E7=99=BD=E3=80=8D?= <「空白」> Date: Wed, 13 May 2020 02:15:24 +0900 Subject: [PATCH 0681/1117] Fix sort-by handling > Add other cursor fields for paging different sortings > Sorted as they show in GUI code-wise for more readability for now --- .../API/Requests/SearchBeatmapSetsRequest.cs | 41 ++++++++++++++++++- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs index fb2cc66dd8..2987d554af 100644 --- a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs +++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs @@ -13,8 +13,29 @@ namespace osu.Game.Online.API.Requests { public class Cursor { + [JsonProperty("title.raw")] + public string Title; + + [JsonProperty("artist.raw")] + public string Artist; + + [JsonProperty("beatmaps.difficultyrating")] + public string Difficulty; + [JsonProperty("approved_date")] - public string ApprovedDate; + public string Ranked; + + [JsonProperty("rating")] + public string Rating; + + [JsonProperty("play_count")] + public string Plays; + + [JsonProperty("favourite_count")] + public string Favourites; + + [JsonProperty("_score")] + public string Relevance; [JsonProperty("_id")] public string Id; @@ -70,8 +91,24 @@ namespace osu.Game.Online.API.Requests if (cursor != null) { + if (cursor.Title != null) + req.AddParameter("cursor[title.raw]", cursor.Title); + if (cursor.Artist != null) + req.AddParameter("cursor[artist.raw]", cursor.Artist); + if (cursor.Difficulty != null) + req.AddParameter("cursor[beatmaps.difficultyrating]", cursor.Difficulty); + if (cursor.Ranked != null) + req.AddParameter("cursor[approved_date]", cursor.Ranked); + if (cursor.Rating != null) + req.AddParameter("cursor[rating]", cursor.Rating); + if (cursor.Plays != null) + req.AddParameter("cursor[play_count]", cursor.Plays); + if (cursor.Favourites != null) + req.AddParameter("cursor[favourite_count]", cursor.Favourites); + if (cursor.Relevance != null) + req.AddParameter("cursor[_score]", cursor.Relevance); + req.AddParameter("cursor[_id]", cursor.Id); - req.AddParameter("cursor[approved_date]", cursor.ApprovedDate); } return req; From 942cc48e99ac41a480c48faa9270534d31225e78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 12 May 2020 20:26:11 +0200 Subject: [PATCH 0682/1117] Improve mascot scaling --- .../UI/TaikoMascotAnimation.cs | 2 +- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 17 +++++++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs b/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs index 0bf6bc7d49..01cf88a87e 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Taiko.UI InternalChild = textureAnimation = createTextureAnimation(state).With(animation => { animation.Origin = animation.Anchor = Anchor.BottomLeft; - animation.Scale = new Vector2(0.6f); + animation.Scale = new Vector2(0.51f); // close enough to stable }); RelativeSizeAxes = Axes.Both; diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 21676510ad..5940ee8b69 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -5,6 +5,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Layout; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; using osu.Game.Rulesets.Objects.Drawables; @@ -15,6 +16,7 @@ using osu.Game.Rulesets.Taiko.Objects.Drawables; using osu.Game.Rulesets.Taiko.Judgements; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Skinning; +using osuTK; namespace osu.Game.Rulesets.Taiko.UI { @@ -32,6 +34,7 @@ namespace osu.Game.Rulesets.Taiko.UI private JudgementContainer judgementContainer; private ScrollingHitObjectContainer drumRollHitContainer; internal Drawable HitTarget; + private SkinnableDrawable mascot; private ProxyContainer topLevelHitContainer; private ProxyContainer barlineContainer; @@ -40,9 +43,14 @@ namespace osu.Game.Rulesets.Taiko.UI private Container hitTargetOffsetContent; + private readonly LayoutValue playfieldScaleLayout = new LayoutValue(Invalidation.DrawSize); + private float playfieldScale => playfieldScaleLayout.IsValid ? playfieldScaleLayout.Value : playfieldScaleLayout.Value = DrawHeight / DEFAULT_HEIGHT; + public TaikoPlayfield(ControlPointInfo controlPoints) { this.controlPoints = controlPoints; + + AddLayout(playfieldScaleLayout); } [BackgroundDependencyLoader] @@ -125,14 +133,13 @@ namespace osu.Game.Rulesets.Taiko.UI }, } }, - new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.TaikoDon), _ => Empty()) + mascot = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.TaikoDon), _ => Empty()) { Origin = Anchor.BottomLeft, Anchor = Anchor.TopLeft, - RelativePositionAxes = Axes.None, + RelativePositionAxes = Axes.Y, RelativeSizeAxes = Axes.None, - X = 15, - Y = 45 + Y = 0.2f }, topLevelHitContainer = new ProxyContainer { @@ -151,6 +158,8 @@ namespace osu.Game.Rulesets.Taiko.UI // This is basically allowing for correct alignment as relative pieces move around them. rightArea.Padding = new MarginPadding { Left = leftArea.DrawWidth }; hitTargetOffsetContent.Padding = new MarginPadding { Left = HitTarget.DrawWidth / 2 }; + + mascot.Scale = new Vector2(playfieldScale); } public override void Add(DrawableHitObject h) From cabf3a89b1e79edcd452900477a684ad4f5b3bd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=80=8C=E7=A9=BA=E7=99=BD=E3=80=8D?= <「空白」> Date: Wed, 13 May 2020 03:44:57 +0900 Subject: [PATCH 0683/1117] More robust cursor parsing solution > Change cursor request to return last response's entire cursor structure --- .../API/Requests/SearchBeatmapSetsRequest.cs | 54 +++---------------- 1 file changed, 8 insertions(+), 46 deletions(-) diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs index 2987d554af..e1df7fe6d3 100644 --- a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs +++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs @@ -2,10 +2,13 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.IO.Network; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Game.Overlays; using osu.Game.Overlays.BeatmapListing; using osu.Game.Rulesets; using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System.Collections.Generic; namespace osu.Game.Online.API.Requests { @@ -13,32 +16,8 @@ namespace osu.Game.Online.API.Requests { public class Cursor { - [JsonProperty("title.raw")] - public string Title; - - [JsonProperty("artist.raw")] - public string Artist; - - [JsonProperty("beatmaps.difficultyrating")] - public string Difficulty; - - [JsonProperty("approved_date")] - public string Ranked; - - [JsonProperty("rating")] - public string Rating; - - [JsonProperty("play_count")] - public string Plays; - - [JsonProperty("favourite_count")] - public string Favourites; - - [JsonProperty("_score")] - public string Relevance; - - [JsonProperty("_id")] - public string Id; + [JsonExtensionData] + public IDictionary Properties; } public SearchCategory SearchCategory { get; set; } @@ -89,27 +68,10 @@ namespace osu.Game.Online.API.Requests req.AddParameter("sort", $"{SortCriteria.ToString().ToLowerInvariant()}_{directionString}"); - if (cursor != null) + cursor?.Properties.ForEach(x => { - if (cursor.Title != null) - req.AddParameter("cursor[title.raw]", cursor.Title); - if (cursor.Artist != null) - req.AddParameter("cursor[artist.raw]", cursor.Artist); - if (cursor.Difficulty != null) - req.AddParameter("cursor[beatmaps.difficultyrating]", cursor.Difficulty); - if (cursor.Ranked != null) - req.AddParameter("cursor[approved_date]", cursor.Ranked); - if (cursor.Rating != null) - req.AddParameter("cursor[rating]", cursor.Rating); - if (cursor.Plays != null) - req.AddParameter("cursor[play_count]", cursor.Plays); - if (cursor.Favourites != null) - req.AddParameter("cursor[favourite_count]", cursor.Favourites); - if (cursor.Relevance != null) - req.AddParameter("cursor[_score]", cursor.Relevance); - - req.AddParameter("cursor[_id]", cursor.Id); - } + req.AddParameter("cursor[" + x.Key + "]", x.Value?.ToString()); + }); return req; } From 5962dedd35947eab90642aef09a6dee8547e3ef5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=80=8C=E7=A9=BA=E7=99=BD=E3=80=8D?= <「空白」> Date: Wed, 13 May 2020 05:04:39 +0900 Subject: [PATCH 0684/1117] Reimplement cursor as part of WebRequest extensions > Added WebRequestExtensions > Moved Cursor request logic from SearchBeatmapSetsRequest --- osu.Game/Extensions/WebRequestExtensions.cs | 25 +++++++++++++++++++ .../API/Requests/SearchBeatmapSetsRequest.cs | 16 ++---------- .../API/Requests/SearchBeatmapSetsResponse.cs | 3 ++- 3 files changed, 29 insertions(+), 15 deletions(-) create mode 100644 osu.Game/Extensions/WebRequestExtensions.cs diff --git a/osu.Game/Extensions/WebRequestExtensions.cs b/osu.Game/Extensions/WebRequestExtensions.cs new file mode 100644 index 0000000000..c8e3c564a5 --- /dev/null +++ b/osu.Game/Extensions/WebRequestExtensions.cs @@ -0,0 +1,25 @@ +using osu.Framework.IO.Network; +using osu.Framework.Extensions.IEnumerableExtensions; +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace osu.Game.Extensions +{ + public class Cursor + { + [JsonExtensionData] + public IDictionary Properties; + } + + public static class WebRequestExtensions + { + public static void AddCursor(this WebRequest webRequest, Cursor cursor) + { + cursor?.Properties.ForEach(x => + { + webRequest.AddParameter("cursor[" + x.Key + "]", x.Value.ToString()); + }); + } + } +} diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs index e1df7fe6d3..a49cb70c37 100644 --- a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs +++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs @@ -2,24 +2,15 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.IO.Network; -using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Game.Extensions; using osu.Game.Overlays; using osu.Game.Overlays.BeatmapListing; using osu.Game.Rulesets; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; namespace osu.Game.Online.API.Requests { public class SearchBeatmapSetsRequest : APIRequest { - public class Cursor - { - [JsonExtensionData] - public IDictionary Properties; - } - public SearchCategory SearchCategory { get; set; } public SortCriteria SortCriteria { get; set; } @@ -68,10 +59,7 @@ namespace osu.Game.Online.API.Requests req.AddParameter("sort", $"{SortCriteria.ToString().ToLowerInvariant()}_{directionString}"); - cursor?.Properties.ForEach(x => - { - req.AddParameter("cursor[" + x.Key + "]", x.Value?.ToString()); - }); + req.AddCursor(cursor); return req; } diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsResponse.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsResponse.cs index 2adf7004e8..a4d2c0e871 100644 --- a/osu.Game/Online/API/Requests/SearchBeatmapSetsResponse.cs +++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsResponse.cs @@ -3,11 +3,12 @@ using System.Collections.Generic; using Newtonsoft.Json; +using osu.Game.Extensions; using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Online.API.Requests { - public class SearchBeatmapSetsResponse : ResponseWithCursor + public class SearchBeatmapSetsResponse : ResponseWithCursor { [JsonProperty("beatmapsets")] public IEnumerable BeatmapSets; From 7874045b1f82e34dac1df8eb8d849f25a878ab97 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Tue, 12 May 2020 21:12:48 +0200 Subject: [PATCH 0685/1117] Allow disabling SkipOverlay through AllowGameplayOverlays. --- osu.Game/Screens/Play/Player.cs | 6 +++++- osu.Game/Screens/Play/SkipOverlay.cs | 7 +++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index a2735c8c55..b24cef8eae 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -83,6 +83,8 @@ namespace osu.Game.Screens.Play private BreakTracker breakTracker; + private SkipOverlay skipOverlay; + protected ScoreProcessor ScoreProcessor { get; private set; } protected HealthProcessor HealthProcessor { get; private set; } @@ -189,6 +191,8 @@ namespace osu.Game.Screens.Play HUDOverlay.ShowHud.Value = false; HUDOverlay.ShowHud.Disabled = true; BreakOverlay.Hide(); + skipOverlay.Disabled = true; + skipOverlay.Hide(); } DrawableRuleset.HasReplayLoaded.BindValueChanged(_ => updatePauseOnFocusLostState(), true); @@ -290,7 +294,7 @@ namespace osu.Game.Screens.Play Anchor = Anchor.Centre, Origin = Anchor.Centre }, - new SkipOverlay(DrawableRuleset.GameplayStartTime) + skipOverlay = new SkipOverlay(DrawableRuleset.GameplayStartTime) { RequestSkip = GameplayClockContainer.Skip }, diff --git a/osu.Game/Screens/Play/SkipOverlay.cs b/osu.Game/Screens/Play/SkipOverlay.cs index ac7e509c2c..757dcd21ed 100644 --- a/osu.Game/Screens/Play/SkipOverlay.cs +++ b/osu.Game/Screens/Play/SkipOverlay.cs @@ -39,6 +39,8 @@ namespace osu.Game.Screens.Play [Resolved] private GameplayClock gameplayClock { get; set; } + public bool Disabled { get; set; } + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; /// @@ -105,7 +107,8 @@ namespace osu.Game.Screens.Play button.Action = () => RequestSkip?.Invoke(); displayTime = gameplayClock.CurrentTime; - Show(); + if (!Disabled) + Show(); } protected override void PopIn() => this.FadeIn(fade_time); @@ -121,7 +124,7 @@ namespace osu.Game.Screens.Play remainingTimeBox.Width = (float)Interpolation.Lerp(remainingTimeBox.Width, progress, Math.Clamp(Time.Elapsed / 40, 0, 1)); button.Enabled.Value = progress > 0; - State.Value = progress > 0 ? Visibility.Visible : Visibility.Hidden; + State.Value = progress > 0 && !Disabled ? Visibility.Visible : Visibility.Hidden; } protected override bool OnMouseMove(MouseMoveEvent e) From bbf4c687a8b155499cf50634ce545888f3ee0770 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 May 2020 11:09:17 +0900 Subject: [PATCH 0686/1117] Improve xmldoc --- osu.Game/OsuGame.cs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 6e202b6315..7ecd7851d7 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -768,18 +768,19 @@ namespace osu.Game private Task asyncLoadStream; /// - /// Schedules loading the provided in a single file. + /// Queues loading the provided component in sequential fashion. + /// This operation is limited to a single thread to avoid saturating all cores. /// - /// The component to load. - /// The method to invoke for adding the component. + /// The component to load. + /// An action to invoke on load completion (generally to add the component to the hierarchy). /// Whether to cache the component as type into the game dependencies before any scheduling. - private T loadComponentSingleFile(T d, Action add, bool cache = false) + private T loadComponentSingleFile(T component, Action loadCompleteAction, bool cache = false) where T : Drawable { if (cache) - dependencies.CacheAs(d); + dependencies.CacheAs(component); - if (d is OverlayContainer overlay) + if (component is OverlayContainer overlay) overlays.Add(overlay); // schedule is here to ensure that all component loads are done after LoadComplete is run (and thus all dependencies are cached). @@ -797,12 +798,12 @@ namespace osu.Game try { - Logger.Log($"Loading {d}...", level: LogLevel.Debug); + Logger.Log($"Loading {component}...", level: LogLevel.Debug); // Since this is running in a separate thread, it is possible for OsuGame to be disposed after LoadComponentAsync has been called // throwing an exception. To avoid this, the call is scheduled on the update thread, which does not run if IsDisposed = true Task task = null; - var del = new ScheduledDelegate(() => task = LoadComponentAsync(d, add)); + var del = new ScheduledDelegate(() => task = LoadComponentAsync(component, loadCompleteAction)); Scheduler.Add(del); // The delegate won't complete if OsuGame has been disposed in the meantime @@ -817,7 +818,7 @@ namespace osu.Game await task; - Logger.Log($"Loaded {d}!", level: LogLevel.Debug); + Logger.Log($"Loaded {component}!", level: LogLevel.Debug); } catch (OperationCanceledException) { @@ -825,7 +826,7 @@ namespace osu.Game }); }); - return d; + return component; } protected override bool OnScroll(ScrollEvent e) From 78f1b230e924b7065d629919f7b2ac0c42dcdc47 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 13 May 2020 14:43:50 +0900 Subject: [PATCH 0687/1117] Disable right-click placement in the mania editor --- .../Edit/Blueprints/HoldNotePlacementBlueprint.cs | 4 ++++ .../Edit/Blueprints/ManiaPlacementBlueprint.cs | 4 ++++ .../Edit/Blueprints/NotePlacementBlueprint.cs | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs index b3dd392202..c63e30e98a 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs @@ -7,6 +7,7 @@ using osu.Framework.Input.Events; using osu.Game.Rulesets.Mania.Edit.Blueprints.Components; using osu.Game.Rulesets.Mania.Objects; using osuTK; +using osuTK.Input; namespace osu.Game.Rulesets.Mania.Edit.Blueprints { @@ -49,6 +50,9 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints protected override void OnMouseUp(MouseUpEvent e) { + if (e.Button != MouseButton.Left) + return; + base.OnMouseUp(e); EndPlacement(true); } diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs index 400abb6380..3fb03d642f 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs @@ -11,6 +11,7 @@ using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.UI.Scrolling; using osuTK; +using osuTK.Input; namespace osu.Game.Rulesets.Mania.Edit.Blueprints { @@ -46,6 +47,9 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints protected override bool OnMouseDown(MouseDownEvent e) { + if (e.Button != MouseButton.Left) + return false; + if (Column == null) return base.OnMouseDown(e); diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/NotePlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/NotePlacementBlueprint.cs index 2b7b383dbe..a4c0791253 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/NotePlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/NotePlacementBlueprint.cs @@ -5,6 +5,7 @@ using osu.Framework.Graphics; using osu.Framework.Input.Events; using osu.Game.Rulesets.Mania.Edit.Blueprints.Components; using osu.Game.Rulesets.Mania.Objects; +using osuTK.Input; namespace osu.Game.Rulesets.Mania.Edit.Blueprints { @@ -30,6 +31,9 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints protected override bool OnMouseDown(MouseDownEvent e) { + if (e.Button != MouseButton.Left) + return false; + base.OnMouseDown(e); // Place the note immediately. From 8500f8fe767c4f7362cf4c6c38685de270cdf77a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 May 2020 18:12:58 +0900 Subject: [PATCH 0688/1117] Add basic implementation --- .../Settings/TestSceneDirectorySelector.cs | 195 ++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100644 osu.Game.Tests/Visual/Settings/TestSceneDirectorySelector.cs diff --git a/osu.Game.Tests/Visual/Settings/TestSceneDirectorySelector.cs b/osu.Game.Tests/Visual/Settings/TestSceneDirectorySelector.cs new file mode 100644 index 0000000000..416d978c69 --- /dev/null +++ b/osu.Game.Tests/Visual/Settings/TestSceneDirectorySelector.cs @@ -0,0 +1,195 @@ +// 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 osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Platform; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osuTK; + +namespace osu.Game.Tests.Visual.Settings +{ + public class TestSceneDirectorySelector : OsuTestScene + { + [BackgroundDependencyLoader] + private void load(GameHost host) + { + Add(new DirectorySelector { RelativeSizeAxes = Axes.Both }); + } + } + + public class DirectorySelector : CompositeDrawable + { + private Storage root; + private FillFlowContainer directoryFlow; + private CurrentDirectoryDisplay current; + + [Resolved] + private GameHost host { get; set; } + + private readonly Bindable currentDirectory = new Bindable(); + + public DirectorySelector(Storage root = null) + { + this.root = root; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Padding = new MarginPadding(10); + + if (root == null) + root = host.GetStorage("/Users/"); + + InternalChildren = new Drawable[] + { + new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + current = new CurrentDirectoryDisplay + { + CurrentDirectory = { BindTarget = currentDirectory }, + RelativeSizeAxes = Axes.X, + Height = 50, + }, + new OsuScrollContainer + { + RelativeSizeAxes = Axes.Both, + Child = directoryFlow = new FillFlowContainer + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Direction = FillDirection.Vertical, + } + } + } + }, + }; + + currentDirectory.BindValueChanged(updateDisplay); + currentDirectory.Value = root.GetFullPath(string.Empty); + } + + private void updateDisplay(ValueChangedEvent directory) + { + root = host.GetStorage(directory.NewValue); + + directoryFlow.Clear(); + + directoryFlow.Add(new ParentDirectoryRow(getParentPath()) { CurrentDirectory = { BindTarget = currentDirectory }, }); + + foreach (var dir in root.GetDirectories(string.Empty)) + directoryFlow.Add(new DirectoryRow(dir, root.GetFullPath(dir)) { CurrentDirectory = { BindTarget = currentDirectory }, }); + } + + private string getParentPath() => Path.GetFullPath(Path.Combine(root.GetFullPath(string.Empty), "..")); + + public class CurrentDirectoryDisplay : CompositeDrawable + { + public readonly Bindable CurrentDirectory = new Bindable(); + + public CurrentDirectoryDisplay() + { + FillFlowContainer flow; + + InternalChildren = new Drawable[] + { + flow = new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.X, + Spacing = new Vector2(10), + Height = 20, + Direction = FillDirection.Horizontal, + }, + }; + + CurrentDirectory.BindValueChanged(dir => + { + flow.Clear(); + + flow.Add(new OsuSpriteText { Text = "Current Directory: " }); + + var pieces = dir.NewValue.Split(Path.DirectorySeparatorChar); + + pieces[0] = "/"; + + for (int i = 0; i < pieces.Length; i++) + { + flow.Add(new DirectoryRow(pieces[i], Path.Combine(pieces.Take(i + 1).ToArray())) + { + CurrentDirectory = { BindTarget = CurrentDirectory }, + RelativeSizeAxes = Axes.Y, + Size = new Vector2(100, 1) + }); + } + }); + } + } + + private class ParentDirectoryRow : DirectoryRow + { + public override IconUsage Icon => FontAwesome.Solid.Folder; + + public ParentDirectoryRow(string fullPath) + : base("..", fullPath) + { + } + } + + private class DirectoryRow : OsuButton + { + private readonly string fullPath; + + public readonly Bindable CurrentDirectory = new Bindable(); + + public DirectoryRow(string display, string fullPath) + { + this.fullPath = fullPath; + + RelativeSizeAxes = Axes.X; + Height = 20; + + BackgroundColour = OsuColour.Gray(0.1f); + + AddRange(new Drawable[] + { + new SpriteIcon + { + Icon = Icon, + Size = new Vector2(20) + }, + new OsuSpriteText + { + X = 25, + Text = display, + Font = OsuFont.Default.With(size: 20) + } + }); + + Action = PerformDirectoryTraversal; + } + + protected virtual void PerformDirectoryTraversal() + { + CurrentDirectory.Value = fullPath; + } + + public virtual IconUsage Icon => FontAwesome.Regular.Folder; + } + } +} From 2c83417c41e29be9a5e474df65a7806dbf747577 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 May 2020 18:08:43 +0900 Subject: [PATCH 0689/1117] Switch to using DirectoryInfo/DriveInfo --- .../Settings/TestSceneDirectorySelector.cs | 122 +++++++++--------- 1 file changed, 64 insertions(+), 58 deletions(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneDirectorySelector.cs b/osu.Game.Tests/Visual/Settings/TestSceneDirectorySelector.cs index 416d978c69..beb5353115 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneDirectorySelector.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneDirectorySelector.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.IO; using System.Linq; using osu.Framework.Allocation; @@ -8,11 +9,11 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; +using osu.Framework.Input.Events; using osu.Framework.Platform; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; using osuTK; namespace osu.Game.Tests.Visual.Settings @@ -28,18 +29,17 @@ namespace osu.Game.Tests.Visual.Settings public class DirectorySelector : CompositeDrawable { - private Storage root; private FillFlowContainer directoryFlow; - private CurrentDirectoryDisplay current; [Resolved] private GameHost host { get; set; } - private readonly Bindable currentDirectory = new Bindable(); + [Cached] + private readonly Bindable currentDirectory = new Bindable(); - public DirectorySelector(Storage root = null) + public DirectorySelector(string initialPath = null) { - this.root = root; + currentDirectory.Value = new DirectoryInfo(initialPath ??= Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)); } protected override void LoadComplete() @@ -48,9 +48,6 @@ namespace osu.Game.Tests.Visual.Settings Padding = new MarginPadding(10); - if (root == null) - root = host.GetStorage("/Users/"); - InternalChildren = new Drawable[] { new FillFlowContainer @@ -59,9 +56,8 @@ namespace osu.Game.Tests.Visual.Settings Direction = FillDirection.Vertical, Children = new Drawable[] { - current = new CurrentDirectoryDisplay + new CurrentDirectoryDisplay { - CurrentDirectory = { BindTarget = currentDirectory }, RelativeSizeAxes = Axes.X, Height = 50, }, @@ -79,29 +75,39 @@ namespace osu.Game.Tests.Visual.Settings }, }; - currentDirectory.BindValueChanged(updateDisplay); - currentDirectory.Value = root.GetFullPath(string.Empty); + currentDirectory.BindValueChanged(updateDisplay, true); } - private void updateDisplay(ValueChangedEvent directory) + private void updateDisplay(ValueChangedEvent directory) { - root = host.GetStorage(directory.NewValue); - directoryFlow.Clear(); - directoryFlow.Add(new ParentDirectoryRow(getParentPath()) { CurrentDirectory = { BindTarget = currentDirectory }, }); + if (directory.NewValue == null) + { + // var drives = DriveInfo.GetDrives(); + // + // foreach (var drive in drives) + // directoryFlow.Add(new DirectoryRow(drive.RootDirectory)); + } + else + { + directoryFlow.Add(new ParentDirectoryRow(currentDirectory.Value.Parent)); - foreach (var dir in root.GetDirectories(string.Empty)) - directoryFlow.Add(new DirectoryRow(dir, root.GetFullPath(dir)) { CurrentDirectory = { BindTarget = currentDirectory }, }); + foreach (var dir in currentDirectory.Value.GetDirectories().OrderBy(d => d.Name)) + { + if ((dir.Attributes & FileAttributes.Hidden) == 0) + directoryFlow.Add(new DirectoryRow(dir)); + } + } } - private string getParentPath() => Path.GetFullPath(Path.Combine(root.GetFullPath(string.Empty), "..")); - public class CurrentDirectoryDisplay : CompositeDrawable { - public readonly Bindable CurrentDirectory = new Bindable(); + [Resolved] + private Bindable currentDirectory { get; set; } - public CurrentDirectoryDisplay() + [BackgroundDependencyLoader] + private void load() { FillFlowContainer flow; @@ -118,26 +124,26 @@ namespace osu.Game.Tests.Visual.Settings }, }; - CurrentDirectory.BindValueChanged(dir => + currentDirectory.BindValueChanged(dir => { flow.Clear(); - flow.Add(new OsuSpriteText { Text = "Current Directory: " }); - - var pieces = dir.NewValue.Split(Path.DirectorySeparatorChar); - - pieces[0] = "/"; - - for (int i = 0; i < pieces.Length; i++) + flow.Add(new OsuSpriteText { - flow.Add(new DirectoryRow(pieces[i], Path.Combine(pieces.Take(i + 1).ToArray())) - { - CurrentDirectory = { BindTarget = CurrentDirectory }, - RelativeSizeAxes = Axes.Y, - Size = new Vector2(100, 1) - }); + Text = "Current Directory: ", + Font = OsuFont.Default.With(size: DirectoryRow.HEIGHT), + }); + + flow.Add(new DirectoryRow(null, "Computer")); + + DirectoryInfo traverse = dir.NewValue; + + while (traverse != null) + { + flow.Add(new DirectoryRow(traverse)); + traverse = traverse.Parent; } - }); + }, true); } } @@ -145,48 +151,48 @@ namespace osu.Game.Tests.Visual.Settings { public override IconUsage Icon => FontAwesome.Solid.Folder; - public ParentDirectoryRow(string fullPath) - : base("..", fullPath) + public ParentDirectoryRow(DirectoryInfo directory) + : base(directory, "..") { } } - private class DirectoryRow : OsuButton + private class DirectoryRow : CompositeDrawable { - private readonly string fullPath; + public const float HEIGHT = 20; - public readonly Bindable CurrentDirectory = new Bindable(); + private readonly DirectoryInfo directory; - public DirectoryRow(string display, string fullPath) + [Resolved] + private Bindable currentDirectory { get; set; } + + public DirectoryRow(DirectoryInfo directory, string display = null) { - this.fullPath = fullPath; + this.directory = directory; - RelativeSizeAxes = Axes.X; - Height = 20; + AutoSizeAxes = Axes.X; + Height = HEIGHT; - BackgroundColour = OsuColour.Gray(0.1f); - - AddRange(new Drawable[] + AddRangeInternal(new Drawable[] { new SpriteIcon { Icon = Icon, - Size = new Vector2(20) + Size = new Vector2(HEIGHT) }, new OsuSpriteText { - X = 25, - Text = display, - Font = OsuFont.Default.With(size: 20) + X = HEIGHT + 5, + Text = display ?? directory.Name, + Font = OsuFont.Default.With(size: HEIGHT) } }); - - Action = PerformDirectoryTraversal; } - protected virtual void PerformDirectoryTraversal() + protected override bool OnClick(ClickEvent e) { - CurrentDirectory.Value = fullPath; + currentDirectory.Value = directory; + return true; } public virtual IconUsage Icon => FontAwesome.Regular.Folder; From 49e4fc6cba39028b5be157b975a26ae85e2e2161 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 May 2020 18:37:14 +0900 Subject: [PATCH 0690/1117] Move to better namespace --- .../Settings/TestSceneDirectorySelector.cs | 184 +----------- .../UserInterfaceV2/DirectorySelector.cs | 261 ++++++++++++++++++ 2 files changed, 262 insertions(+), 183 deletions(-) create mode 100644 osu.Game/Graphics/UserInterfaceV2/DirectorySelector.cs diff --git a/osu.Game.Tests/Visual/Settings/TestSceneDirectorySelector.cs b/osu.Game.Tests/Visual/Settings/TestSceneDirectorySelector.cs index beb5353115..0cd0f13b5f 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneDirectorySelector.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneDirectorySelector.cs @@ -1,20 +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.IO; -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.Input.Events; using osu.Framework.Platform; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using osuTK; +using osu.Game.Graphics.UserInterfaceV2; namespace osu.Game.Tests.Visual.Settings { @@ -26,176 +16,4 @@ namespace osu.Game.Tests.Visual.Settings Add(new DirectorySelector { RelativeSizeAxes = Axes.Both }); } } - - public class DirectorySelector : CompositeDrawable - { - private FillFlowContainer directoryFlow; - - [Resolved] - private GameHost host { get; set; } - - [Cached] - private readonly Bindable currentDirectory = new Bindable(); - - public DirectorySelector(string initialPath = null) - { - currentDirectory.Value = new DirectoryInfo(initialPath ??= Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - Padding = new MarginPadding(10); - - InternalChildren = new Drawable[] - { - new FillFlowContainer - { - RelativeSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - new CurrentDirectoryDisplay - { - RelativeSizeAxes = Axes.X, - Height = 50, - }, - new OsuScrollContainer - { - RelativeSizeAxes = Axes.Both, - Child = directoryFlow = new FillFlowContainer - { - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - Direction = FillDirection.Vertical, - } - } - } - }, - }; - - currentDirectory.BindValueChanged(updateDisplay, true); - } - - private void updateDisplay(ValueChangedEvent directory) - { - directoryFlow.Clear(); - - if (directory.NewValue == null) - { - // var drives = DriveInfo.GetDrives(); - // - // foreach (var drive in drives) - // directoryFlow.Add(new DirectoryRow(drive.RootDirectory)); - } - else - { - directoryFlow.Add(new ParentDirectoryRow(currentDirectory.Value.Parent)); - - foreach (var dir in currentDirectory.Value.GetDirectories().OrderBy(d => d.Name)) - { - if ((dir.Attributes & FileAttributes.Hidden) == 0) - directoryFlow.Add(new DirectoryRow(dir)); - } - } - } - - public class CurrentDirectoryDisplay : CompositeDrawable - { - [Resolved] - private Bindable currentDirectory { get; set; } - - [BackgroundDependencyLoader] - private void load() - { - FillFlowContainer flow; - - InternalChildren = new Drawable[] - { - flow = new FillFlowContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.X, - Spacing = new Vector2(10), - Height = 20, - Direction = FillDirection.Horizontal, - }, - }; - - currentDirectory.BindValueChanged(dir => - { - flow.Clear(); - - flow.Add(new OsuSpriteText - { - Text = "Current Directory: ", - Font = OsuFont.Default.With(size: DirectoryRow.HEIGHT), - }); - - flow.Add(new DirectoryRow(null, "Computer")); - - DirectoryInfo traverse = dir.NewValue; - - while (traverse != null) - { - flow.Add(new DirectoryRow(traverse)); - traverse = traverse.Parent; - } - }, true); - } - } - - private class ParentDirectoryRow : DirectoryRow - { - public override IconUsage Icon => FontAwesome.Solid.Folder; - - public ParentDirectoryRow(DirectoryInfo directory) - : base(directory, "..") - { - } - } - - private class DirectoryRow : CompositeDrawable - { - public const float HEIGHT = 20; - - private readonly DirectoryInfo directory; - - [Resolved] - private Bindable currentDirectory { get; set; } - - public DirectoryRow(DirectoryInfo directory, string display = null) - { - this.directory = directory; - - AutoSizeAxes = Axes.X; - Height = HEIGHT; - - AddRangeInternal(new Drawable[] - { - new SpriteIcon - { - Icon = Icon, - Size = new Vector2(HEIGHT) - }, - new OsuSpriteText - { - X = HEIGHT + 5, - Text = display ?? directory.Name, - Font = OsuFont.Default.With(size: HEIGHT) - } - }); - } - - protected override bool OnClick(ClickEvent e) - { - currentDirectory.Value = directory; - return true; - } - - public virtual IconUsage Icon => FontAwesome.Regular.Folder; - } - } } diff --git a/osu.Game/Graphics/UserInterfaceV2/DirectorySelector.cs b/osu.Game/Graphics/UserInterfaceV2/DirectorySelector.cs new file mode 100644 index 0000000000..5dcaacf0a4 --- /dev/null +++ b/osu.Game/Graphics/UserInterfaceV2/DirectorySelector.cs @@ -0,0 +1,261 @@ +// 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.Linq; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +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.Platform; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osuTK; + +namespace osu.Game.Graphics.UserInterfaceV2 +{ + public class DirectorySelector : CompositeDrawable + { + private FillFlowContainer directoryFlow; + + [Resolved] + private GameHost host { get; set; } + + [Cached] + private readonly Bindable currentDirectory = new Bindable(); + + public DirectorySelector(string initialPath = null) + { + currentDirectory.Value = new DirectoryInfo(initialPath ??= Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Padding = new MarginPadding(10); + + InternalChildren = new Drawable[] + { + new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new CurrentDirectoryDisplay + { + RelativeSizeAxes = Axes.X, + Height = 50, + }, + new OsuScrollContainer + { + RelativeSizeAxes = Axes.Both, + Child = directoryFlow = new FillFlowContainer + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Direction = FillDirection.Vertical, + Spacing = new Vector2(2), + } + } + } + }, + }; + + currentDirectory.BindValueChanged(updateDisplay, true); + } + + private void updateDisplay(ValueChangedEvent directory) + { + directoryFlow.Clear(); + + if (directory.NewValue == null) + { + var drives = DriveInfo.GetDrives(); + + foreach (var drive in drives) + directoryFlow.Add(new DirectoryRow(drive.RootDirectory)); + } + else + { + directoryFlow.Add(new ParentDirectoryRow(currentDirectory.Value.Parent)); + + foreach (var dir in currentDirectory.Value.GetDirectories().OrderBy(d => d.Name)) + { + if ((dir.Attributes & FileAttributes.Hidden) == 0) + directoryFlow.Add(new DirectoryRow(dir)); + } + } + } + + public class CurrentDirectoryDisplay : CompositeDrawable + { + [Resolved] + private Bindable currentDirectory { get; set; } + + [BackgroundDependencyLoader] + private void load() + { + FillFlowContainer flow; + + InternalChildren = new Drawable[] + { + flow = new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.X, + Spacing = new Vector2(5), + Height = DirectoryRow.HEIGHT, + Direction = FillDirection.Horizontal, + }, + }; + + currentDirectory.BindValueChanged(dir => + { + flow.Clear(); + + flow.Add(new OsuSpriteText + { + Text = "Current Directory: ", + Font = OsuFont.Default.With(size: DirectoryRow.HEIGHT), + }); + + flow.Add(new ComputerRow()); + + List traversalRows = new List(); + + DirectoryInfo traverse = dir.NewValue; + + while (traverse != null) + { + traversalRows.Insert(0, new CurrentDisplayRow(traverse)); + traverse = traverse.Parent; + } + + flow.AddRange(traversalRows); + }, true); + } + + private class ComputerRow : CurrentDisplayRow + { + public override IconUsage? Icon => null; + + public ComputerRow() + : base(null, "Computer") + { + } + } + + private class CurrentDisplayRow : DirectoryRow + { + public CurrentDisplayRow(DirectoryInfo directory, string displayName = null) + : base(directory, displayName) + { + } + + [BackgroundDependencyLoader] + private void load() + { + Flow.Add(new SpriteIcon + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Icon = FontAwesome.Solid.ChevronRight, + Size = new Vector2(FONT_SIZE / 2) + }); + } + } + } + + private class ParentDirectoryRow : DirectoryRow + { + public override IconUsage? Icon => FontAwesome.Solid.Folder; + + public ParentDirectoryRow(DirectoryInfo directory) + : base(directory, "..") + { + } + } + + private class DirectoryRow : CompositeDrawable + { + public const float HEIGHT = 20; + + protected const float FONT_SIZE = 16; + + private readonly DirectoryInfo directory; + private readonly string displayName; + + protected FillFlowContainer Flow; + + [Resolved] + private Bindable currentDirectory { get; set; } + + public DirectoryRow(DirectoryInfo directory, string displayName = null) + { + this.directory = directory; + this.displayName = displayName; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + AutoSizeAxes = Axes.Both; + + Masking = true; + CornerRadius = 5; + + InternalChildren = new Drawable[] + { + new Box + { + Colour = colours.GreySeafoamDarker, + RelativeSizeAxes = Axes.Both, + }, + Flow = new FillFlowContainer + { + AutoSizeAxes = Axes.X, + Height = 20, + Margin = new MarginPadding { Vertical = 2, Horizontal = 5 }, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(5), + } + }; + + if (Icon.HasValue) + { + Flow.Add(new SpriteIcon + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Icon = Icon.Value, + Size = new Vector2(FONT_SIZE) + }); + } + + Flow.Add(new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Text = displayName ?? directory.Name, + Font = OsuFont.Default.With(size: FONT_SIZE) + }); + } + + protected override bool OnClick(ClickEvent e) + { + currentDirectory.Value = directory; + return true; + } + + public virtual IconUsage? Icon => FontAwesome.Regular.Folder; + } + } +} From 246812e0b1b550c0bebb63739ad67a4b45f0f124 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 May 2020 18:42:20 +0900 Subject: [PATCH 0691/1117] Change breadcrumb display icons to match design --- .../UserInterfaceV2/DirectorySelector.cs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/osu.Game/Graphics/UserInterfaceV2/DirectorySelector.cs b/osu.Game/Graphics/UserInterfaceV2/DirectorySelector.cs index 5dcaacf0a4..1cd4ab6f01 100644 --- a/osu.Game/Graphics/UserInterfaceV2/DirectorySelector.cs +++ b/osu.Game/Graphics/UserInterfaceV2/DirectorySelector.cs @@ -145,7 +145,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 private class ComputerRow : CurrentDisplayRow { - public override IconUsage? Icon => null; + protected override IconUsage? Icon => null; public ComputerRow() : base(null, "Computer") @@ -171,12 +171,14 @@ namespace osu.Game.Graphics.UserInterfaceV2 Size = new Vector2(FONT_SIZE / 2) }); } + + protected override IconUsage? Icon => Directory.Name.Contains("/") ? base.Icon : null; } } private class ParentDirectoryRow : DirectoryRow { - public override IconUsage? Icon => FontAwesome.Solid.Folder; + protected override IconUsage? Icon => FontAwesome.Solid.Folder; public ParentDirectoryRow(DirectoryInfo directory) : base(directory, "..") @@ -190,7 +192,8 @@ namespace osu.Game.Graphics.UserInterfaceV2 protected const float FONT_SIZE = 16; - private readonly DirectoryInfo directory; + protected readonly DirectoryInfo Directory; + private readonly string displayName; protected FillFlowContainer Flow; @@ -200,7 +203,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 public DirectoryRow(DirectoryInfo directory, string displayName = null) { - this.directory = directory; + Directory = directory; this.displayName = displayName; } @@ -244,18 +247,18 @@ namespace osu.Game.Graphics.UserInterfaceV2 { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Text = displayName ?? directory.Name, + Text = displayName ?? Directory.Name, Font = OsuFont.Default.With(size: FONT_SIZE) }); } protected override bool OnClick(ClickEvent e) { - currentDirectory.Value = directory; + currentDirectory.Value = Directory; return true; } - public virtual IconUsage? Icon => FontAwesome.Regular.Folder; + protected virtual IconUsage? Icon => Directory.Name.Contains("/") ? FontAwesome.Solid.Database : FontAwesome.Regular.Folder; } } } From e9e03d038d780f13d62a3b29e9c6243148f10ebc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 May 2020 18:52:10 +0900 Subject: [PATCH 0692/1117] Tidy up naming and structure --- .../UserInterfaceV2/DirectorySelector.cs | 87 +++++++++---------- 1 file changed, 43 insertions(+), 44 deletions(-) diff --git a/osu.Game/Graphics/UserInterfaceV2/DirectorySelector.cs b/osu.Game/Graphics/UserInterfaceV2/DirectorySelector.cs index 1cd4ab6f01..427b9a1eff 100644 --- a/osu.Game/Graphics/UserInterfaceV2/DirectorySelector.cs +++ b/osu.Game/Graphics/UserInterfaceV2/DirectorySelector.cs @@ -34,10 +34,9 @@ namespace osu.Game.Graphics.UserInterfaceV2 currentDirectory.Value = new DirectoryInfo(initialPath ??= Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)); } - protected override void LoadComplete() + [BackgroundDependencyLoader] + private void load() { - base.LoadComplete(); - Padding = new MarginPadding(10); InternalChildren = new Drawable[] @@ -80,30 +79,30 @@ namespace osu.Game.Graphics.UserInterfaceV2 var drives = DriveInfo.GetDrives(); foreach (var drive in drives) - directoryFlow.Add(new DirectoryRow(drive.RootDirectory)); + directoryFlow.Add(new DirectoryPiece(drive.RootDirectory)); } else { - directoryFlow.Add(new ParentDirectoryRow(currentDirectory.Value.Parent)); + directoryFlow.Add(new ParentDirectoryPiece(currentDirectory.Value.Parent)); foreach (var dir in currentDirectory.Value.GetDirectories().OrderBy(d => d.Name)) { if ((dir.Attributes & FileAttributes.Hidden) == 0) - directoryFlow.Add(new DirectoryRow(dir)); + directoryFlow.Add(new DirectoryPiece(dir)); } } } - public class CurrentDirectoryDisplay : CompositeDrawable + private class CurrentDirectoryDisplay : CompositeDrawable { [Resolved] private Bindable currentDirectory { get; set; } + private FillFlowContainer flow; + [BackgroundDependencyLoader] private void load() { - FillFlowContainer flow; - InternalChildren = new Drawable[] { flow = new FillFlowContainer @@ -112,50 +111,48 @@ namespace osu.Game.Graphics.UserInterfaceV2 Origin = Anchor.Centre, RelativeSizeAxes = Axes.X, Spacing = new Vector2(5), - Height = DirectoryRow.HEIGHT, + Height = DirectoryPiece.HEIGHT, Direction = FillDirection.Horizontal, }, }; - currentDirectory.BindValueChanged(dir => - { - flow.Clear(); - - flow.Add(new OsuSpriteText - { - Text = "Current Directory: ", - Font = OsuFont.Default.With(size: DirectoryRow.HEIGHT), - }); - - flow.Add(new ComputerRow()); - - List traversalRows = new List(); - - DirectoryInfo traverse = dir.NewValue; - - while (traverse != null) - { - traversalRows.Insert(0, new CurrentDisplayRow(traverse)); - traverse = traverse.Parent; - } - - flow.AddRange(traversalRows); - }, true); + currentDirectory.BindValueChanged(updateDisplay, true); } - private class ComputerRow : CurrentDisplayRow + private void updateDisplay(ValueChangedEvent dir) + { + flow.Clear(); + + List pathPieces = new List(); + + DirectoryInfo ptr = dir.NewValue; + + while (ptr != null) + { + pathPieces.Insert(0, new CurrentDisplayPiece(ptr)); + ptr = ptr.Parent; + } + + flow.ChildrenEnumerable = new Drawable[] + { + new OsuSpriteText { Text = "Current Directory: ", Font = OsuFont.Default.With(size: DirectoryPiece.HEIGHT), }, + new ComputerPiece(), + }.Concat(pathPieces); + } + + private class ComputerPiece : CurrentDisplayPiece { protected override IconUsage? Icon => null; - public ComputerRow() + public ComputerPiece() : base(null, "Computer") { } } - private class CurrentDisplayRow : DirectoryRow + private class CurrentDisplayPiece : DirectoryPiece { - public CurrentDisplayRow(DirectoryInfo directory, string displayName = null) + public CurrentDisplayPiece(DirectoryInfo directory, string displayName = null) : base(directory, displayName) { } @@ -172,21 +169,21 @@ namespace osu.Game.Graphics.UserInterfaceV2 }); } - protected override IconUsage? Icon => Directory.Name.Contains("/") ? base.Icon : null; + protected override IconUsage? Icon => Directory.Name.Contains(Path.DirectorySeparatorChar) ? base.Icon : null; } } - private class ParentDirectoryRow : DirectoryRow + private class ParentDirectoryPiece : DirectoryPiece { protected override IconUsage? Icon => FontAwesome.Solid.Folder; - public ParentDirectoryRow(DirectoryInfo directory) + public ParentDirectoryPiece(DirectoryInfo directory) : base(directory, "..") { } } - private class DirectoryRow : CompositeDrawable + private class DirectoryPiece : CompositeDrawable { public const float HEIGHT = 20; @@ -201,7 +198,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 [Resolved] private Bindable currentDirectory { get; set; } - public DirectoryRow(DirectoryInfo directory, string displayName = null) + public DirectoryPiece(DirectoryInfo directory, string displayName = null) { Directory = directory; this.displayName = displayName; @@ -258,7 +255,9 @@ namespace osu.Game.Graphics.UserInterfaceV2 return true; } - protected virtual IconUsage? Icon => Directory.Name.Contains("/") ? FontAwesome.Solid.Database : FontAwesome.Regular.Folder; + protected virtual IconUsage? Icon => Directory.Name.Contains(Path.DirectorySeparatorChar) + ? FontAwesome.Solid.Database + : FontAwesome.Regular.Folder; } } } From c048d9b6ae215eb0923ba39d060e5ae1adb0ace7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 May 2020 18:55:06 +0900 Subject: [PATCH 0693/1117] Fix incorrect assignment --- osu.Game/Graphics/UserInterfaceV2/DirectorySelector.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterfaceV2/DirectorySelector.cs b/osu.Game/Graphics/UserInterfaceV2/DirectorySelector.cs index 427b9a1eff..59de931df5 100644 --- a/osu.Game/Graphics/UserInterfaceV2/DirectorySelector.cs +++ b/osu.Game/Graphics/UserInterfaceV2/DirectorySelector.cs @@ -31,7 +31,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 public DirectorySelector(string initialPath = null) { - currentDirectory.Value = new DirectoryInfo(initialPath ??= Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)); + currentDirectory.Value = new DirectoryInfo(initialPath ?? Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)); } [BackgroundDependencyLoader] From 00efeb7cc6db07eac414dab21366b041fa46f6ba Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 May 2020 19:19:58 +0900 Subject: [PATCH 0694/1117] Fix spawning too many sprites due to not yet populated sizing --- .../Skinning/LegacyTaikoScroller.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs b/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs index 3ec6be8a6c..03813e0a99 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs @@ -17,6 +17,8 @@ namespace osu.Game.Rulesets.Taiko.Skinning { public class LegacyTaikoScroller : CompositeDrawable { + public Bindable LastResult = new Bindable(); + public LegacyTaikoScroller() { RelativeSizeAxes = Axes.Both; @@ -50,21 +52,16 @@ namespace osu.Game.Rulesets.Taiko.Skinning }, true); } - public Bindable LastResult = new Bindable(); - protected override void Update() { base.Update(); - bool wideEnough() => - InternalChildren.Any() - && InternalChildren.First().ScreenSpaceDrawQuad.Width * InternalChildren.Count >= ScreenSpaceDrawQuad.Width * 2; - // store X before checking wide enough so if we perform layout there is no positional discrepancy. float currentX = (InternalChildren?.FirstOrDefault()?.X ?? 0) - (float)Clock.ElapsedFrameTime * 0.1f; // ensure we have enough sprites - while (!wideEnough()) + if (!InternalChildren.Any() + || InternalChildren.First().ScreenSpaceDrawQuad.Width * InternalChildren.Count < ScreenSpaceDrawQuad.Width * 2) AddInternal(new ScrollerSprite { Passing = passing }); var first = InternalChildren.First(); From 49e616b7e561ab940a2fa10f294af179f2b39e91 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 May 2020 20:19:14 +0900 Subject: [PATCH 0695/1117] Also check for directory presence before migrating --- osu.Game/IO/OsuStorage.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/IO/OsuStorage.cs b/osu.Game/IO/OsuStorage.cs index 6dc25e871c..b060add03b 100644 --- a/osu.Game/IO/OsuStorage.cs +++ b/osu.Game/IO/OsuStorage.cs @@ -51,7 +51,7 @@ namespace osu.Game.IO // ensure the new location has no files present, else hard abort if (destination.Exists) { - if (destination.GetFiles().Length > 0) + if (destination.GetFiles().Length > 0 || destination.GetDirectories().Length > 0) throw new InvalidOperationException("Migration destination already has files present"); deleteRecursive(destination); From ad1d050fb437673f35380f8751c7a3f7cc68c7e9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 May 2020 20:29:15 +0900 Subject: [PATCH 0696/1117] Throw exception on copy timeout --- osu.Game/IO/OsuStorage.cs | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/osu.Game/IO/OsuStorage.cs b/osu.Game/IO/OsuStorage.cs index b060add03b..71b01ce479 100644 --- a/osu.Game/IO/OsuStorage.cs +++ b/osu.Game/IO/OsuStorage.cs @@ -96,20 +96,7 @@ namespace osu.Game.IO if (topLevelExcludes && IGNORE_FILES.Contains(fi.Name)) continue; - int tries = 5; - - while (tries-- > 0) - { - try - { - fi.CopyTo(Path.Combine(destination.FullName, fi.Name), true); - break; - } - catch (Exception) - { - Thread.Sleep(50); - } - } + attemptCopy(fi, Path.Combine(destination.FullName, fi.Name)); } foreach (DirectoryInfo dir in source.GetDirectories()) @@ -120,5 +107,26 @@ namespace osu.Game.IO copyRecursive(dir, destination.CreateSubdirectory(dir.Name), false); } } + + private static void attemptCopy(System.IO.FileInfo fileInfo, string destination) + { + int tries = 5; + + while (true) + { + try + { + fileInfo.CopyTo(destination, true); + return; + } + catch (Exception) + { + if (tries-- == 0) + throw; + } + + Thread.Sleep(50); + } + } } } From 1ac9c7c15a88fe24132084b93c2d619cfb46b2da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=80=8C=E7=A9=BA=E7=99=BD=E3=80=8D?= <「空白」> Date: Thu, 14 May 2020 00:04:31 +0900 Subject: [PATCH 0697/1117] Add license header to WebRequestExtensions --- osu.Game/Extensions/WebRequestExtensions.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Extensions/WebRequestExtensions.cs b/osu.Game/Extensions/WebRequestExtensions.cs index c8e3c564a5..f92f707d30 100644 --- a/osu.Game/Extensions/WebRequestExtensions.cs +++ b/osu.Game/Extensions/WebRequestExtensions.cs @@ -1,4 +1,7 @@ -using osu.Framework.IO.Network; +// 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.IO.Network; using osu.Framework.Extensions.IEnumerableExtensions; using System.Collections.Generic; using Newtonsoft.Json; From 0933217389f9b08c4d64815b31fa352cd8a4ce16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 13 May 2020 18:53:47 +0200 Subject: [PATCH 0698/1117] Simplify mascot scaling --- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 5940ee8b69..ded1fc0933 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -5,7 +5,6 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Layout; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; using osu.Game.Rulesets.Objects.Drawables; @@ -43,14 +42,9 @@ namespace osu.Game.Rulesets.Taiko.UI private Container hitTargetOffsetContent; - private readonly LayoutValue playfieldScaleLayout = new LayoutValue(Invalidation.DrawSize); - private float playfieldScale => playfieldScaleLayout.IsValid ? playfieldScaleLayout.Value : playfieldScaleLayout.Value = DrawHeight / DEFAULT_HEIGHT; - public TaikoPlayfield(ControlPointInfo controlPoints) { this.controlPoints = controlPoints; - - AddLayout(playfieldScaleLayout); } [BackgroundDependencyLoader] @@ -159,7 +153,7 @@ namespace osu.Game.Rulesets.Taiko.UI rightArea.Padding = new MarginPadding { Left = leftArea.DrawWidth }; hitTargetOffsetContent.Padding = new MarginPadding { Left = HitTarget.DrawWidth / 2 }; - mascot.Scale = new Vector2(playfieldScale); + mascot.Scale = new Vector2(DrawHeight / DEFAULT_HEIGHT); } public override void Add(DrawableHitObject h) From 43450b54853712698da7909e31568a5ecd6b4566 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=80=8C=E7=A9=BA=E7=99=BD=E3=80=8D?= <「空白」> Date: Thu, 14 May 2020 01:57:03 +0900 Subject: [PATCH 0699/1117] Resolve remaining InspectCode issues > CI should now pass build test --- osu.Game/Extensions/WebRequestExtensions.cs | 2 ++ .../Online/API/Requests/SearchBeatmapSetsRequest.cs | 13 ++++++------- .../Overlays/BeatmapListing/BeatmapListingPager.cs | 4 ++-- osu.Game/Overlays/BeatmapListingOverlay.cs | 3 +-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/osu.Game/Extensions/WebRequestExtensions.cs b/osu.Game/Extensions/WebRequestExtensions.cs index f92f707d30..80c8b147bf 100644 --- a/osu.Game/Extensions/WebRequestExtensions.cs +++ b/osu.Game/Extensions/WebRequestExtensions.cs @@ -6,11 +6,13 @@ using osu.Framework.Extensions.IEnumerableExtensions; using System.Collections.Generic; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using JetBrains.Annotations; namespace osu.Game.Extensions { public class Cursor { + [UsedImplicitly] [JsonExtensionData] public IDictionary Properties; } diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs index a49cb70c37..0c3272c7de 100644 --- a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs +++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs @@ -11,15 +11,15 @@ namespace osu.Game.Online.API.Requests { public class SearchBeatmapSetsRequest : APIRequest { - public SearchCategory SearchCategory { get; set; } + public SearchCategory SearchCategory { get; } - public SortCriteria SortCriteria { get; set; } + public SortCriteria SortCriteria { get; } - public SortDirection SortDirection { get; set; } + public SortDirection SortDirection { get; } - public SearchGenre Genre { get; set; } + public SearchGenre Genre { get; } - public SearchLanguage Language { get; set; } + public SearchLanguage Language { get; } private readonly string query; private readonly RulesetInfo ruleset; @@ -27,8 +27,7 @@ namespace osu.Game.Online.API.Requests private string directionString => SortDirection == SortDirection.Descending ? @"desc" : @"asc"; - public SearchBeatmapSetsRequest(string query, RulesetInfo ruleset, Cursor cursor = null, - SearchCategory searchCategory = SearchCategory.Any, SortCriteria sortCriteria = SortCriteria.Ranked, SortDirection sortDirection = SortDirection.Descending) + public SearchBeatmapSetsRequest(string query, RulesetInfo ruleset, Cursor cursor = null, SearchCategory searchCategory = SearchCategory.Any, SortCriteria sortCriteria = SortCriteria.Ranked, SortDirection sortDirection = SortDirection.Descending) { this.query = string.IsNullOrEmpty(query) ? string.Empty : System.Uri.EscapeDataString(query); this.ruleset = ruleset; diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingPager.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingPager.cs index 4c8902d314..f55e37ebc7 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingPager.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingPager.cs @@ -24,9 +24,9 @@ namespace osu.Game.Overlays.BeatmapListing private SearchBeatmapSetsRequest getSetsRequest; private SearchBeatmapSetsResponse lastResponse; - private bool isLastPageFetched = false; + private bool isLastPageFetched; private bool isFetching => getSetsRequest != null; - public bool IsPastFirstPage { get; private set; } = false; + public bool IsPastFirstPage { get; private set; } public bool CanFetchNextPage => !isLastPageFetched && !isFetching; public BeatmapListingPager(IAPIProvider api, RulesetStore rulesets, string query, RulesetInfo ruleset, SearchCategory searchCategory = SearchCategory.Any, SortCriteria sortCriteria = SortCriteria.Ranked, SortDirection sortDirection = SortDirection.Descending) diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index ba92181ac5..e26f084ea4 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -184,8 +184,7 @@ namespace osu.Game.Overlays // If the auto-size computation is delayed until fade out completes, the background remain high for too long making the resulting transition to the smaller height look weird. // At the same time, if the last content's height is bypassed immediately, there is a period where the new content is at Alpha = 0 when the auto-sized height will be 0. // To resolve both of these issues, the bypass is delayed until a point when the content transitions (fade-in and fade-out) overlap and it looks good to do so. - lastContent.Delay(25).Schedule(() => lastContent.BypassAutoSizeAxes = Axes.Y) - .Then().Schedule(() => panelTarget.Remove(lastContent)); + lastContent.Delay(25).Schedule(() => lastContent.BypassAutoSizeAxes = Axes.Y).Then().Schedule(() => panelTarget.Remove(lastContent)); } if (!content.IsAlive) From 9ba1a8af885b2334822506ee5d789ca19fff9902 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 May 2020 09:44:21 +0900 Subject: [PATCH 0700/1117] Fix mascot getting stuck in clear state on rewind --- osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs | 6 +++--- osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs index 105baa84cc..407ab30e12 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs @@ -96,9 +96,9 @@ namespace osu.Game.Rulesets.Taiko.UI private TaikoMascotAnimationState getNextState() { - // don't change state if current animation is playing - // (used for clear state - others are manually animated on new beats) - if (currentAnimation != null && !currentAnimation.Completed) + // don't change state if current animation is still playing (and we haven't rewound before it). + // used for clear state - others are manually animated on new beats. + if (currentAnimation?.Completed == false && currentAnimation.DisplayTime <= Time.Current) return State.Value; if (!lastObjectHit) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs b/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs index 01cf88a87e..cce2be7758 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs @@ -20,6 +20,8 @@ namespace osu.Game.Rulesets.Taiko.UI private int currentFrame; + public double DisplayTime; + public TaikoMascotAnimation(TaikoMascotAnimationState state) { InternalChild = textureAnimation = createTextureAnimation(state).With(animation => @@ -40,6 +42,7 @@ namespace osu.Game.Rulesets.Taiko.UI public override void Show() { base.Show(); + DisplayTime = Time.Current; textureAnimation.Seek(0); } From 76af6f25f1ccd0e0f2e6697ed7fdc8d5546ebc15 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 May 2020 09:56:30 +0900 Subject: [PATCH 0701/1117] Remove pointless test resources --- .../Resources/old-skin/pippidonclear0.png | Bin 72589 -> 0 bytes .../Resources/old-skin/pippidonclear1.png | Bin 40613 -> 0 bytes .../Resources/old-skin/pippidonclear2.png | Bin 73308 -> 0 bytes .../Resources/old-skin/pippidonclear3.png | Bin 34541 -> 0 bytes .../Resources/old-skin/pippidonclear4.png | Bin 71177 -> 0 bytes .../Resources/old-skin/pippidonclear5.png | Bin 77056 -> 0 bytes .../Resources/old-skin/pippidonclear6.png | Bin 78392 -> 0 bytes .../Resources/old-skin/pippidonclear7.png | Bin 77056 -> 0 bytes .../Resources/old-skin/pippidonclear8.png | Bin 71177 -> 0 bytes .../Resources/old-skin/pippidonfail0.png | Bin 67970 -> 0 bytes .../Resources/old-skin/pippidonfail1.png | Bin 69118 -> 0 bytes .../Resources/old-skin/pippidonfail2.png | Bin 73351 -> 0 bytes .../Resources/old-skin/pippidonidle0.png | Bin 68649 -> 0 bytes .../Resources/old-skin/pippidonidle1.png | Bin 69329 -> 0 bytes .../Resources/old-skin/pippidonkiai0.png | Bin 76964 -> 0 bytes .../Resources/old-skin/pippidonkiai1.png | Bin 75434 -> 0 bytes 16 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear0.png delete mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear1.png delete mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear2.png delete mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear3.png delete mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear4.png delete mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear5.png delete mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear6.png delete mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear7.png delete mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear8.png delete mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonfail0.png delete mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonfail1.png delete mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonfail2.png delete mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonidle0.png delete mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonidle1.png delete mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonkiai0.png delete mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonkiai1.png diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear0.png b/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear0.png deleted file mode 100644 index a5f4d03e2a61075513c85f24ab39280c31f591d9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 72589 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�F*Q&jpbtTl0ro%YGC`VnPkLo4Qt@L z#vt!}@>>R}YsVov?S!j!{xxkipf|%A_e=R;$v0DVATGDtBRI6FdY{h@Ke=&S!$7q@ z>l*d!Rz-Ch4Az~4F4TyJ&ni^!zhir*p8N1a&n5>74ATW2`Y53fq2lEeVe6JFF^P>Y za?qwxeFYt%Jjr6#{lo@(XwqYv^26u4`1orSUZu19RWFzZKV#K*+VaNT?i0QTJL@#M z>17S+G}Kgz+_S#f&h1Ju#N$Z)&3(oXZ#XlSDLwuXrNo?K9-mfL#%cuw)lq2FL+*DJ z4OyA1O_?7Wb<@9D7uBs(hz3={XxR~Wv!2In99^hg zVDOQTz4;k!*qIYK(YW5}s&h&4DtT!kV)sf@Hn|mVsHs(j>6n;2@7g0$umAFj#^3pf zIyAq)RP{WCm7S^|jk3=8xb#-dW(5VS#SJg%)DC7;6p&}AWt4t;+&#*OXO&#(DgE*a zdsE+(nDdpk=Sribe>4SXXPXODSoJ5wa{S`Jk(9IZkqtK0xb}w2k0`8sg{D0a6Zul6 z&#&fKA!-0MFa*I7j1(ed1}4qb@@v#SRrj5JmM(eh?8C-Pdt=q2-C7u%*Xc0zs+28?rOAZ zTW*D25GcoC_nx75KO3(r`;JmbcuV&yE)bXf!TiYz|E2VDL_5PPtK&7#YCs2as~mcx zGn2nCL;EY0?Qkvc|1W__ZU~tUD|y#x%1C(GC|@FFy%D6a8qX*wtf%|9XdrJ-X6m7C zk0v#{Yv2fW=_`FC?!vQLK-7VcaI6qDzXRGu{*WvMos2^X$FLPc7ZMzzN|8PE*!kya zFS>!x4F2uqo(MPrjhkWHyg5 zl~kcRuYGSN3@qBdj%9(2jCeD=&0UzA_s1y3lft)fK!JDaR7p=xdOpdP)>HMJ=pA69mv()QhE zNR0JX6&>uKxWfvEb0kkz#=$qOrQ69nPg82_B}O@qH=Psr_3)g;wqFr7UT|+zTUi%97uOa7* zRDwG>5X1ia&c9g)PQAuDF7Jrgvqf(`cY_k+y~-#cLQf1Gt-C)zx*QDie&3_>hL2EE za_%Hlotj^ulka{*j)+1m@hkxZWH}Y^^-Y?-D^qL!`PzNEkFZW^{6l; z4N#oTqS)8L3)!ifw0w23%f&|DlWObRNna@$4%{>zPmzugeoXk!m4J^pcJ9C*(16K5p6q0EEt z8a4WneOhN74bo4XvHq zNnmxfBn|Rgw$CcpVP~FFW+V;P#=3IYw2bG8NelG))F*YyX_xBWGw;z4 zZ!grrBJ`s~dAvFLb4_2(x*XSyuUfs2`gA|pdR~!mVoT<1b62Hx`!;Fzq~f_hywUfB znwtFMWQB&YxsMxJ>6)_Q2c=K)ZDzZ${V{URo2ErCJ)oC=nX0*eib_jaAv` z{QOFkn?C2a-MZkCbCi+h_O}Lx>bzbTsKIr)b7WTpuw(lxk6*0J^qi1&CY0_u`DQ&a zsc(Vu_Vrb{kAM~=*#WdFE{Zn|TFoUL;!?t&0@V7G#HZBuB%PC4UuYv=A* z#l**JPhyhdQxOV+4EP@{g0Hjxz6UPC4lrd{P1S21uYL{c>!dEntK%gXt5v(o zt_TtHDYji(wJJduoi|(wNjZMqTJ=uXO@q!<+@P!7XDSwcdGJzgh?(p@a-r#Q=jn&Z zZ>d@9qxU6~s^!!1i*@URm&ut)9xfNcBf9FcXMa>!wJ2*B`N}&tC@^M|LK3zsAoYNG z??J^JOw-21RP9d6(EjvPrKBb*J$=72QuoM}k~Nn?Di92PX*cALu@io=yHH?|F~cO@ zON>LRUTxIy&E{EAYhmHQD!{DtC32=OvMeiuO-G9MdyWJmkb=WH7#-1BL1DcV6izeq z4O=?X|5n=GtK3I9h^*dUlg5ov-=Rm&6XQ#K_0lX|_2>vw-U)d(5BSj79AkupP%&9# zSK_L=yb`s2A=HSx#%s_X>+A7L;}q*;j0r?`nzabDN9(gR?azsdiY`R3bRNX+$yCqd zhimWNX?bso&8+-gItQ*JH(`ew!Zzh-HP(Pgg+(-0)v!9MA6d&huc~!1tCetYk!b;* z?=>nVT9*zuLs28&Dwo;u3WJPU-|OvYDd zbnKmmBwf46gOIS6YTmGu93cT_^y5@=a*|RFNoSF=pl0U0iR1t{`@UN`HPl^^Ea0iBBVx-oJKv7DHodY^0@P+o|dKa4@d zHu=G@_M`or*X-A+^(5`aK}w_tyS!M*C8|_yrt!BttV^D{oWmmHqX)jw&13JA)3Mje zyyr%uF^HO*fj^4B-^-Zopv_gAa}jm{Xwu?*UEZRek_L}1_X}K(gb!~$RY^(I*&Os~ zdy%Hhf8WaUQ4(N+@``UiH8mblx-|UgQ_%QUzgQVV?UR z7}7%FHD0kM-bxcpY&z4ISqpPmT}xyK8R=+l0l|$F7~I(X*vNVe$R}RBG;p}Bw!q^T zKA{`N-+jbq@b&)5=U2zEpHn zGGt4*9giII+|Eu#L$Kdl)fnlli1O%fjLTB>)gtSvbyOAgX;4eSb=xbY z%c)9_DmagGX(KrGpn_A3B_C8)Qj!AG6BU$}qy>Nes^3$TO+y~lm-EIMCOUexd?a{Vc;YcwA!K`+#Z+ulbTu zsy*DV`r$%`QeuXh4&=}bd&Fgrzv>x{eDx+P~BwUN*2!O5>0WsS=$7bkpV3|?4a z%yxTNEMC$mE28EP*~%Egqp!q~3HjnSsqm`N3X7_(!0I(E#g~5ljoTVGQB_N0(i{MG!21AhXS*m<}(guWjT#Pbtn^+V21M!XSN*XTqeUi zbDU}FhV|TBlJ(p?W|_O@G`(4Ry2Dt883v{)(~&AiW{MmcNyZQvxiPfMD1G+lIP;s` zCrSXMrM;;CE&6>mZN+z!Xug!S7*rf3MsMqxPH;*XYC`31CpafG-GR7`4G*XC~zrZzdtz0LQnvgWcvMh+3 zYXgUn_K{$bxJ~5Nf9~S~lzDkYP&!u-;jkIp=dVsE@WQUA%SsM;iV9lSZ8E%B!Y-HAXTLb|6Q* z;fLc5IfPn*C~cyq;zG8lV+a~zwh0bZ%JOeiDUU@axtetb=*?@d*RaPga-S(0a6NTz zGg9kze(OD9K;h$u!ud)p6bXs#2?P3g`?t`s@OvO_{1tRAbUI{^VZxL5@tY`QpS8}s zB<~IDtd9sk7ZI9&*0+29PLQtbd#%RKc-b7gKTtwgx9XsV48Bsk_Wj~MlLIHCgUsSZ zN^#v`VbztE=2^VrsSs}14F`gzkHN#DRpP;%a7U1je;L?~ z$(v|-&LirW)@a2SK&*%Pj{Ly)@;wkb4cAVq$4VXU`5{CN0)iV@tNNI1{I9{0Iz#Dk z_gc^ML{sDfld^Sc57xv9&#U{O(kUlI+x!&#SW0>(yRCgQx&xAVoCGJ8Uy(wF9kd(U~?sJ`dCD-4YmCn)Rq zz2hV~ZxB%Y2nfqzIR-*WJ+@HGkvC&FP&dGG>MY43Rs32Sy z5GcXZ++@m*PL%SrfoSj$wWacmp{VTVz`Nb#1Dmw@yF>$6%l7gkY6}6C3|T*|pzz|& zt5if}J7Sd*JKQ`uXbLJjFA-99)DllU4GXWTliFRNuV#){m8!*T;DrRymt#l0q{n~$ zQt2uGdS4qrIpa{r9cufgi3szq_a%H8 z4ymb-kSIk2S5f8AN=nO2S1J=%tjuy6%S=*MmMJ64zm=uS23(@?Q{OEX5wKdYbL)$= zamROdoGb-TPdVNgJDYP;NQiK_2?PErW_L=qLB_U0%C;ZVCUt&0mX~PlUjYeYd>8NM zv-Wpz&inJc5AYn=-(g=DuR}^Gd(^+(Veox?7q9bOJlfX->`KC8-sc!)1qsrLL5O?~ z_Z6Kn-{(?A^#*Ior?04epW=;pjs!De*7&=>(1Ra5BS*R`cG!!kaa+;aAWmjGd4#B` zdrSr+(In`_wc*DjT%fjQGspt;a=mTXA&5R zNQdH5)j?7E-)UFrfp1y-!P%09!?g9sE`Cw3O?y*Wjy>K)%@cceo(yNGAg&qr6uuau zG>9C694H5CG)JUHQDBp{Bh!)ZB;~LRuF~X5AIh2MHU1W>8W}YPS32EVdaeMg*p5kf zM|tFlq@XTAfCwU<(~0Japa~1BsWUqc(XIE~t`jflT%z?TIWbdjUiGA2p1)8D`)A8( zDwJ2}%MWNz}*QwW^Qs|t~5O;inw0?`U!whOKB)MrZaPz76>HRscdsiB^9B0CSz3Q1TAjp$p zkEiP|BJ9Go3DY*tdz&}B&)WoQmz({(DBokh*Zw?@_H)i3?)z-mmBpK=F-XrY7u!X{ zy*|Sj8_ikw8CBh=(NInPY>e9WKya5lh_Sx)^wIik;m1l#b0yI+pdq1tW*NhUg%6o? z_>K_{XBqRv;X}|wX?VNaf}iN}r1q*~4_Su^xFf*nxZ?nt{1}(v`X+^9SeEhv${biOm$IP)p&+@`c<#R0z?O_?K zEr6VyF*OxY>lH)V2+CsvY_FASNIKiO)l{5ZBn@a=%m>ktZa2n@%pVXj#R!}oo^5eq z-KU7kZPc|z4~;y&mrlO(G9A~nl65}{189$pLFP|hsn6efPt!JT)Peo8%vCw_BWYAK z`@j*N%czX{gPV_QEUs`X5`0b6w#&oL$HA5z!;D#t!W!}ghwJ?A*J;A9FIbhoEHM4u zIU0W7ElN!At$@6*mO)0^?!8}69z$RwEqiux+glCgBpW`kPmWY3vj`tX`)u79-QjgPH8E!H6)6hDVn~v1a5lzTaTXy6+HBXm}GnHTY89`o%*<>Jf^H#eeM7h*7s_(>C0S9zXzgLK3dc)9~p; zsz&$KjorKJ`Z0H_UdyBFNY*Ucp%Itdr*%6Sskwxg7g1BsI6&dUCn$T^ITs0`u0sq_ zhebVLO1Gl=t({8ScbA-Lvn?{rGeJi=;F2QT4K>Z^6(XcD@`LTUEpqBG0wJdlE)h>T z0lTaM18bQ3c${igX`tw8wN2SomeX_!&ddzO9oVC|EP->Rpa+%PU19Up3H{a`xZ@y>HCSVt7%)> zobNF0)1|s{^!EU5Rb?zg>ECp5)IGB*B%TBpn zzpnn$NV@d=CeKih_PTrAMf46H60(<{2Zgp$SoN2zBuW(x>G6-sk^F;2h%a$q9S4tJv*nDct!LVIi5v6=DMcNcO^l!g_6TW`T{Do5Kcw8#0EZ znS4g9;iJa=i5r9)SgKq=qw)nP9n^tc$f#->tgBJb`vpYk#zAMP&ycbu=@di1JCkw^ z!|*+!vLGAGR$Nqy66?v1VNijo`M8Rx*tm^wA9)Iy+4N1@=IDnf$NRjtpMGgKy>`c) zsuo4{v%tONzIs9j!y_8#>|R6k+Cz8f{6|ZUq(Nl05PdT94fSn~$IrDe3-Wb?ClC;m z?Epexhz$`y78bFg^qd*%tes<6BvO%(k?`7)NE$Z+TO8SHmDykFjs5$nGaPfC*V#6Q zg=g%1`=&8mB?u_1tN@~&TBFdWUvcV{Tj>TREPDjJ<;QIY! zT`;J`RCtEk#y!EBn|PAKL_%~-9c3=RJ6Y7%3OtPp8iU zBGfmbawu_z!uXb`NDw>UU6jJH-_K_mIWsrKq$ci9u8qzF!9j@uP*(8=KnV{(z>K$K5X%0760y2Ahf8*xcQK~&Y* z6{x-!kfqjlppJDvqq`#4J2Y=woC31;TdFp)dLP}~|3r;?`XzPjL4WK!L`H?GXP4gk z?#C5|bhlb@(_X}d03cKlHyf@HNQ*dK+7OWs7OS&5l2Xlfu97otfr7*O7}0}yxGfXWF+D2!<_t4;RZ7@tB!iz^f&o!ao`5yiTohF|v6OsnLB5K?okR3$L5R>R; zYch$QFTyK#(39iFs9po>RGEU=;g&rbDt*B&{kf4&+PW7s43srDn~K0q#kg7Abl&7A z>m!5mp&Q0rAq=igk9aeScme}6wC-S}E*LskRSl8(5|DL|vwJCE@h(Hw`&7AFYu(tR zoz8vqMRn;`wNwN{z2hQPDY>3z%>UJtPKttpgS^R_GC{pTI*dXP3FMif{?Ud&AnKaW z1Z3}1X7UuvcU|86)|rX#7?OV3RDgU*8gl&17`|x;k+}kUbsw)Eu3d57o2(&E$eLJY zK2JH(!9lK&EQ?YI24J2ELm}ff;T#Q}=pUkPI8O)3wNT#N1HOarL*d&ZtxVgyuZWsf z2T|jHLs6e#jNZ*sGEHmt*Zoh7v`Df%fIPQd&m5$TMSJwu7Vf2WPiQ!FG*k+17!@P0 zvi753K!KDcb%HqyX72Gd1wuF}se2W@BSgJ@^;aA16Yu*%p*X=xfMTUc@lXPV6!)Sj z!KDOum*QGHI4x4#U5f-O6oNYiibL@Nfue;#Z{BK8=hE}E`Sju(e%!LcY;GctlKU!rJ~X{CAsEvyCHVBC6TcK>Iuta6=z=~_Z) zHpj4)-3Y;ef5&UnpDcK{AR+!Y1^!0e&>qO5rv~yuG~>;IF)aYV_Sk2_O}kgqG4Jyo z&W4_j2)^o!>-+JkA+7axM5c7+T8dX`q`9+)a7u$kK47MqwGhIh*}*6iJ;3_R=#g34 zW5urDA`@rG)wnaCeL5#EXNHBZUgl54aL@Jm%`PV-Lrg-vKQt1>coyfYGaMTKk?Zo4 zK{_0Hh!=6XT{5{g34j#$sV&0~L!mcLSL4@g!CKQ57gv@51Qkx-Uwbe2h{}PJJ2~`< z#K&)MWt8=kmcDQ^q~HfWo}bUx)s>ad4mD1VbJoh!r$(hiz15OrT-EGB@|>uot_iJp zretZlQceM=`_YFHwFlJa7bZc&6B^K^wdDl)8Nm4h>Xh}Tb$t<+<9EXq;+9{u^2D3| zcQtN|1sl-OKbbZ8z5;ohO)ZTAKDFxDxOM)_kBH;2?D?fjj%s8;p;wvhYS1j>;fh<^ zT|Ay4Wd{XdP(_%4Sp2m8wh@y+GnHi9PQpnSEBhKiw#R2_Q)1iK*>2dgbWOn`^5z?7 z;446m&o`=TBh|T3S}2>n)}$JX5M@C;3q>?yN&-{6oy#gE4vE*QeFFbUq@oGQZ*D6~ zL+*)NK{mM7*&w5s@veoD%j_<}z-*v(6l`t7S7by*P3VZ!zIG^C>mW>0(WRk~b?O;(c(Nt%y zThc*fU)WuTRi8nR;zaI{)=Hin|6GWk^-X9Db@Z*^pGOcm!$M|-HHZm zaI0bDcT<_oQ`wn*u_j*XJmELUa!EU>l~Z=;au7?Rd*fRvYq4e@;_Iq_pN*g*Pw(Xx z#bXvn-VGf1cO2CsR;S3a&*w#ETC`>)FtFTP7If+rIpaS4Gi&?=@~ z0!d%$&KWXd@CcW{>O*;^=Lc+0iMf0eZ(aZ#6j@VC$TM0~JA2K;_n^a$7GGXE9PuK4 zLxu$xR`b3$m^enc*R(t`7HIlBi}*nmcQ}QbEGD4q`iN)b!t;2L zA27Wt04zOyQ!ED}1zqC+b$7LTGL)Osaki~Ly)*R+)L-{9JQOKG-D|$+h%#zeP(mCD zm!N}{56(0*!aaFlf|{jbOAx}3LyA_KPyJGb(Pyci6q~AryC)PA)^&*1e+=sMJLX>_ zHPb9U`|cSWYWFzTt`?C3>z3rSd-9N{8wuK+T9E*vAie0wTX?dnwKcN)U6=7J=b~6@ zf|KX|vb0x`GOus+9j*d`OB$;E=l%4_k*#)=_Cf?t!)Tq72`&Ky?3@1f%D8S$VnH8L zMg7aT;S$W6`=h6(kU=X1R$Q_6it` z>sP|o=n|0DVke9bZ6@sZrXzuf=&IVVXgcY0#4r;*rTw7h+=p<{>BUNb4uX8vzSDC{ zI>pIB5n1)t^#+dcCamY{S_W+e;J=Vk`PDFz3h*F_^ z;)Rbt2XaY~j2K?c$J;5ZJ#KR_2D}4W^d);V4;x41d_)ct9jGpOVbMTqw(M_5Mpn$I zFFZ?5+Kuh}6^Aq5Byc%vb&F-Y!G9RRk~zci;B74g;paRDlwozsC?he7#R*Fsn!(EF zu%r)=PY11<4_o!d;X4D24mwxP^7(?;Z^g*0=ASKjg#%cOS=7XR4+pnMgYCC)UG$>c zAdSvc83-c0Td8PJO7dKmvGK89TRxRqYH} zXK~shaeP>}rUBg}$2POO;;tvZ&U~YN3qe~Q=PAO$ML5b0vH!lK1dCEKgC!(JVm33B zp0P9O7t+3CC{xq)dXX1W-qbYy*FB4P67xLJGoNWHiEzQ9d7z&0$8H6)qu}il+T)8I zpNp6`w8EOtVKyv4YYvI`VkS##fjUb9u4?;qnpQqadQt64W|3ENSYZ*+kTaOc^|a59 z)%IaCapGsH_4EGz5=OS;F6>Tqm-B!!qAr@=RqtTun8xm+<)*GWcGK0;{^3^YVuEzc zKkePr&!=kV=Wc}GI@qL+o@MYstPh(Wn}QzoW?E1cz-5SrBn;0Lp*&1G_8oFuMk}f} zvFB`#D>P{OBK58O(v_OV$7{t-6h0^WWUxCQ)Dh6`B=0)KRZ8WPAod6#gy@@KG=r+u zFil2YR-@dcdUjy%ih<^Qp)2Aanc>T9v@U%dbqsQLvw-8P1Gu$-Yr++#0R&N`Pr0fS zxd36`x+3VzD>|jC-mjCgJLU1x2c27M!^BvVDW#n_!c3W}E|8jgc%1!{kUlYTLyv4A z%IE&8gtb5zYve&L z6F}aoq0Ox3(N9A^yy10dH43vD*+UaLXdp>;H1(S$d$0`c~Jbpw|8 zqD%Ji1_l~@hN3E(60WVg6a6*1-SxY9&yQ6S&dHP-gQ*rKRf2bTCAlClaH?60X8Qgk z4*HtgKB)q&f=_(AAF%#W!Q1}Cs$*-P!<+6P_D zHUCe$_q|;L%=!12tCPp`Gu8rHn|~w#BsA-*BN#6Ha^`pq+avGybUD7Y3W6^lfBKa! zx(21B_K0VNUrUyh%?jW2GS&W!X-%iT;m@u=7EyVu($U}3U(8ah&MOk3X6optOfCA{ z`L5WvXMGhKdkTi)IzNkuuPlAL!6~PcdYeQ@5LeBZAbj6#UU<@dX04vxSZBQAI~#d1 zu(F9&*#TR=rSyH8UD2@(OwlR5GaXPAm%>QRO{HV;Vjb=X9he zPiTVil9#*i3x4m%m6`=h?Wl69EEn?$9hP8NomSESqE0k;q%t14U;j~Fs{I>O@Ro>+ zv=Dzq%|CiHY1H)h$Z71GE#3J8!{!qQ6R+bEKmQ9$_D>CAx0nxhOI^VU3e?W7IV8jt zFKW^M~GV>yk`}r9Hl=iHYirp$1Txout zOGke{(SpLuc<8i5+}U@g&#e)|)WqqcvxrE;?|6Pqf2(EorS08bBDDTC zv3{|}Sf|flVS)D-l@atJ&N`VGV?9@?9w~V@&@3v@v?=`2j(1hRvVr=VZIH~u042RP z$*irL`(KgEn;n>B25q55^uScCN<29N)Za+Yu8ggblK7;v4!&nxd7YbEgll{Dw(s8} zI_I9>)&nT@$*NL1Jn3hiyg`Nh2GVnBoUx>rk#X^S7%>-C$gljh+`FjVzX6UBzbKfy zT{}jPlOGF;T6fHk&^p{e54qF}@z*R7?&kA!e`$Sj7PS$b$d;5OwwDSFE$Z{flcv?I z5=sXhgjH#N(KlQMf^e_tuaIWh8)!I({9`YP!PZp=8&L z6l|gCI}Yt3yCx+aF1o^SQD|)>QT^ofry$uh&qXGsr&lm9U}~@!V4-i?*TY1yf0+N4 z7Kg(E<>uOhNrV_cat5c%_wx75N(TR7%(F&p1-#94GwWGo!}RqM{1l{&5Y#hj;%#r5 z<^zgH(H+u(;t5ktqJb6TMk=NgkXZ7_{GOmj={>Se-C5m0D<|S~n_=D@LWr*5Zlv@+ z2gV+`4X?6C`-N6ogCf1;fICTr=jmt;hkL`@^Vm3pU`z!s=Y4loP)9F%TNEp|y7GIi z5P{FTf2y0spT1`!Oc?4?E%a=ht?$5tfJDJ4k@0W@SXdDyPA)4aO9KEFawkj7@gBY+ zJzRm>m;}d&UshC&1ZcA+r5w!(2m}-f1m{%HIbgx}UM1=4U#rv{XMipw11^tmmt&XK zL*6jW5uSh{T}{Vy!(aXBJEnoYUuzhB!ot|RzJ)kktdLJ;A;12J{ZK3)b$k7^=L4O=`9)=-J}E0W4O3nQmuow6R~KZui2n12H)H&dbVsoMhk!^l?5My z6m0FHzWpykM38IhHd{vKQm5vP1Grk(h_tY9t}w{N%YI$W%Mh$>W?R1{>NMS(16pd} z(LHZHn(y(q@MQ6=5}KLkfs7tVxDl|}J!cqRI3|H737^mpElUmmgb2?3G;R7q#^_CJ zfs4qo7(@OoC@83eeW_Xty0J2Gb#T$p;R?9_%89(Hq1{$qx8zRdq3dCI4W|>Ws@4yX z%_(BKjk%SI$JWFUxk+k8VqxNfijAh(^$J01Wn8AExYZcgqFS|a2J2Ph|CyAN$v=L++GACipK!GnAnpe() zKQf|}!(0v$ zXnGOC{5EB>yn6fW+(F7=m<-*Y=kC&yDdNn%ztk*iqHjMVQ|N1v#?VpZm} zmpkJGk!@ZOWAXvJRhxwX+1Ty}n*zz6dqhz#jGy#qH8~%Y7hH5z2fZ(Rc&xXf^JiC-#f)%X7Q_aOC)? z25bL)UxEoO_5M}iv;VD!gL|3Ig=YN+kieO22na^lmln9j8+};NIuvGk zp4g-KiS$&*Ln_nQ8D1UL07cO=H0{3V>+7Qi0BdEsCKkZ7#swbEuR~{dh=iL!3yBL< z?|QgxP-Jn%jG1H({d@RczZot15kDVTo~I$B&zEexCN<}1aA`i2J1s^a3YuA*(nLsq zh!A9XTzTJNo76B9AIyl`{?qnSi#Ms!>2ni}F#3F9yAkg?!C%FOM66X*F=J2%^$Lxx z&q{xN=#F$2L(7tOoqKvD=#4V9S)$f%KIYz>NLUqDwAYMpBlglF>bHL#ef z)`~<&y=`p(<@#Z}+`dxYtlaR+E2Sb$359QYU247aRaTFGql|+mEV7UnY}m8*Cm`7w z)BNs+Lp<yvkJZ5)2-ezY*U07r1=ImQAa8vaO$A2GE*3ZH`BW)Xn`B=>)GTm zP|1@ZS?#X$#bri!;o`8Om)x{q>GDsTOR`1@pZx`uVf&?x+*n6ijuq^pAg;pp zeGk0_-m__iHptOyp|*7vyS%3I|HO!qKUPu*t*JCP4I_>qV8(pkqyJ?Uzwp`&L~@xF z+zVI#-W;T7vR07p_s+irdF5g|*9w22tSlu|nTb@N5p~g+7uA;39eZex{D&%iN0@tw zkaG`*Gov}zHa_jq_KNiuAW!(Ylcw1xqVQYnPkqxYk*uRIpU-Hw2NvFEX9lbLuK zIm+`%Zz;PT7sr7csc7@P3Z{PiYwo)WCpoN@SVIqXX{Hd-;fM3^>6!xYc~kJCa|Gyx zu0<<{?Vq$v!}Cd@nwTFYRuDZN@TH4fK;-9_jJhPjOw!#$_WoaZ9*u|b!-V^t1YBQ$ zQ}BZdYk{X!UzlkKVZ1|&91dH1Dgq%$4(2hywa410xccz0Fy1DWGy< zb+ldceKpOZp-Z73m&odwOwj5Yn!0cO3g5mYr|(PnG)rNl%s>20{7Lou8vcvOKuR2r zyJr-PyldtZG@v{0YROtMkWK3h~$vI59NaxksQ}& zv*;9EZzDhCVhv1c+?kzF*!UA(kHm@o5m^(PN&VTL#N67dq$_`f!xkp|NWGZ?E#NhO z@jsT)`C*&*y#h|0r_W}Yh_V6XGu!6J@taTJPtNL#O>pXCGL-_xExW_he91tmt@svD znn%Z78I^N7&7v526VL{yy%e|5k(Qb)GF1-*k#!>RZ!iT_;|gR@vd z1W3*nfO{++ur{a8DB((8=$;3lhY{;_Spay57CB6_(^%g8-pa!(+3o@qS3zySy*0{4!nak2Oixut-}bAS{nZeNRDbzuankut3X&Xk6X zS$5zAhxPJbV`o~}pZ{lWPvi1&3gbaWcG!;C%ge89QpE>5*R#u^Uz2KTs-19JNO4+M z=~~WAvol7)RR6Qlr@!)rWIJ1A)~<@ww}H={@|t)A5x9jD4lSzvK@)6fzE72Zl>evE zo4UT*ViC~tHfble5X7U;*A3eYnlHZnH&04p$1vvf8@bdQGVH4MfA2;>*fAHy9CQPn z*u=^mOG|Vo!wPN_OH1>(iCH4Y(uzJ~9}l|0(srH(6<%Q(V7T`FGh8f9Nh}I$M*lG# g5&Qql%2@7J%D#)R2V)X#`VaO}QP7aDl{E|dKa5ZyL;wH) diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear1.png b/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear1.png deleted file mode 100644 index b239cc561c5cfccc67f04badfa8ab1b54ef6852f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40613 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�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 diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear3.png b/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear3.png deleted file mode 100644 index 31e0b91e9fdcb82988b6b8d7b2505a38fdf70358..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34541 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%0Dr63wml-6F*tONlh7MSGocvsq)+w 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 L&GqVZUE=-^HyYM< diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear5.png b/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear5.png deleted file mode 100644 index 417af6c61166c727b9ab50cdd14a498c68744e0d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 77056 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 NziqdsJ&+yE=yzftZ#5;QXZo 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 S-AwW`#1E{fu22Os|L{NMEB7e? diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear6.png b/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear6.png deleted file mode 100644 index 5cec7a105d569aa2f9d4b37f709496b29eb96e2a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 78392 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

    3s~@flweDA35(#PV^FB#0ww8-D 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

    qC|Je@A{CPaWgAH#&us)?C)p`xW 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Ɛ$)7cJ87~{#3|_#o2xOh1UuK?bBmXyMpDjq6-3zT?WMe9&2jV@xa8^EsbHy zdl6^hx6-&8qE#u`q61vqt0>R4Nq)Go$B==U$1p6l?&G`I;1#EU-je=&|r1vn!!= zlQ^hUL$gip`cl;=5t2h_b$e~f;F`DIJ#6b?6Ex}614?7*)Kn!QEfAE3`&)1p=`63+NQ(%@}w+G>B=SG&&j-GaPj_uW zc!)kTO+Y(G?}Q~0n9aZmdhA@QGw}=NMl57lA}*@uVvJ>_itfdo_;@5G8i!#XNv)O@ z(5|@f>+SS)7-EE>WnzG}2)t`rF4n=ZEPQKoh32f`S6DdnOT@=)Ug|21A8gOhqQh9C-Je`YTQom73jy~Za|ME4< zE$U%&ndMgHOjR~hl5WHCUj8l~bw6#M>;tzV`htqKMoiLqT>i(fmdj%dCkH;+x+5Ij zYAV}t(I;_b1MQ%7;TTWndUcH#D{-Ni&<&*LqGm`aBqiKN zz%k1%HkOp|ZTB>M%bGG!&Yd=u!p|+?3H*9`TwRO8yQXP1y@$acx9;1ONQl#Cy>XIS zyY|DZ8n@?XdA1k`?-HgSe^BHRoIIPg$Hx#zjr#S?#$A$d^G@ERo?1#wi{9u~qpCP| zR+#>5WinD@@W;Ak2s18i_xRFwd|&vMsG;9TPr<57SCkAZYva|h1&Vs=u_t{eAc~&&&+a9j&CNJ#Dy{wm*&TFc*9D)+1_46%kjWc(TV9O6# zFNa*%saU}tT`QO>Uj#<@iJrLit$vmEjZwvzj+KsouHVAlpu8E_I=A-*R$jPZ%=m0s zzBcOiX{(=gFFY1&EbK=CTQ%cPO!?p|Y&pFViHVsKZ1{&+`k2C^E?vx_02&v*X1zH6 z{|VU@7fvKm`bcSMsW=vF*`PitSsbWSrXgHC^jTb-t$+MC>(tFk(V09KFHa}9SukXB z{!%KgMViE|O&KpZ=25?-wvP{--AxM|`6(r8vZi9)QYhkX8g~hdEtncl=EOJZguyk- z3azx#h^iIgVrvV2`&o1x@~C@lBXCf=x=&f7SF6+B#l5S>exKD~#G#jODcITT=c(O` zx`DmlT0c9DV=Z$(n1{IN!^$>8egnJrR~GlO)~x%uSRLjLn}PQ?Y{Z!hYmt z2^aA@Di$@3UdALB-1_?Fcs3c1@|8@hCE5~Kf)3%Am%mnesgE;7E7ydJo9NP$j70cf zHrBwhDn2dyqKGjxb_GYzAF^mYr|htQ?FIaEmCK;(ZFoc9`e;9)yRxvKHD=wP+2x0+ z1190qJ$n&wa|3i{olyZh%bdr;+-G&7B$bE4Lfa>WC5L2n>UfLt;H!iBR03mkHzp$uN8(>^;YV)*&3_` zH|M|s?D<2V0rXv1!1oWC zVcjr#EHV^k1uPX8rXy2aSYq1fYQ)F*A@HL9qOgP_!p*MTX#k3qY;M?-j?L#!<$C#J zJB{fo)alaUkcGI!ieGXw)Ho*;B|SXhSgomH%lc%LsH%vbN@_M%nDlXXHD;SBi(*~Z zugF)>q;+`}HLK|7Ti6=W;d}AXH;WM!#>@8cq+%U!6lcD$fq>wEJQ;1~a^?M-v@lkk zhzQ+_l}nZ($x4TI<;>;S=Wi0L_SHI|&(zL_t(+4Wpv5NxX5h77mcsA)-{RiUM_9Be z#xG90#uQ^_5Kx)fYg2`V9?Rn$Tg?Y7aXNXB&BcXEuXr!1sS!979Bgq2f<2M8BU++W zIkhJw&SvCEc<##G`V{72u@qs%b-XklL0AM36l*G$RMts&Qf8lFZ8AKIXA&0=m#h;G zR9wY-9h^*4k&}b+b|pefNh(~VWu!-9+fjY`VNS6#`x!b-1`^^Q?HOprO&FDA z5|?sCO_?`&LcnRHn@w*n>8`KYWC`x#yE!$i+{ke%YO;mq7K??VDH=%8s|)*ZyGHQ z+;biaJr+4h(#lvWF0|Gh)aMCc-na2G4h^}5lPfJBci{kC(-)sbrLy`-VB}JWAKWhJ zGevTOz1h!56?a(3HC`_dM<;$ws`8WcR48VpDZdZFF6=Ur&Eg0?o|bpbJW7^=laon| zq7p4jpvVtc)UsilVes;{&B6VoxHFi!W(~G1+H2VJ_(0?AVm2tDVHYcxV-$~~(R2OL zsVbMw(EpKBxA$S;OY@XWubi;sw^R7#kd$x@!8eUFTrHwr19uFqTFyB8SDSDF0S8tp&vSyilbhk0aIX>*5q|5@ zX_@Td*|F%Efs{Nhnv@zBBt%$$j5qb$sv4@L`DYrVFt0>tJ@r^oSO#SN6 zI9UFh@{{O@J$QBY_c*f6`Xy2d5?;ePv&*(*ky1+7Ij>+k@UZby;Zd}uvdyUkUu{~2 zgFCXHU=k9Vjv2GQRm7h-xzxq1d0)cYv?992?Emu^2DO`jsVkP^>h-nad+9y<3^>8D z=tQ`b`VAUKeQmNdoqpjl1FE+||BD_AZ!9S+R>abN0uMY-08a+QWN0J1D~9 zf{esLT&T^SeI6yf)lwoE*x~P=<0X)bxtPQyDKQc;fsanvRcaNbnSOe3+@r&2xgsMS zTkZrab1;ZU(S~S0Vu3y3^3b~Ff}nd_FlE|@ zICE0pOMVhlL2J;#bI)9lMM!>X17iU=(h8$${_ zkaC>G%2+BcJn%dT9CzVh1zkzS%5i|qFCr2rcCv7&<+SNl8GTsO!SEF#!;eGruVpjW z@5kG$lASgw1WDnKt^ra^+&uPX-Mo;PysH_KeryfS2Qjp(r*5f=sMEnz?Kn%s_jGqR z-5^PM6ej1J+dpratHYyddW)n`TmMafBxE#}D z)nHCO`grT(4^ir9wdx=uoTLe{pK0fJv&`vt$B+9eDJ?Yah? zYxcs6UyROj`hd#a^8t9j@7w4y{5AY~@;K)+=}Q237IOm z?!u)Um6i8lSIYq1;;4v6Pf{!@dSy))g)ys|O5 zJtKqBbdwO8i=r(N(d9)vTc@JUhR2xYbokm|m^^ke_G}I;^n?6CR|14VHwdN18C9xP zM%4wdLQ&e=#_9HF6T=ztYwWWr@RoK@v}cevuZ+M4Cdp=^}q1!Q!gPZ^5GEx zj?Jw2`Vc<-XDw0^^=tB%Ez=FJ&UzoEEXbz`x^@q<2EL0*U6MwI|Cmh*OCFI^aiQnJS*^<1O-i^9|J#?8NlV$Gn|(PjEZrJc=DyZP>D$vL(&0&U9S5`{?i*!(x?7n97z1LT2?0Yz>Fr)6t}s?S{vch3fp*VbYY>ux;6eY+tXo z#Px%xh4-Q_y6E8O3y;#?O2y>7hM~77>O150*WZLsnWyzTg%`5(@+u7M@FH$p{cpkv zo$X&f_y^Nwzl*4FeeE~*qRsGD-`;4^{?SX3Uq2s#@txj6^N!Esv+X}3=hqOzyyq2u{a&IP{v@S7$*;{m)N`J%`9sYn10! zBUOjBTYpAmh(6VlUdyPuRZ+iDHry70E-f9gv8FmmUhdgBW%&!kW1U;|ftR=KhR0|} z0)jVV!i*VM{@ErZXE7(r7NHmL_qGGdpgM7OErpV-e34IZ%;xD~wJ__&3GnvrqU>V_ z>iCVnFlfL;EFLo(CyqoRT69BC9*f6^gFeFR-+zF_czp$O3d^LX_0fC!2<2Y9AI?qs zd(jcJt2Y77J9NXJM;9U{YM&xvDqQ~9!F{msR{j>=%4o6Y(f370hN*)lpdDl5*{5C0 zez-)ECE2l5RCk^XW>=`8F=IF7X4q{US&{wuDo?-A0hP>3(jO1telCHH}r#+qnfbAfTd z-LrTb3~W^aPfebV;`(}+3KUHL+d8~H?R9Xfg108Ug32|rKS3l$suvz!T`hgVBfsM1GS(<3io&;RhH#i&`WU7_+|PV5a90%ItFDJ|a&xu{y8GG2A(f!lulSn*o@-}ipT<~>#zjm<_ z;!_Ye|A1zL(Pu*TS8$|IA6LeY^onioSOV^DmaVc`kfb}hqGhj%@G6nB^>NbEbQn>s z1m5T~3LR??7apdmqI6!sLaq7((Y|d7cxcn%R<0_(nK%or8w^9UM)mRS zfPS&uXw>qcfq<5WX&p}v3>_WwH?06$yHt5J>!n{KsUWFP$rWGxGaGYXpN7fpTch8A z?&v?TANuz1h;C2UK&=`YgoJ9a{d5AxO@9u*?wN%c?JK6X@U>Q~tUocvWen?=cxT9(V_lSyaU>SGfq@nEWz4i!y|x=kDEq z@a@X^h&{bATUT7%sZIE5>3g^rvemGMa|62I``>1xbPhun`?dz*Ubub`lyHHlg!)e! zwrrocp08kFeQ|8FX&9JJ9+5@|Sx-#!-e~{MQcP$!2F`BQFO=EDw=$YPVcTO_BuN~S zcgB8#_;@ZoA`Yv)7y8w%4xdLST;&_=K=ql}22+3j0d4H`-sZg!4GtZN!^VwAad`hq zga&REf|7YwKj){|yR?E+vA5vrvr!1jayS;9WSm)V4boDLD&Df7=(FY!GrioK=v8S+p?mENP z!|5`jF+LdaoD%0(iHg{bFPDFY&GRNAKGZar{vdR5u?YC<6MX*1hlr0g)kpIx+73T2 znvJ@REU$oU4#OYceEf+JS{`CyNHitUyIKvUo>yVQ=h=@?r-~tE)nd|d;l4q3{%-qc zKfts>FTJsAS{OMuzTIF89OMQ@?Uc*;lM+BQzQ{?tvLs`3<84 zcW%Vu_o+)Tsn-O2b?BrJlp{EDz#u3o_mzp!IK$qx1Ds2I0=Kd|;9PPZ96ScW&ak?s zb;&?_n$b1o!K1LSLrg=(razz(t78Ab2OlU;KK$@Q<N$)37 zYxvtpb2UvEEexbPyP}?FQCzqY2yK$(b*P+*)Ii<3t&!%Exu~eNQav;&TN(Co3Ah^( zA;gzXbk1p5p#xs|czmYJC0isgBm;v641`W+s)W(I?$daFMn9{pFP~i~cF|F(*thH; zw#-|N4?p=8pD+3qYY+d8Yr)5m5O)I^Y4;Dxy9z;hN}SvAaQ2!32e%%=JER{| z^Q@-;^C)m)-G{Tj#)Uh~^fC@5S&W@SQ8cgE9bf&r0F4^v?uHrO=2dJwYbuBHOtVoa z{^C5tR^h{a?JRt=^G~E|GsV6iN=ctC7&~M#;-7eM#btW6aqB<8inG^{5Tg$hWfvxM zeBS?MO#Jy}!yeltG6v|;dLk~~Hf2#dI9I~&KWs;@f%!APh4oTzUx~u$b$bwU`2vpJ zy^GTki3p9ni=;SlzZq6_{SS6-LQvYm!L<|YU0RFnTrAd-L(-CuAS!@$#uPYSOasF( z1M`!aR+_sWisXUhBrHZdO!cSrRYPNp9T{U`8*kjiwQG+a?|2Z@w+SD-4p)yR%J!K% z{_xtdP^w|s-3%40^;Ng7kF*p`?zp>m42sz5^mK| zzP8UJlUA||L$Ms!;pE4Fz%=Y!?T25d{(|W}-o(?*$D-SS33%zd*_ggzA=aH-f!qG; zkraFKzrteYA-t0Aa4zw+KIB#W6D0&z5W+&zbzH&N2)z_F1zCo~@{pW(E`)Dp@*{^5 zIdaD}f+vW^$tDgiuD)3C($^UM#Za-CJy_iC`KR8%>XVC=?KX`Dqw)B8NOQJqP?+n& zEc|Jvu{W!+_ra2{mt*jF%a<|cO?K);JpP)!6#tw&iqJ&SJBz}FB0G1udDcSl zqQ2->!56(>oP-YjDk}n+8*W}pK);^jaVzK_!yce{?Llbzdd`Y^+7d1icd_C78MyAB z^;j4y?@sSC0TJD&81_7z65GGWcK_2jc4CK;Qf>(x41c%FD7>-yqg>9bnQ z7&irnlx2GF^Q7%uQLI=ClyLEci-VJB4sdaFgqxESJYAjP;pzfUPdB)`xglM=uCsGQ zs-1%ngE%C_#UeQ|0m;cpNKVlrIW+|_$;pUMNk*bB8QSzDq^2eyEj3=W7|V{S@K7Aw zItme?hr&>HKGdSIkfiW8kq~9PpV(vhPBa<_&O~KsEc-oSNtWbPTnMioP7AIm8pmAP ziZ)y{C5AQT$#M8@#}7G5!QHt&03%*}4vBFm;p|Z#?{w~m(B7{bUd@`AA6~;B$Nn(h zknZlyas1>~lrNXP;IvNoXZON{?|dWxH`cAj?a%-?YV6=(uYsGR9g27qM~fH6pkZqt zMQw8n|K8XRFX8Xge~Qmz$BJG{UA)tE5JG!RQ??5`MUSk*cWV|SF6xkB&x7HB{d+xL zMdZNu40|4p=cyeyc6=rFoZM-VG4%26fnOJVh1OlNHyJN4;jI{#0PI_K80WVCgQcgh zA~cMt{kGY;R!XqAwo<$ldzW^?h!l)xk`?iR{e+k*6#^J>VRkFmRp-L}qQ|IAr_kSQ z604FMabZY}Q_eXHkvW4*w_@;GaWTr4?}R@VFGbs@vw!pIM6d1nr>0=->0iWF1{&8J zgeNC_t<1!?1gF>t>|FRdF8GNapW()=S78V?9a@s>fm_4j%9Gyix6gi!4>tddv=mc@ zTEj*|(Baunl)7Dy3+{i;#GJi<8P~XZ7(<$lLCJ~V8n&{=%~&oohqm zB${I#Xq=ybohEy=@(O~O+by9>QIZQNE{rd-dM0a3F*||>nc*>smC22`@E@?NQc^V5 zQQ<%d<6x|z@8IaGt_beTNu!! zrQ-Vcld$!i5Pwq{^#9PZ+AuVJXOUscGCcjy;q;Dq*l}#PGGFRJlqlT^KY#HVIt;Sj zJcAXM9aO+VQqkpKCYIg|Ybz|c z2VTMIrJut8_6EbA|I)bfP_%sa2g8=-c-*;!gX`zu;F-U~)_wD3#3lQDtC9G6%iKq% zsArcL(J?-GJ_BL5BM^5t0C)XwBPci+;gJ!Dj1ECmLKLEsqLGlI7m@$e&Uye04ca@^ z6JA1P*f~@bk6bUOl6cO0pb#4R0w7&?6_Nh^4O`0C3VSOw2uvwuh=2pBQe5(YoXm4E zhbM)B1$KiN4r9=Xo{N$alps!J)d)K@>TTg%6*E|##nrwnHXpnIoi+&3aY=Y8Jp##f z^(DS7A)!`Vw2nB96ZevkrVCf*L|h5Igq`cJqDI;Js8lNli*{QP>iX*U{=^%<{(-QN z?S{Q3BEqiXc!&;>SC6A{;88eultxN18<~!sOLRp_qD4kLPTq-xj>h%@G%Vi~UM;yO zRrW|J>W!-9YT(kTt4P*nE-zA~cq_d6(#xpX)Kus+x9~hJ9{ny}|MqLF`Rh|`-Ln{n z&#uLVn>%qc_#lF#&LS#a|6TMm#UC+#EIbL$R%iJW=i=|e(PJ>|UE9Ilxq%Rtio(-i zU#&`xiyUvsBdl?i^5ig2oUzM$qA{++*d}75FJRM^ z<48`*SxB?na}Ck6I&%f=l=%_AUkifA*}eJ~EhK5kER1T}1kUb;`b+64xO`_FhQB-o zi{JhmDFt+C(KGvf+7i6>`*K8tK5P}wbR@-{!R0G|;j_?lkXwD<*PF z?kBgNi;C*Ka5RIB<1b3kAyQWim7-lxuXFZmLNQY3TeT5ZuRjc3N{|q%yD00Rh5Hjd z#EX{cBFlz2)$V{+aeg>2zDG*3eh^ukdT=@ZbapX({-O(X7IY#t~4e8GriAadMj#WDQ6VISgSAF-OLA|y}1!L73plVNZwIvGyIUKS$KQ+O&Z^)uBq41MM( zJS}+$+L)!#X^oX3IDpRyWDJMVg=D8#4(8_()-b2E2y{1DMu8`cdE|^|D&9+I@O~UV zll@A`D$UxKLjQ*C#4~ZzWh7wprQ0aJO+SO*QaDE@Y4{wx-nKow%XBsD6<;vr29_TB z8KWmn#_QduVe^ki^3;=IcF>;XNAPU77cgVvAEKvgs-Da>UnY+F5nFctj5j~-iT!7n zVata7=+fW?xVkk};_rIWkr;atC$6r+XB!q^^{3;Z`F#f5?&-_c6gF7irCINn;p>|@ z-09#{T6i?sD|q=JQgx<`tLEjaqxpHEK3Rs%!Sb1{{NFt4YS47$-!oSBJmTZ8;i&&< zTs(6Q#jhNNueT3UymDUCtRP4!=7W}q$FUp!%Gen3C{hnCYc)h-wdTrpPH;T64@ZwN zIY>NrtBL7@N1|a@>z1cyXXN9Lm*Q5azG$hFTWz?Bzm=lZr;}%h4v}LK=D6gOTU_YT zAuVZ_IB@Z>v(H^=8!l~}6ggi!DjLH7LB$R*dmWk9K9*P)Yl{m*VI0h7bw$P= z_^64E2}H#VdvqMm@?+V@%S(gwn`zj0LEOL@$q0*zMoV27QX6Eq+Afe(=?OHb-3X;3 z)bNunp6;oyWcafxz`Z$7e` zfT}yG!nx)+idOh=OgZH;EEf*m!KT@NV)kdhVAg^^@b86nNJ-+gt;fZq0mii+g1Kwv zqG1bfxKf($gF?gK|74%$7(RM1>bllMREPt@buPkyg(w2aaFRAD09QlK;`mA7eVsar zYOZcbEnCI#DYlQcazj)Nyo&RIm&H~pe9JdMo!YISEmu?jh8(cxhYt`M!_Na6RC)>@ z{PYf-ova&nVu_UR|5`52g+AQcx_U?S>N^pqFI^DVR;aj0V#P(`ri8>K4<{q_E>h!u zg*M`S(V26FgGXPnH+O=Zp}UO4C}Y_idM-yqV`z*^yRvup@H}`)uE}jGvI_q(M_-vO z#LD(D@v}8{;@AlTpTR>Q%zSzp zZUp(mKl&bGlLCdObxZ8MFV-eLuV#f#nDOFxbequjfuiy#;zfV!*rrR^vg9YMx^^8A zq5J>yH@E^|rBbahpzkOoH8+I}Mun-1n0VuUZFoFs45JCVoD=R5|`Go((Z=$Sa6$JrMT){DAK_%`;4h z)xfF5hj4JUen_rxlPbKFeMpV}3u)Tp;xIYGsn|P8wPb6M8ovh7!7~k8%0NEj=`0Pw z9tTqt^&tRBo?tC;Aq;^rBt~DB-A%6R%e$F^Q0F zo>vBZdo=5hs^h;$x_!>GitL0KIPLrmtNz`B;HYy*Pcy7np9kd~`F*#s;;ZlR=Y>m%jyfoe96M+x>>VqjYr}!4{NnqDJ+_T|@O3QydMXCB zDh;>(mRH2SpCg}+!>OBVl;_nd^~2Wfi&5Iwx^tWU{mTy{CQm{_ETb)U=<>u6RDZ=Z zFDGuv+gNsNg)%XcV`eTTzEUa#|G%SnDGBSLi{B_V^pzCsotwb1*c;F|<}7snAheMm zA}+$XqAkNqUIw)?yil!0 z&a!o?HY$tT+b`f;;02|p7!aeu(`k`NsF{n^8SOMktJ(;)x(r79#+^|;#Tf~9zR=ng zL#j@=2oEbIo*jgnU%YgC3~kT~)1DuT39~1lM9JJ|cyTU}Z{=d>GNKE5ckB$!jdb|M zI3YPXP#hn9ry(Of2G@g*;O5>VsP|NVA@f$8IhY6NJd5K=4;K{E6hUJ3?9Y!Po+q|p z-QH!QqYx!duQ#T(e;S=e!PS6m%C>v478uaw86=lC&H8tJqCH%1 zUB>P3>!KHziqxdtN_gyHsn?G;DZ)$n7rN-#NQwCd=_#zfk_0>Zif}GI6OKh+63?wW z?<^%|A#^Fm1qbP=Ff7K@F~-7WrdS?hP0xkUd*O6^jwsW6;b<)DjVUAV&d#0~(q<5r zY+4KlC%!fN?B8}z$?ggb(cjb+tMtX-{x2c1PRjb5GNilMn@nZDGjm7 zS|ldKLz^5egojmQiS1l%qi#rl61;o4P0ojZ*iTuROtmqyOQ!-b7>-A$w>u19M8 zCh_w*XVHM@yg3z}00)l&Vk2j^;L&6t_D&0=Xq8lcy7M#`n?++<9|u0^GG%u2C051S z;=&K%2m-qxTyyw83yGEVX@@z32VumwFL5(Rd^7w@e6aZ^q^AanzvznIO?#o@^9zvf zkj=r1Y@$ik!Z|ub^m{_!m=FQ`gfM6lBax99gA4I@5R#$6`3tuZA0>>s_+H%`jKb{I zU!t7os9NRdW`nrsB+MN775+SPK>vLNo}R5R^4rWMW*#@(uAapF1+PGxa6ugOBADLg zIlQy>GsCOa;q0Cp7&G-FTw^JGLFuwjW7L3&h;R5{@2J#V}w_k{jyM&l93vKm^(d@nx^On z|BoxJaRxT4B6Eg*HfFm@a)PzRV9r^p(!GNQu|VbP((2*2})s5=HsJz z?b@MJ?E#qg_m?Q2O<`fLI8b=<0l6Id@hs+w8pWMZrc7DG9-vC8$3Gl%Y3na~VG)$_ zX@#y6jVlmZ1umww;_ErM;jgb($7F%A4eC5pSV&l}*D1n zmHAk|hcQl;Cf0W$Qd4pE>M<;PV-7cSgl~CgygXzmJiPQ}JQ$i-a_%xbPd%)RO5Wpe zc@2)Ax~qttSJAeZv0yfQvRMX6;34G+Vkd5sl&Ft|(f&!IKBhyA?V=GZfZ|_UgHC$R8hV+>M0jqsn%%67A9a>G2k2E7I#t z8}JpzG-;$PTdkstStvy23-Qc$BQzSv8gMH58eB^M3HNgQ;p($ah{m^K->Y!&940)G zNs4$ldoL8PtrcQ&K(uXQ-}^#P=&j|CpqP`EV(4V(p{P(?=(*4~$#7UckQd@Y*fZdX zCNONu~ zUMnvSPTr1vT0)vO)4KVzL-%_wk~}7_#D$VUFNJpt2Uj?KnHd`FPAJ}sRvU?BXHH|| z7y7vfIlP4ji5J;T{g~FFkV^#SX7kw(|qd%UE?g94U!c;O5yBFHL(3WwW_x zjaphe}ZeUrYG#w-I3Or#vs1z5@YmwrTPvn)j5S~Euu@`zT>`Ln`o**jx z7~c5tPlVq!OHs@wPYtY#u^opQ!(%ZK`>=8UKX45*Rzl5N_AJGX>j%W&cgJ&$+o0=^ z%#*-^!o&|3(#m-e4`S8K)%bbsB5XKu9gSTbFlf$nrQ}OaIkfI9mfZ}6HsQRo%@9n_ zcHMJ48L=>+(Iw&U^Jfi)A871b9(`Z$r996qY2qsW^`&pH=$NUBLq*^2XffeaWLQ|4 z`$0tanu?bOPezeqri?LWI3Y`w(=OV*m`9NNZi@Ep0 zu@`zT>_Kob5=CG*;C4H33nmSELwR#faB>7b`1LiktT{G|*mnLGgtpM4X{zdi{6K;g7hm}tm3(_Cp*)53WTZrWAR4nD zW$CM&z`=Y&3NoQ-Va0ZK?-;7*3pq76l5CKd;zHO%;2lEmg|lMmPB1jaI@rqD*Xfe6 z`NDQAos#p(eI9PW*FSv@-_nK}S{ZTJbZ!r{zfIR~=Mib!en7~*{fgH#wsl)HZ~osw z{MJqFP{Oe^e!3QfH|Nj7i0-4YWZZk0JNiW&%n*k`oPjEpGSIzpE41jDyEWanE!mGh zF8Lc4tYUbm73%kXL3#eTVwaJQO=nJubFOy{eM>xnejf~1p68bBOZVf`4I2>_tY0#M z3(id#JPGj?tyfu@pVfQD59n6!S=ebzGt^TP{zh`>^TJSOTI@<<0cl!&QilQB8Hx+T zVloXmAIWQR;YsAlWRHOsk0ls5_QExNX$=&CNlFU9yQ`Pu(%wviaM_|xLoa+faU9%> zvZR?%wbT&&eEeTH|Fb~Zwl?%yR$RU&eqjR2c(*~1w@czL!J{}%%{jm*LN5sS(BQ4!thLi9b1OPXm3<;k>mq;QCsXm zP-N8J0*6_7_j90yH82!QkdhpTLkB}KVst+^Ips|AYPGC}^rQYb6c7xqm%`doX96P7 zvP?Ck`BXH#%R1Qq{W*4C_(%L2Crs`#0s|(rdguVMqa)C?Un{h4+8n;Yu?Vp9Mrg=+ z-1R?<>o=1Slu{faJO332!5bComsM(pWta8e?8N+kR_M#T3rdyhgr*ZdQx-~loQc@6 z1gCC^V<+$^(iC%ln**;BxvSgecRmX5zVSW|U)&@vR;@V4o*3SI2%LxKttV_>voTsU z>;V6>H;|CT%J3vZcrRCw8vnPrz8s-(rugJ_pRn+vj9W!7RVPqVNI2htGn>s99o4JKb?io2YrO=*RwZP#1j0!uU-Xh0#k9t-_n%8$d)aUQlzA^{kX!A*seQ_ zw(9#WEz8$Ih03{0w+;@{;p3Oy#-8)r#K$IyPj*1ZTD?(h+^2?jF{ zNf|#fk`b@WkWUJJ25HIqiLsLZ!jM=RvoK6hrOiDTDJ=O!et9m$+B=Yp81qd0W)6prt_ zjB3@}3Pb5;(dVxHaw~q=xdEwKea)Df<-4N7_*qmLW&3f(^Yp*id3cSodZos}2k%XK z9rd29Ya#{t=?{ za9eav47W~34AK&}3V}HwPOm#Ojx6E#c%SF&kQ)C7k`wivECG9IRAR0H&YOcLm7QEk zK9HYRCGQS;DeOgX?hD;iT7Q z85aYu;lieUh~*Gu7SLDS zYO_zsJsm!KW*U~A-eF8RE?>F_x=ou?V3SXEO*>%d2S1@-!)M{(oH@mv<1yNZ8A!U< zU#aJXbPFz){{vE!j~kvT(5^6A!h<6-#qyc_?(&#tkEwHCxGLOo(Tu+Ay^tLk|J+Lm z)K@Oa5p9Mv!{;xKMA`DD`3`4puf@gL(+pcV!*Sno1P5_3JD^_a8fe%c_wz^GT!6Mu z6~Xl1Uc%pdf5)=b`%$51KZ{nVT=&HW?D3~g>+hMerJ5qP{RrjxRJS4C(py{17Cq}*`A_T zk~!BsBQ+Q)F>{a@+zYyx@5HvcZ1?{k9B)a_{QJ4VOfinL&|}FIFO%dg`7JKA1U$h! z(HzjHt4f!Z>B%e+YaDF2aQiQe?KMSNi$14xf2k`z9Xb%DeD!PXbCTVjOTXd5+;^4j zobcDNQ^r)^c1;@Q$ZL5R{H*dmK)0vt(6f_Wmf2&RIlg=UM#M(yOX_p2_JQ?lKh_f_ zRXhSNBQWNg@|@*up6b;zN9&+nIu(K0lV2CYaulg4ycB^l-cRG1*S>!VaIPD|#zpZ_mgs*JaoDk0NL;b6Y%xv*0!#U&pp zh+wT69^(n8bTB=cR)UN8&`K)dG0w2vbAAb?KJzJJ8A!_^13wsu_q%n5m$$woI!mJL zJ+}yFzt7nK?X6H#n&X0vf8e%X!KH|F#!$DNFm8=*_& zbSb8LS4Eokd%vIxaa1* zfay=Qfv1;#4Hyc{fs=D^a`9|sJ3AEjG|fXuPCkdGPxi&so-=Xda%_&X+;Ymf(_4|0 zWD++=Cug|cJe22^32cE&0lZA~2D4fTUlhw`ozH#i{P6MnA7I__e~qCq?~+|GYSM>D zC}X`c{dqx5^IoVk{&OLSx1miDf|9CJI%Dj$>%?;kO;U0qbSWW7iCc-Jkg-U**B82& z1yD-mKc=K6W*To{kBiX}c1`7YO97Cd*<$y@6U|1GtTw_6qLyf!te{CtOU8wpXW)_E zQW)}bIhq4Pn)Gc0yCauzEJWOZ+F&6t>9`ws5SqXY_%^eA4gMMpp2Wq&`;exr=OM0G z-94PTeFCf3o`&|QKPonBjH1Qzr^I-CSTPh$4#K4{d!!0Ok`Q|x2XEhno!@EHwoA*j zp8Mkj#q-3$b%+S#a2+s27~3w-Snj!O{pA!s{dNuxo?})RFE^k>$xayi!n=sCWZTOR zYZIdpaA^;Id8f(!At1H;#d}J*ODH%@@u&DOl{r1-tWtqFUAtQ;?x?XZ4LglKWxueK zk$MLyu|J!aec_1he$hCt%bS(qG2@Gpyr&?=Mdd*P!*Chza*k+dbh;QEJ97%4jHh>yLd%msPA|1i{T&;H1x;f1zt-adS^a1qX4 z*&xmvJG8QGnmz1&FzbWS z5k^T=`y$B)Y*Ad)3xEQ{B2LOMtyX&v2M?V>y(fmCY86LCEV7G6V~6H_IwS7j8Jr2z zB3&1uh(l1wQDj_?McLM!4DWn2>7K<=r|VEu7oFaKXmNM*h7~dL3?T4X!8ND}>T^yb4(YQlfB%Zv4^P;z}FP9<& zChR2Au7so9lZN7#7U25ClW6etaCGpg3eRL01SUBkH6;WcfZ5f zcW#d|dD7LrGNw)&{m4+{`ThQw^}@TDcVG`9L-*=;+IgaN&A#X|bvBYqme=pIK~f`w zu=<@AIK2A@!yW~CE9{Z47LBD%C@dU=;@}a(K}_@Kg-xNMN5fg_EZ3m+F3f~R4V|Tk z>ox%C$$OzoSb>bx>*D%xgvNn4n(b#FCP#cOeuFXdhhB<0e#?wh#y%hafk+#}R>Xy< z9*in5>Na~{qALXrOZ3Ml-^8h1*U_YXf0QXLu6yx9c5(G|Myp<3kbe9MPKVnmg1|o) z9B~G^3xO!#E_=lZQ_5C{SF4_QvT6gQ1SKLUMs%1m(vXmF13M4DIS@C$B1If z%u{u|g{s5-l6=J0#6@l90z-F};=+6&4R3((*ekfS{~j7V*#o}WT=h}K$;AO}hjxS0 zsXI6xmX4HUcKR}KFX}v^PFzOyw%w3n!P@BN=t`79xwgGfDj*8iLn$!HNKW<_0&@)& z-0PrL<1#rKFUl^y72MHxR1Z{0uYwboqmhurdn`j4^uHK%4gP+2@T3sHP(6A$?_h_`@-nfH|l+#E}{0Awq%f$1O zLfG$#&XPUs?CB8Y!$tiFQWG{KF?P3MOR*bVi<-ZY$BV~?5fJW|tbwQ!IjmgA-cTdLG~WF&~X{vAhO<{?9Kj#>{^wuAOehY1B)^5r^F{{7Fik;sm3yBeTkCvGG(QFJFSKlfNSy{@x;8aY1_Cu^awGL@I zeb1e+oXAtPnxJvtDOO#dBtNj%CSv7>V-OmmU(cRUG~7LkEAt$5x9Fdc`oMgLSRor z^l*!@3fYj86J({9?ygLqDjX_ePzSMc5@HoyM zIgTbJ$|Iv}_KP99)NO_u(eXHcN7w^l8OoP^k(8RT^^+%faIMNcI(EPHe#jBIG$7_nl(P(x%+4C}bFwJU?#rzcu z(5No=p(I7X+k-_oHYm{>L#U_A)4<`ksa)U$IqjA^bwT0aSruN{<>M8z!2Wz&N5w^)dMV z&xL51%LvcWo&FdpA2cxN&K!gq*+tnbvh zcr?I>hV|k8%$G=YrHv^Zcpl#Zw~$LnPl*#(j0UM*6_Hx0krL|5?zze@94_rgxDfbX zj%-kR{${A&$}!P6P{c76iVEEsdKmOL_z%?gnR_eQLA}|jxNHv!pP35{A1OAf=&&)0 zz*5b8FF%8y`OvFYBhH0`aMv{&(HzUd-l>|n{;R;wsfzNb5uL8=icUsq2qJFj%Q^{o zOqkU0y=YwLn4(NErV!`!I7xDWEsG1Gb$F1#(1Uq^z>LPcKNg{BE+afQuEgNw5wGKb zpAeV~j)@BFil9@So~So|7IdDB7G#ZE@HO1o@D(;*_Cr!!roq_?EIls<&coW67JJ_Zmfl_Y=JnT6$Gz*myxuaMZ^6 zGra#)0pd*EMxxQv;XRwBccVfN$4)eR2QMKym0<7aD?FZ3;uDIC{i4|$njJNbAInM5 zmyr-E?nOiC9s$FD%-3c}j0cyZ%;QFHMv6-TU~7xBL;wIX97#k$RO{kGvL!H$s*S=o zzb{6s#@v&0BEnNKbLbmber6X^wY=APg*u{Mm0svEEz$#F^W^yVS@FA-7VZv1c)W-tE)TEhZM6qpfB zJEG{AIfW)fX6ty|JcE>jKjF9Y=WzEf=k^&JRVZ-)sAfqGS%oT*?%G0`==O zg`-yqL;ra=NEU7ZCpy8=9?z&)4Udy{~+#PRcLtsQ5@KKAF6^wu`w5%`@I~BjoP@)dzX`JZ6hTV=@9miFCyoLQE-$<0jElSj2^7OJJ&%8-(wcEknB&;*Q|I z%ps}b(%tyo9!y`b7DYiekdMcM#u+9WsuAq>7IWz7~qL?|+3YanU$_bPv)~dA|T-JHCx? z)=jr~evD3h-uhJ`IJ@dSwCp+@Jtysb_dA>K7jRK6u?Zqp?Y$~UTYD?DcTqu&($=WG zY9t6Tx==gzXpJhdirAZ?rBx(mRqZW!)6e&M|BCm`FS)N=x$gUU&bZG$$JJi;K_%HL zO(>0DvK>9|pm96YHJMr3>6>y;kPQpzoArsJVywqcbuZ;JsnPi}R*&+lU0V<8rq`@Y zedHN71xP@jg|O(RkD2+2!+buu6CbztRmHs^xeBMk5QU1xj8JH+)rgi3GqJnaLLm~E z=l2b4Cd+2KB(Jg$vr>Mp$t5)U9am(^rH_Qb;W%kpZ__tDF-5n84e;tg1vcW+$^v&` zxH|v!AEPE-e$PjBS>x7E=5K|aoSj1#8=2UCBhCy1wuG(fICG!OGT)zQl=NXlBf>89}Gnx5sjGmY&fbA!EN zbn19GqD@Iim--bf(n_yX!@^>q3k%;;)91v!GNpha$ zEz8|Is9o51u8fmES%^Tz4C8{ppUM~HH=~bvv^WY_wMT%ti7QO1tf*XgHEo`@dwzry zx;l{i;G7V+(zJ{#%2tV2ii{v@UnFK=ubzYonvcccJIVCbV9rOZeb+IMIIFB^oCHlw zYGFQ2+=tvI?&h7rsq1CuQ@!7Z;kwLRhkhqbiWFXohCmpX0^wh4*Cee{=n@8mCW zenYGH@s+mtcs!}aRa2aKTKxY7+E55pXk?J4BX+mx$qwo4ZJJcCDE6 z>9r??Wo6NY0IDni-Sw0YqS8R7&nD)#Idw!N$#r1NtXQg1J4fimD^jyZ^=?|S0P6O* ztcL<8rY1gBer%f7)(Ihd;S2R2DfI&@3D%#+ulp{0AE9~e%Dj9o!+VE<)R#9LL| zV*BY=sMkybIN<#&(DVnT)ei)=_AhlH)|li@DdQLyA2H&TqluE=n#T&vQ8-w9VpH-* z`=9vF1ycDdszdP3D_ik1n)gZU3Q#vX??BIFlYJXj6Eq&kp4=Z9(b4ewoE{X+C}O*z zA?UtILBl2c4*EShT3w1LAgo2Zpg?BC3vou~x9XCTHk~R(fRT&8YLB9C#*jMC2XW|? zkO}k@FD?#3nxjb~br`KdvgF)DE$97}}VVV!hq-Xb{e!nd=S zOrd52D>H`~l(A7tmYkOP{8u%sHbm`XEIf#rNk!Np(x|w!PxW;{v+X4zp|AN zov0#fLg=X!2d0@Z{o^G(BR;&${PUfP#7A@aZbypAZTnU@!&`k?hxHzgv(54;_mX`~ zyyvCaTju|yJb&bwpmjCe{b4cZn2M@e=IFi1Eiuh=!+Hy}&$|=KiHy$fKd+X=H(Yc- zdr@6|7_n8ES2&&0ZFVn5%XEp^)N4-QwGu;BmmQ|rTz($x74^1i4_~f0Qkk%P4sjzm?_R)HR^3#ycuFAJT=?-B| z=%qBrJjBmNl*%3bb)xPbLEY>!y1?`?*X;2WNJ%UB@+#h0ow_xE5WoK@xhG}&u+vR! zp|_l!JuM@(Tn9@w{3qFgqRT;F(^+&gV#0bO=dDoi-`)KAM=qwEHRh^x|~+W z(XyJo;!K9~PFzYlpKQ?R&+b(Wo!^-`ICG5WDkYS`$G3YUruy);lqEEg@{2UXcWRi* zZ7*8MkDVlT3|mU2e;>1XyTr}_vY{XZ311(d=nGb2H%WXYfb`eS~*Fbz!7Go+C`Tg8cnTSwTk4ssgxGAr~OSpAcL!L`I7K~F6anq6c~E!c$OOyKmlF_Y?&))_ zmlUu;;UI<4>-c{BcI}`FMZ{%#>Z>rO$3uag-DlG>CzXx3hnJndy9p1rk0QDK0!LYI z8qKJ*@Mhd5W^!WrLgrxoLrXhPHdR7_rP0rMMe!Uj?rvZs^QEP8jjYlg06nob96;b! zp$;~(AL1;^NUn8g$OWZMWh%nx0@zbtW9To{-UkDy-N^lN0JH~IOsHbv487j#*R&kt zKlzzVeH>@rMn%$wmmbnD%r2}i59TQAnO;}fTjckU$Y{rX5Iy%P8bpdXe^>wMN8 z|Do?&jGya|Z(PxbI`3S5Gt0nF=MscDY-_H2UPvxgUHL<2TAZ9dJqhyJwR_wgygWP`al_mwhKqs~ z&?Ep}=s&up2D7&0)G)dFU_tDJCHV^5yCf*7$qXuiR0s%R)q7<-2(&f|R5=h`J1)C~^#+?tfn$;5b*xMDu$p zAgJL`U-rsHLZ_Z zR`fSS-px~I28M_bq#G1lGXKL5+`{gplS9M=9y>Pu6w6)wrPt)<=O+A<*L8B|SgvS{p_CY?$)X{yJA9U&3W9>U9Fu)f^*)6ifdO_B9o9PsTviTdY)z*u> zIeP4j*Ncsf*L#f3&DC($0H*svW&3k``aWquc|am&CuT5 zIqC+@wJXk>T-Ki+a&DF~{}ltby*Idwt9jb&z2}KF5RJ|cQ<`3 zZ96O+Ms{kqH?uz_551m)9jFP#fYXuVHT2z@IYal2gY3PprjzydQ0~o0=$TX zMQ33Yi&Y~r(GQw*%lBX7V?Qn@)$m!QbDD|njcPLj)5tw|qa9Pd8%}6^QkKN_m{Y4z zp{xBvJT*DOW2nfb{G;{BsQC-_tD?H650d}3$8nL}P|xZ%Za*SP(tTh!EZY`jKdyoM+&LN_d?u~rXu9~${y%&q;?W_Z6=lZP ztxwY)V=^y_<)6tiRTa30{hGVIr!!IJ&B#940y=Wc5mNqIHl8}}EnS(EXJ!JS-SZPr zlD~YQDG2|3#M`pygxb+~L4%Pb?1ML<-P;|DzlcqpD z7+-paEOj@0fiZp1R`4?mI`}8U#0X#l@Rm;LUR`aif-YF0M?%TxC8 zLr>C=JD>DYl|07|_n4umV%{G7PI_KysIid#fjh)ZPnYzE9GJ4bMUh&) zEm0X>;CC?g0i~UOy-~k!k>kY;HMKx*L2)q2F7TCcOset1Cj^&%-8u`?&`s-d?OD74Goh7CvGj_S1OV4#P z-)9H$x>9N`JF)PMCxf||1BX^NQ;o~E5#m^QEro2is%}81gl<2^LapFs*<-6I9i(0X z5a{=x@FACwT*a!#jM9UYm}zVLHe>uuPMS@uIdWQ!iE~z8{5hlUZIx$S88mi*o_tjJ zkzJlEXp5GX2z)NEI5yVagZ|*BHTHc*za$CK>?l+d7_C%NPH~=0?icap9MapOJ|RZ#t8erM(tY zb~a=*gP)0r=imcQ>Fw037gyWvL0P%tI5+OUeI`A6-iroy?v^w>BdZjaroql z+-L5~4*@Hp7CKFhpp|G`=>e%ly>j@QP zdlebL`)V>e|Lz!*alE?zpfLyzhe;#ap*4ov1s~nD_5a{)Z(M+tQE5yLTIU6ai-VDm zNrsc|`tx6teFTciv4ZN1QiOi`scB}xdd@0pg`RX#itc);aBkGo z>^BrUuw-?uwe_y!(8g)m&sPba@4C#~=7CfpSIRLnf{Dsut%nse}>=|bJ+P|AqTV{b8K65K$7x~_U`Dn$yvK93K>d^|nl@onKhCe$=- ziD=l${hbO+vd?$rbXG#<2b)^kV+~zgAiV)xNR-73!Ki2;>y5}QRu%w&%D{Ik!NhuFY7=4AOw&;&)oW8vdl5rHlhT`Q#kyFz zB1;nrbpMrUqFLBY4Y4$_yxiK`rgL7Wdf`%a)2fybww#{P=*)2YYi27yZM24nK}a3@ zVCX5F5B2nj7H2)i62QmW<5a_eRV^dr{qWF}$Y;y)1! zxNmnt`l%k;i!}!ISz)4Gj3#W6C7>Ww8x`U+cf6HDCZeP*(B_AygA@^dy$<8g)mHEZBUARn}u(^_j1Ke6sjGrQpFHYj5t+ZCNm?ke+#W^6$a7 z?}mhQp6fALgTag=qsfW4cBfx_5#`bfEkZn`P;m$zR&91?V|#I}PMhybc4A^B{atQI z)%XoE+s~@U9%G%2tv|_>81?fTr;B_wvRf*L&v8-snSpAwPky`uRi7=f4*_VnPRh1F z4bLm`Vh24^c|;-1bzm(ln%#PWbmw@PU$CY!TodR$lJV8wTzlukPEvh6q*87oX6yvl zrt-FZwqf-& zU^EVGrxFtF$o~jT2+JceFP;)Bm*#_MKDR6@1COK`;lV%7{_wP(_t;(6IW6!?5R&%9 zF}~Pdymqs&dxh)jXF2GXC*qclzr2Z7aEHEJY{1T--Xl(fg&6c}4}qhoUvs zLxRVkSWY6yOHsP%oL~H2Jy8>hQm@m4(B7~7G|*(kc@J|XX8q8dLaV2hk(4UfceAn{ z#trk}U^y;g?>qM6qLlqJMJd*sP_mZ%OXHW|w@jC_fj97=tCn+)As*V?zB8SmIllRmIX_caQN+9l25qzFWMB{b)^ZTWlA?#b1e|m%4koC3s16% zj1;FP%V8u(WX~|UNfDaeA>SbHp7b&eP|27OJ*hA~%eay?H8mJ5r0cb!)bJG1KXV+x zZ78=v_&E4yL{ttNRVh@3{|krf6q+{q>VA{4Eh9**CVy&P)tdmBxgdXC<$PEFPdc6| zt*SF=7WQIp(qv9J^GyB-=!xxtKeEpFTYr$VauPBb<_9aNT}CB(lox9gR z%NRk0MhLQ8-!dK-hj7;~0xA4A6S?@?7(!$=uPf?GVp2v<&iXwTtR<2#&VPa}tSOZ4 zlyFy0SBZ!M-8>!(yi!+<2$6)SReZ39aob((>d10GNEteJz)AKKn3PGb-9x&6AmuR&# zSw($%t&q+6t0zFT-os@4H)CwZOlMyysM*pzF`cy)gzN~~y&SHx>M9kWAuGx2w=^o6y=->;UCp8Rmx-}hP-1JPPcn_iiT zZs_sq1>-sDMNMCFum)1!O@8i0o8d5aZj~H{FD! zT4$bta@VK0Kb$wH|MvYj9v?X}#4pOvQ2LFKnD6IC5~}bbJW{m6qIQ&W5|CH)G$$Dv z>?frVJS;4Fuz_(Q?SVT`50PY~7+ow7^Yct+`lWmd$k8z8-(e-3epp`5$?Pr=>_HhK6`&~in7dT35;vyiPfU~Q)aAanW$}ov!js% zmV}6(!eq>=;lFziY=tGM<4}wvWtTaBXnGpOEb~MA2&^oOLi~Jprps6fH#UQ6;)ONJ zhQ(d_Vt>&g`N=0vehDxp{7rj=$L7lR#4@Ur^?7GRXFb$P%9fEFv$P+k`x1S^~>>m=1n0X+M6OfExY$_hYh~>t5WJCpF6n+4d`AFNYnLtL!f6plAfJ} z<@fj%N}#O{Q^mAC#1RI~_9o%R{PsqOJ7SS<9T;AN%3J zfuE6GpuGqFsl#JAscqCv$57Se@p7@Z?_WERVA~ag6FTF1iRy0t;3hqaNg$JScvxt3 zuvFo7o-_Fq=&Qb*-@TAgyU`B!H*pa5mn368lAfJQ0?k}zDx5DBZnn|{(nVPCwL0`B z^t#Yn1~>}A=QI9yq)InDG6s*Gmqn4D_alA${3>I6+kUeCpsrm8PW+5mwV0!v^O`AC z^??;=jXJ{`m}GmV5?-B(h*x|^(=JyIfy=bGab{;Ct;ZJzS$8jU@uI;`ClZ624Y@JU z{YtT&b5jEl*V48lA(_};x9FHRZRXY~%+meZOlS#ayhet`M&?*2dQBBoOe zRBK^0LadfE3fS$jg#u(E$4okrSXxWOMnLG6AAxwgJ_TprXPxtxQ<-zy(U)(!Yv{Ap@U(Qy)RpPd4bMM}3~C$G*m& zyZRbq6or3DAgLnrXvh!w858bD#8XMcG+~5qjLy%K`+Hi5y;+iQlPczsk@;h=XWWmdI}tGx9PYfg??FFRlOcnfp6$8$%z<2?npfduVIsfI$^w_{;;W14ydzE> zV`2^hhzY}FjzP0sYjgC@#|uN1B+UlwUNd!1-_wEsD5X+uHk^O=f+lYH`K$l-KIM^w zT!UPPP>BEe_(DS|(K0$zW=k~8J$81>vif7qVkY(?ThEYTWqXMV8E2`w^L#(FtS?6? ze8ukP&!2_w(&@qm{Z59C%B`(lsM}>md1*2~@Nd;ZCTcpxEXQHS`^#Z6&aCzBwXf9e z0BhINw{kX(OU>voA>KR>$S<{JnlL38g4zMDM4cXA48-F#ff`gGXR*Vk919*^gZc+e8%Z&GfK zI#2ZTUCdTN-e|j}Ax|)cd{NvYNNT4{be2lA;t?f8tLN8*Mv{tk)H*SNnbmZ*XZ< z7Fd6Nf{^$b$D@nl70>KmQMpGXiKl7b@#rH-k9GGjROHya1KD?VyJPs<81~Q4Xi|7H z@0Zp{^G_q@Nl_sV+zUFeh)t0ffYDK|9SI$4l@F;?LD6BxGvSRikB=0Qd|i=17Vsef`XxnOuA_Im(_f8IJwwJGDs6!qD-xI+ z;xu*q@BZrUA(%K4rzzj-M%3KCkL&qdCOg;?qVDXbm_$H5IjO$Y1J_Q!k@0&xJEm(O z8{koH8*9Ie9DT0da8${Z2leeyoAB!@AfJD{I#sq{#4dcI`u76+>)!kaM^*W=Ky zbGT>W*ysQ9aEJ2WGc`o3Gqa2~;^x?$MM!caxpqmaN^PLJ@rLTrW{A`KNra~advc|8 zUEVas=vJ2W^U!f^Dh8%fjj*6>Sf#gmRFy+@n(-S0Jl6TioQdiVbn?VkUC245%D;Ch z1p`u<*vd4FMfv#;DaXdTiGJ$Z_zE;ozVGu-J`h>n!rGl;CP`jGl-Km&K2@M~l9|*i z=R$zP{Y$Epdp)sz*?1=o7rhC@Qb(KgImBJY`*+_=#CV8Sn66hU zQPhC1P51Ab@JJp>*2};j+PzShUvhRH=ASId537kA`|M)C|d9e_gtfEHxMz5UV8)ZM=ublkwPcOZ30$PN+Sd4Xn zgtkA9>2fvt9s6ZCm)p_S>`*BN^j4AgKylP-b?>raWctF&`zV*&^hK{SCGI6db#b)y ztQl{bHIW^tXh=}(-``m2PG~RfwcSH}z$O@GG<4K>?{~UlbL)Qq7%#(#YG1cq`xV9= zStHcFfL*vGiKi|+nvraeJe4Ij6+=cbI2cvk9q~-yWI^hyQ8j7u244%7kc4@f?2={9D)bG@u%EhzHP+(!6ZKcriWT(PD>hw}-I8-LaT7 zP1uhkt_#+I1iR@O3L4B^F4ZoFR3fPv!PWu`Fy6B(IcT;IhXa4yYW7giQ(X_-yvW+S zE>C{xy1U|gF(RF$$kD;I;q^bR1ea@=cO>mTS*8j+~(g0Gnma- zSmjPlc&95N{FLR@>yuAF_iM-Ty;FiARD==orw!A@1L+u0xrq~@35iWf&r@=-?Uj;# zlY+W>hFzH5ZC+)0vojejmr8dM^ez~`yCUZTyq}=y^zh%cdaVq{hUlI-R}_Blv}kI| zTjSU%M6o0WkKLh{h&cBY;N6n(a*jo0V+S6g!R^soHd)-EXK|3xEKOPDe9q6S#jNTt z9go|i14*GT2WH@t&kw6A8bj}cEikF z4n5aQjgJy~6YfFqK+}ixtm=GKAyORFZa1(D6T*AQj&)O~+RYhJ1I``Nk9K2`StCo$ z{=3QsIqk+l@N=3_=99;l^naV(H)`KHBx;&c5H)=sNCTvqntj5!=H|$Obx((Y;Cye< zcR~EB#N$c9)2CsYDQ`}JAq+Mx#|#i=X@L5TpAbUWW`Q7D(@HXJ4x z;RZO_O}b zOA)ZvD=rea*`ci$C0FEhC~k%-MQNyvAP7DdBQ4aQ#t)CkW$azLx6V8U1YG)y8F{9{ z5yi01_`u!y-Uk%aGf7jBCGI7ri1tEsEt9&M%LYgwrCP6Ai?`Nib1TdKnZ?$_v~&W7s@uvXU9Lj8bt`PQZh^qNYs?L9j5P;gz& z^)}eQBIzr>stKB?u;L^`!u6&m)!ZwyjqxGv)4q3!LJZtlA`emc&Lxay{XSZTzju_P zg<~_D@F+wgI5_V?$XQm%Np6 z?`)eEKlF$N-~XE#p#%WZmGt_)TtvIW1-!bJsy~(+MeQ=9*78f-$uuj)-Au~&e1n`}~^UKKpc`O$2>X_G_&8$qQUR2wfAo>9ib(o?1N3empN^P?zFl_ z(`s4Sv1PHs-JXSMRf*AttcZhp-J;@B|s>sYRe^H0@Va6nU`E&KT56%0}S0F6x!}CvsqO?(Mt^n$rr#kW4W7<;Ew8`E9A7V?hP@Sy$^QtSz zZ$`dLuoJh-0K(4NZ9-@Od;LImGg@^?J0*7C*D#^P74eH3AeI!UVnnYM)0Y`N${-nX z8yJ?$q#VM|HM>4N#qy!dU6GP-yebs)b5$s(FBa98s@}Pz#Q=k*W%?f&PW6(F28)N$ zeZm6veLmm)zixToc48KFxuk745ntYR9W!PDJ^}EoP`)JhA6$GTD7DE^3%|buHP=?A z6KhZQe;L){2eqy*{7OD};rt>|JB@3|cS15=8wPtS3D)r6x%82KsfYx!(H>2{#`%XIOJ|*U{O@oZFMDTFm=)yx4>4;Jq|R4jj~FlX34-59J*?MmX+IGEKx@+S74;LVl8{k6TWWj|+vwx)a| zVns6Vkh|mCpiv>u#)C7VNJ!EMGN4w+^U`PLzFc2#(T%^80_1$^AUNaqMF70^&thac zokG9aP<+=nOR;DDX}5JJV}Gs;g~sY2$Gcitm=8e{EzV|bjE8Bc^XX$fsx$ikz}LY0 zFOlUanQd}S5rh~3t+~D5ltu5#z0z|TE|^)rs>+%Fx_i}aw0!rN@WHiGTbbf0dsbMK zI%eV_?gV-FJJ!znGH=nuTnPvOG0ghgC$?J(b6ECg$tAM1&${uese$Ey=77NFXk z)~-jUHWSdru~ZwQiHx@H<5zJ26x`T=0>$;|?Y9K&YPhqQ_&w|{b26p(@Zyz;HNX5z zAgdm%>l?AcuFcus(T17LRl>ylJ{wn)5lzm-Bn+ed_d#WbidAG;u+;lqY1*%o`miHb zvqYP)SGj;b%PDyKi83;^wM&4U7*`^0#6oPB#ce9Q{|F$3E>R zAQzKRT~0PqQKPt?Qi)fWBEl0u(Pt_Q30rOelP;aRfDB9*|3?P)cIM`_#%*lK_#fsb zCR*!6z4i`}%EEDG3SuqCY{_Ybi%JWu?-u;eWd~#-CbC+%pOgWRKg6R{Y?;~QnBSrL zTdbY@{@ZQO+>U5G5-r?>Vx{l|P2MzJac!a{P5iD(2D1M9!J1P5x0wFtF(57~`FgbeEi8#)$p62=|GO{{BO}o5uWgN5$S4x75kGobhMFJM H9i#sbzl81d diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear7.png b/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear7.png deleted file mode 100644 index 417af6c61166c727b9ab50cdd14a498c68744e0d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 77056 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 NziqdsJ&+yE=yzftZ#5;QXZo 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 S-AwW`#1E{fu22Os|L{NMEB7e? diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear8.png b/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonclear8.png deleted file mode 100644 index de64264914cc8330093550eab1f0ffaa01e91be9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 71177 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%0Dr63wml-6F*tONlh7MSGocvsq)+w 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 L&GqVZUE=-^HyYM< diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonfail0.png b/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonfail0.png deleted file mode 100644 index a25325815275dfe5e34358f3509f93fa273ba40f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 67970 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 diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonfail1.png b/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonfail1.png deleted file mode 100644 index 2dcaeb92e3bf4650be65948ebacd9a7ae8bc24ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 69118 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

  • ATKFj*pA9VSx9n&4rRut6B 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 diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonfail2.png b/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonfail2.png deleted file mode 100644 index 0f0b1d4f59fa046156fb8911ba14cb73ccb60710..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 73351 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 BEpq?> diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonidle0.png b/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonidle0.png deleted file mode 100644 index dde0adabb403485d9f92830f5ab95a4b4396ad83..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68649 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 OK*OB3F|9W7i2WDz654kF diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonidle1.png b/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonidle1.png deleted file mode 100644 index 7295e95efe9c962cf5e5cee94ff928241b81765c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 69329 zcmV)@K!LxBP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D|D{PpK~#8N?41Q* z6h{}w|982#dq{|2!QI`xxRsWo#i3AIoVH&n6{tZ4D^lEx6|3Ox?h;5s+~ty-ee-s2 z?=BFzBzKn!&5usAvuAsIyZ?Ff=FJh(sdwRw@4hgdbbX3T<1c{|a^D)xSkW z_u`{%p)PC-btNmh`-J_%-%<)mUiW_~=1Hk&zxfD1B8jhVq5Z;NVVeqxwd9q*y06jY z)w;gCdeKo`D1tx>e--L&p$K)|*HF=g>cVq`*9hChtIz+>QYzXdk5bVt`RIzSAh1GR z_)CQZSSXgP&~tU4FVyKVE1jY%+`?y3A%wpLMY!nbudq#m`f4Gu6^dM4bobF~>3O

    8CJfgqHE01HJBS}KAVQxSw#5L@9f5@Bog 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%0DW8d%49(wA_m-Wzf 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 K%$b<;G5-Nz>y;7! diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonkiai0.png b/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonkiai0.png deleted file mode 100644 index 911378cbe91f2a6fbaa7d2ee1f8e71de3f7e2d75..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 76964 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

    g`m? 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 diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonkiai1.png b/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/pippidonkiai1.png deleted file mode 100644 index f89568bca25c2a7bbef738d024c2f2f25ac398e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 75434 zcmV)~KzhH4P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D|D{PpK~#8N?41W( zQ`Z;Ae+glSz4sIZ#EpCJy?2+@E^DpbwXLmoSN~nw+Nrj7sI~6B_a3O=Ku|&U-YXE2 z|2^;J1yDdn62S89=a%_h8D5cGA$zCLJN;Rg1dzTn}nPM-HJ79)@)g`QSH-l5`72N zF$>i(`gek5PmNxW#F+$Mu;NM7Nx-vNi&AZqsOPGVWwDm2YP3(sN$jap5X8O(EzlzJ zEwu3HLN_*vI|(#d=jN=Du-mcb#F`6h&aCxf&6hQ@*aa~lF|Q9K*aTVUw4KM=P1eF# zORCUPStG$0#9naYw4h;HM81U<9!=<`CZQ%P+>|x4)a_X#VfSFoleJN*W5RQ_1&KY0 zegcfDvfeM?9*9W&)O~4NbuuT$H=`yLQns= zMd&p|i^#XoT%e1Te8Kcri1HBjoAL!Lh}HdniHgT6ZVUeDidL=g_hHbvAmOG7R1$dd zuanhI;@*a}@vI5{bq>1!4fGA_BFHU{?#QB|T`@mNibgimNBa@2(Fs*Vf) zPJqq>oex3a$?xvNS|DqaRoisl)EF8VYFym!rv|YnKm2yqF0e*cJ-KkwSj%CJgq|L+ z`2AX_2Q4DsLQ{Y)KtZTUqy!PCMlJ_xG;vt5k=P4@FIe@2wlC)UMW^C^RonFU{w4@? z#lJ!$0VhkhVk5B={*pVRViP7T>2>HF2re2&)@U*{n6+nFBbSZZde?=x-;W^nvJ%p7 zHT3!SvqnN6!5RsD3Tq_vg{;xz!g*?;9<+#j3ypyw=oLksnjqXH;$)3dqmVxe>!ZIk zfk@l*mmVWQqvs3Oye==~ilS51HvPT731TIPvLNb$z*iJ?p;Z)g3R5KEqec^z)M#4> zL!|Fd=ZT_c$ZBuP+EmtBv&IQL`P&88Ok+WsQ!JyeqK3X_C~HSqBa1$UH4=K7kR?C8 z@co4I*g_p>5&0Ihjc{?15R<5r*w8kK^Zk~3MNtj1sU+&+F**5pJ=(V^fbH^+HrT^0eq8z0qIZUL=IH76;NDN8j zNZiTAL9Pv2R7A(=yt=a1k2N}v+_^I{G*W+$rUaconvgA1(AVLh??5M&#QgwkcUhyz z8yeM6heo3p>d;8+>ABSTwTOKUw1|8Q4@0;(DhfIY2Q?D&`&&h8okX3QAnNp&1YHny z!Q!NCdOdotY6YEK4SHfdi1n;sVC>BHO(D@YghY>;0rbTZ80$-5BGH41z8=gC^kBxC zwSfUFjPzk*pwIp-i5-0f_EvP=bJxDAsba(VZT_!<{ZX=QJ%s}41(Aq1Qy?{6jFZPU zu?H$yp&EnkNoo{{qEwW}o>v129W}D*$;}~jj6GSS--EN@$rs;Lq3=p@^`&xh&8rav z{2FT~StF~S7EcK-9ARR%#b5srT138u`%$S}wSq1PI5ojSr&e+Qe&4zv>Orj0f*g9B zUYn>^)WuBD4GbJ$VdV!WsU5s6tT0(A2Z=pSG4zI@5 z#M8JMTa1`H2br9Z=Rmgz;$KB5B0)kY{3WxPeh&%V+l}X@5kq3=1`|_vSg_wptS9{B zJSDOcWM##pn01~?MIray?u)!3==)G37u99fNbG6x6#4IIBqO9nXmR0GLyO3_AP73O zhY7l1nG2CF6&oF=zk;~?u|{qK+NReMOv!3RowL;S%wT321Q#0*3~;uGoo5j8JC8w$ zIpxdJJ^BSXi2H3J_D4q|FY7EjqIN4ZDuUowA&QmIwH2?Y?*e=K_VBinqOGkV8%62E zP%MSMff+=GtRvQwKv|dxnJg29tfR~ommpnViRd&jk`i-~nsNgrMR%$bd$QcOutqK% ziohYUCs!4@aD+uvG#qHL>MPJ9^8Y=m5_NL1RP>*dVAEd`Y(dZktDIUzKRQKmP_zp@ zhhB?bSGb_66?9rQ&n=;s7{b`JHLNW>(az2S-8@^N(5EK~gStVnwlYqJ(o&AQKok5(7EN#ui6ErO~f!UZ;uX6ei+`qBg9>2Kw@mc#6|UREs!V0 zLK%G&d8wDMKfMqUckZxG^&%5{(seZ?^z^FaquK_@Y%Z3V=mb+UH<%gO!q>_S zJ=~lj_6$K@$3al&)5mChL`ntn)_j7s=eD3!MnN7;7fai|=+@O0aq)882tNd+g51@* z!@{CBCbaDYkKT{N#Ld4>!d@MvnHkW#x&hbXuVK&K1SBUOLun})>S_d+)G^jBvL=W< z`RmDr(~=sY0xcr{=uuJBNr(keCrkZ)mrGS*E;O>vX)z6@rmdB`g%FZk>FHa-#MA>8 z=B{uzvw$BH^j3~;klT5q*ta8!>?uE1)59nu9>;!Kgo}4~GI^jNj;4)BY>U9)fpBw` z;Ox10WT%tvX&;f;27Lo2qTfsZgQ-$`Bd)^a804M(9jimnAR(ScRFxpHrypq@YqW^! zHf!Xb(~}Xq_Y15IKZndT%7CCo5OL}OC<=!njD!`l zg8N4B)Bh)F5&7na@MDp33XKF^aKQ+cIyJ#k7kub~pwnN9a%s;RJtl~GwW7|4E{Y+M z*u&J!8+N9S@U^x<8xIc{djzA{KLjP_lpdw|VVZOsQJa6j?ue6+72HrAZ9J6h=hV}) zg}$K;42>*cWGsb|krB$|a>(Untd(%BxHto{!h1R#aflJ%J{FVae*<&l#+e8&x*Lj< zdskrJ_5CO=BJYeE5^~CDxsNrVmaiQPjz-?!p?~k<=kPT-5bUVQlIP z8#8wV*w~_@uOF2Dy-?h`Q=><^)CLlBvx_^Bn;3<2_mYv6mxml#K1#}RQ6kHPyd<6J zxbEZIDI^Biz@2VAbLSLc}iGeMWp>{_Gs-N3|ZU$O?FbWD$Ll8t{1ikXKzR0T4olqvZ7H^L=iDf>prIVaToiJm>3iQ ztDr&9_v@}Tf3}=`_p-~okr;6V|3pM1BjY$b9W}?(*My_Xa6B{P9awmEP#tMHDC9+m zSo;osKDi2IY+D5hJx#ol`jgPp1wpH2sr=x7PUu@izVV=wUQyU9`qfF$D~dY#)2WfD z3xZC6>8_*H0Mro(rgoLWE@7f>XyguaD<626IiRzP1DyOiBftG1l$z2@H4nsPa%QdX zLD}82*mXA^cT?}7FrSUgm6hVDn+y^IFSPLu!DRn75cPW=`ue(cbCty1Ld=PuuqpB` zGEz^l3y$=y_UP%mV&ULvFd4N-eStMKlIN_}@q(4LFMO?R z(bL%x#_nxV&~9*}tsZR%G&yQ=b{+QKi^0u=B;;gXVt04b4RI7nEaBnW56`r1198te z&^M!>wDFMLID#t|)?<5EC>!a}ykzYN^qGhmFMkh1eYz~0HrXfF;EN5PB0rzp@*E`e z)c$0RCS^(JX(1H}JzYHi388NhdF>;Jy5L_YQKweX1w(g`AnIhLlL}L#O8UK7BU8IZ zQ5TCP%u;uPrIj!IY@N{8-3^8=ZBW>D5M-A0GR+TKlw@&sEAE6H#L@UPq$Xd1qO4Lx zMU%wBrVmE9b46g=+0gf+X_2M@saf%%@3G?KF{GwYF{YaJO;6t)9}Hnu{K)rJN18sl z=Qm>M`cIIZO@Ll5)+OCyO-m5#9=7pG)GxAE*_)s;-G0T=rmS! zqhyyBKNdY_!KiB2SYNf<;e58FJlJjJkPbg}z1PH3OYM!D1&-7Thp`s0$W5 z2|5WoS?eV16ahm@KsnT@qJ9k%rXm=dxWU?%MBM@XU0k5=)S4G{YpSWy{E^12@$sL4 zSyMlRfg@d-&5NAygIMc0@<-WfE68+|t6P zH(u}B8OEbNQXOetCvPMEr;gyudQb>XD$k8J5bwd#L zivD#HcCyyVQYT>-Trh&LleJD(JDEeBStH>W{`~_;&Aj2|7=R(3?yz-h$3%TFitL-8 zB~=?J{4xL7Z`crZ52;C~n8-A8410YL86l+GcuaN-glO<9s)Nmo;)oMCe&J6Xy?lX< zo+zwbg~%8K`i@8cZZE^go#yIJ6Y_4s0;Dxf*Xd!y*q2PAc+LMN@1r-Q!n2^=;UdGj!xZ?-RDWjjhlXmT0=2S zj0BGUg7tTj5O?o5lnV7t*DSHJ>xcQBf?+!5Gu4siO-Tk5_!En<_C`9A-xWi`PNFV^IFetT8qIo=f1QpC_rL?fPH6)ur#hW$k)9DIjGKT~ZRaE3{n57C z39(!Ts~unCuj@AvbMF)sW$Fbh%@>grgLiA?Bs ztB$hKmt|MHK6DC9`#r}*Lv^qjAulMv?mu6|!3*o5P^znYQN#^}R#I9^a@-3MH`EiT zpVZvZTfBHNKK$@Q)$V^1LD&UrU5J9AJC`O#$ttIrPFm|v>39QJqY%fbtdY2rJBB&{ z5_VxCmV;6U=t~S)t03^yp|Xq2ST7I`!=6<=^U=lf%oZHq@HPH9zZ;nuXQ6BcIofN3 ztJg4$ZPf(^0ew_Q9t}k7nlzx3}Ou-553O7sk1XyVDOH1)T(LqAtXSbMZZuGj)QPjA4*@XhRv`bZ&&OvS)QhiEjMia3#^GricVMT1S z7yAE_$O~Um5NJWzD=to|7{4wEJ0<<1-kv%GqLR3ig>GQz3~Spq@UgZ>e^-0>whl(e zX@lU`&H+LV{nRWnJk}O*St+b`e1lb?mywWg zkliAh$%5FFFftr}`u7i5?Ixz~u(fN49!`$vAJi89V@IHUUr$KcD_3_q3z^XWvv4gIto;=Q zITxUB=!XTPW+HRs%c^HIZ_F<4#mN)@;%wwO$Ype&JeoMUjleUrm%#iH4Q;(2`PF?` zzVkOECG6r4h>S42?F4jxb~%j2G;P()xN!n&j<3YU3v0O|=qkt$|0`?cpTEf(l@X-) zd2-)0Yis^d7I{J3$pWu9fl9(I_}qmFR1$ZZK<&pGJw~SW1H!H^F@Tv>2lVmw!>A6u z&}POs1az>4DSPGGp2Eab;Jc?^!HN?*p(sm+o7)&no%?CCO7D9=lo?X|&qw&}(s|@& zQ3bX~n;vbaV)%}0W1qXb}7w3rS4>QOUdNDMq+Xlx53BXgLVN+C5fgsFiZ^kfQ@l&~gOptMwh z;?h#&$nsEBl!21sBq$X$dgpn(+bjeBo!q{}z#Fd&oCe!5 z??A7)oeOy>?(Ud}pAH{EVSze*1_}Kp)=scS@$-V)hOGQ%+oeBBA}@$L33bIN8Da92 z1f9g38s&X}L;67IZ+L}Ivjj>6pF)|m0b`>?fcxc|JM2v|FBIX>98 z6M31ZVPf71uaBFJ%zjU*p3yw8IP@D<9o&tS6e_FPH17ejIL)mB;ppUmRxT3wD#b7p znK7Zahr+@YWe$N*dIa&6v6xWN1w? z{?Bn>89qI60NLrsxLy9*eF0|Or!g_%4mBH;x6WeonV)d{)OwV$(WnZFviXTMa@){^ zEUk{E)v(QW682FLdHV2zdqs$lA#o>*orIl2AYECb&_{}np~vapJYcn(nze$d)Rpto z@ev_n`1*~ zDi!&;w~>~Wg08Y0DB2EIJ)>!2dhIk0{Qfcahi^k!DH%CU2g-l$<<=k1g!Dy^o|DjO z&|Fvqj)A^!Z|FPtLL%1ZdiOfx=!eXxT?o5%4I+sJ`gEQELw4-Hio~ru%yPmJkrc-a z6d8&Nqmi3k48OL0Akn9(ujYf;s~em%PT+EU66A6&y#{Fm&C!vohAepU$y1aKr8{Vr zE}OaWg#TvP8)G{s6(}#_HSB&lcIKF%A zUF_en9^W5Y!G${>zavL_@*!>*`a&CTJoUp|=xO0B(R&SgrZ;A`_Jmks&-r}Ugx`Q! z=t0#pnjV&H)bab@-yrKNV!Ztw)Dv5fnf% z7i#Fo9>JOiYh*Oid>-{){6)~rXpSN;xLW9>lUs&dGW27RrA{?ZDR<0V)+VtgtVHE7 zFff6G(?C4i=XtDL@G<^8um-b#c?te)t#JHA36}k~61O9^K&=7Zh%USTIDTF5q;Cf}xD4fXDLwRW%t$E=qjWIM3q7BX zc-*f8tZX`}j&Pbmb9dgXk*k-CNV)*XsBCtc*_=LbMeCg`bDB^UtoOF8QJu9}tUbXR znObUbr{c+u4udg&@LSlqVJ*Jhw+v(E59C50OUr;ghn0AJ_A|J4Zwq_22;JNF#nji| zfvuhTCA3E0@vYd+;=;wu(`fMct!uE|`6d5g6G6IV3BKLE7TFp6WLsmSgg)%-hTzSK z?_t7QD`9EwqV+I7LnC{LB<7GvOrdNMc@E{Exp>0giF1yXf7fz2t38R)5)f28M59fLWc6l(2UP>s$*n( zbLuRS7{bYUC|()-D)#L72cKUh^mfpauX@e1&w3!`Iz>Zn^Q#$rW zmx0uQYad!X75u~;wDRVci5KUGV|`>Kj525~M&l#-YazZpyop=9)f6x^X@lt@k74dh z%VF3585rs)-a%>O4LvYM0JGI5SqgyeIYR*%@M5m7LnI5^chLGsTZXMOync;VU4O7knepOYg1UG z$JM5{y+a?o)Nc;9tX+*yw|#`Roh;NT28cV+3cNk>V=P)S0eJ;yxnp_;u6QcI9n(L5 zird!}y@$KvF<(!$MRY6j6buhh6j$Rx3!Du8d;>q6-T_%brG!q6gb4Od!|=|SS!nC?bynImmq@_~z+&vl05HF7krFQ;#aRXDDh$aL-U0M@m7U_MF?;h2WKe zbFu!1l~}s_D}?lRtTb_|WR~CN=<(18M_8V3XR z`;KroftztN^F!zWiG?{jS%<*86&byxex*2k{Tj;lf7$HZHj*~|G0DXmrc!biaFD)G z(Ff9>W@*hgMUkfyC-~o~*A)EkLi7x!{v#`1?Rz)3Y>kB@UdPHUt1)?A2uw)&D?rx! zwadA9YwSFHxaV*6YCQo8VSK8M4<;?1p*m7G6zugcozKDgxeJgOUO&rM!oEq5=Vrpy zje>*NXI5ljPf|P#vvph4T6*;;R-ZeI%v36v)3{KfiYNPYK=0nKLC>H3zKsV>?3y@q zgOx(Z!@TA~-)At)-4J^Eyc++9qfmVNJh$JRuxR@;@ozD~G-w!>Xm_%t&&Sp!Zy~)b69L`opJ=Re zKNn2(v4coL#sUzXlm*KZYq)J)K<=S)C;vk1J$2HiMnh`V9Zz@mg-`d7pyx*YdlR7N zV=(Uax@xC|N{Gbp@E8Lx-||(jx1)|C_R?>Vi<;L$QL%S>OmT6B)SNuQ9Hck2bAQ}? zv#{oylE~A^qLW4^kK~qyM#2Ou<(i?{Q1Z=l0&isOh|MABe}~^d4+9fScz3kwk@_?8T&6DLpfgsJonr{jsUs2#`7*uhA6R9Sx zomTtb13Y@-&lRgN^TWw7qJFdrZrv=zTjO8C7l+oPF!vI7%)wpC)$52=nc4pPf*m>%K^=U$7UUtix@3yq4N zS36AiaDl{-w2)9B^uiV#y;F*lDZ5d)VzSjenE%AfSbuQ~ zXO$}1Z_vtb5?<)u1AZbOT)vtIr97K`a|4X@bH{|wYA)gC>Il5pyBE&iJ&8}3e1OAi zuOKxY*!Ih1?2SIdhNMcoKK%)F>dA+XX#)3q@F7ll{K z>&7-!^sQ|$*0UWfttsAvgS3izFY?*ZMM~>-1Q&9XwdNaBegWyh8`$TDib>Bacc>cNhIC*meyRh;&Yq(pR@t81tCNhT4$DP8v z%!)t3J>SluFNTer28-&Acn)JjVC?&I(Iuc4B4T#ot6#sv#wB|YbM;5aWJa_CxP!vuf=_r%Bnd3&!Hr zo;}%Xo8!dEOU%L+!8_OqHuMu~7;|=3qzeA0DU%my?aCo5e#eb)cEd%gj_4i-mtmOJ zZ4?XwE2VmEp_!wvXNbxE>{b?)2S$|0GBBmJ1qSpT14C1?jul8vKY%6czeLiu&r#Au zg(^pt?@#;02qdhR~>2LQNp~#WLVpk$IZ)SF(@iD#+-38F{@{L*f!8cFdh$d^z>ssUmfhSKm^?)?w zp;QHwpr0&xUf}gj@ZG$x@zh7t)fpr2Cn*hhY0LtwJ+>4IIaTLkLM&~Imj^!%(Tv61 zzJSr`wTO?Ua(--l<=PcpCOuv4$e+@u5K-U_4@Bv#Sl-LhUg03`TD^ek3h$I5878 zA>A-z@iM&HIRGx66hJ59a!9Q`w+@HCdl5y&S^Sfm5c*xFVVGYlc0Z~$oHocePm^a& zI&NNS#h-iucZU(U z6S))XZ--;gYQ6)}6xX&KKdA3SbBtt`HYsafS0YbenobIx1UeZsaY11el&XMM zPH+M*5=rpY6JOxDZ|787a_`p7B7FSV%h+_8RMDx!8)w3mRvkavt3QlA>fhP6&^(ZIn+|W+a({My-c4kNomTAu z-QIc)ZwwuQj_oEe0id-Z3fxK9j^*FYN7jv|QKUw|Fn9>Y`1#&%&C_HO=@~7K60G_E zoXFD`7A9KBcSXG)#lq9f3uTj3n{rF1eux)-o?m5(0^(+92A0o%9qTXc96n^IY87V87Q;L=^;J#>v8Mg;bTN&g2stv8p zk;|E^dBHc|sHz9Y2BGj8oh(L*pQbhEus(%@P(Lrd|oGYiu0`pu^a=px5Gu z|IZ+4GamMi<%^@jvT~3RnW5U_X*IM5UVLdGCUzO3&V3^*yoPVLE=R_ZRm{F`8t8YP zfZ?qx#s8DOQHoTWe4rkR6oypnKO^$=`t*ebfv2-WV>hzk>11#=``Gs5G5^nxt97Hq zTuZ?(Uwwgf=fc#BcHCV?V#1_($ZubLtb5V%Z75Q?Bs^?<&~-`=&I;EQV)j}ihZx|; ze^+C?ts4@vqw5`QbNNUNK3n}aV(w6!KYMLGM?5}i9!ktLx004Hxp5AcQj1i3I!0uK z$^IUYI#j)&t%XMuBcG1w?o=LYoRJZOq{9!ccy(*zCiYCQKvtAT70V zl8{;3_k>%Uk*d8G{wt81qmyqZF1(p2F1UsGyWv$P(Xe-wV(g-?@od-L+}sUOTyPc3 zcCSI+iPcTQbtCEZ6neHMFO3=!bV|WOE*rr&-(;+LZHhd7aSBwdxZsK6;mM)J3A~Y! z4L*DSdvxi}FX6wRQaSL)i;J=9N-PR;_~HXb#vSnV8_SUI{&3%VRN76|7VBAtj@4>e zKO5GK`FWz+nEt9DAO4{=XYuhzpJ3nBL;OUtNQ$BDhr;5i4^_|7EHNbsC@SD1cXSCw zPYm*LfPR}wrPW%f0vVHrg4{&pMI1-rnZHnS;2RX~T!fNrvpaEM^3h zLaCHPp(y1W+gFs8uw(4FqJ;e`5wnhMU}$IsV`DQ&r6#a8lEBAU0zXSz=v%l#;n)Tu zW<}EkC}&zW;#|nhzu&~_Q&iGY$)%KDws-{s`cz%Vfk;i1Vavy_W6k;f?0fTdHKmr_ z@#gU9FzH=$VnJ;q%gVs6&9CF|slQZ5xFQTxy_|;Qmsz72P?~t4N)(NF)>%)pc=2L< z_@PFvM(Q!>dLo~@Fwsg2ot|LLj5SW+=k}Y8x7IAGcA|CP&xf&i!*;~q<9*aL>prXB zGmwm&$?ZQ3g@F+gFP%be&MkI|i@s$Q9!12xGsw=4KyhIVN@VE}>6xHZmcd#gGgy+C*kv(+&t*biz*;^#mIhf78|miV zMpnj6B&J-)wfJxxy?qJCBEoSd{4~y=*p6!_cOd`9QP^i(gFGh{G6PeHOf7V3Y8n}s zu+MXeedcub-SW`g!UdkaDi!9agsGVXPQm>lz7vHTDaq`67oem#9-&FO=%!x^iAUw! zIJ!h)Yy!{3P@GN7WZ#8bj7g>{c`PW>gF^pkoI({SNcA~^c9UWK%qRR4YKhOiL%0-6 zf+j(KTi&}r6ZkXg}nq8$pa z{ezR&E;4bbW}K`gD2?#bK|P=!$S-4Wfu~4RhJ+1o;+IR;kQ{%IiCFpXVrJa~y?gaQ za2p4-Zta19z}E2bZ3`#owyZxn;MQ$byMKyONZoDZJ4rV!A@Vv%DmQ zbprR0lOKiKsaJ6<@&e99-9f~y^T@ij7fx{}p~%faiIW$TDSfSqewm#s?t~skHfMMh z!$|Cm9-~K8yXcWHx73Hd-vEfBZ{uci0{eH&U@MA4XmTOC8D*627a%ssGd`FeEg z#fM4VkDT>;tDaws-_KuU7ic^?B1Y#{;}AUW8z}2&1zO1z$Sjp3E#(HY01{E`;0w{2 z+h{$cd$l77Z9y^c*TW02dj4BjcIEo_ zlo>wcN^c<6N7vx@6Z;Ssy zZnEyBUcHgkc?|!AdQcS57u{0M;6h3X4nU(@6~yPQXfn%tjB>HVUU;7NK%K0%<*K~t}qH1 ztUB=DrHq+D1y^=p)yhv15l8h->An|n*ZI?(`rz?dZ=&n0DR2t*Vb^8-FHHKUR7TRm z+5nCL?(po<5uHa3Mz3LG(Y1YlbT_v`J1Y+qirr8svVyFb3U}~1pvccp?w!QU>o|Gy zB5qtb51XVbkl3($#hh8q+_UPB%*Ym3!p@+mFq&P9Md)ki1&d zc}K#AEXrOdA{{L{k`o|Fk?8M9`Rqmh`B4^K-Ucm&wjIkvsFWUvw3 z&SeMjo|8~=n^yQP8b;63t|r% z)ctoUlNTXv%ZFILe*>~Ik8{VwdJgE`dL&+*^b`iaF&~aTwpuef=n^+%CYY_G3Ea8_ zqxF!1=sSEIdUY6rE+%$RDD036d&p!(oX~TyQAR<*ZCsAMh|sH{D7tYP&XQ6no&DLc zynZ*0p41Ytq5F}NMcGefaI^45r;)WY0O%dX!?1PZ zgy$L-l;C<=Dsr;8q-yjFlCev9L&!g$%NqISY1q^#6R?^RdFn#RJ{Ph}QeGO0bENP| z?y~)2^1FEA{VCO6#M{5#gD(%AKxXO*Zr{smI64h}0j2g0k2E1mS?bvd_BnTODF4kW@vqi>sV&r^?<|E^uMqScF&f?54M0z0D;VnABUjIr8D2^#l{spJbjs} zCR-_mFz1Kp-)1mAT>Aseq*W_?PPQCNKm8xGc6m3AzM((nb?b?o-c-w5Gn5&d!O1BA zhPg?IPKseaLjmrk#UnE^5nX%qfVqw4eB!AYz=59*V&PvuBR%CHJ0gOk%LvTx(Gx{e zK82ip7F}X^JB(Rkr;(kh`_#e%0^7p9NA1F1A4TNHe!>sC*Wz;I4$iosL|t8&_5SuV zuc6nRsW338#_G{A`s_|6!FOkZ-(}cngaq_PH+>_Ro7*#$az{ZvyQrDftSrlABh(0- zh`x>ZJJ%496A7h9JJv~YaTT>fnjVQ`x2~}fLjlSpu9z@&YPCrTtAaX2>$V+Waq|i; zBxXQSmW}+}J4n;FLK{;ni0mq@M%4+{S*f_3nuCHoZZ$dmsASbpk1kmAjTTO+36ZCt zhZK@RC+O^wiAr}h4OzG=__GB+q3eL^7d`y-@(=jw*glk%GAoXFK`eBf@gFp<$gD42LX&tC{oAu)H1+rYE#!Imh~4})mYqC^jI^Vy zn^!<;*%dE!9fT)8_yXQtylZW}>KYPUTNg8U^zMpw{l>swVSoTr1EeymJ1?)86ID*^ zGjHNVOe~V4t|Bm>8BXr)n5F&j`K7Xg9Gt%$!CB!7Ju6I_@&t^G>ekuOoP&v57Zk+a zL{tKWNtYlsEfMzm#c&K9#x6eHcAg@)wkW=L43V*w!YOG2ES0s2*1XO|zM*G=zK9_5 z)N={>X~>S?j8sIz{{a^Y9c^w)rMqDp90Y!yeh?h<=HTQy% zC7-Y4zl5?xhGT2rz>jCPsk43B*bc&nlb*zcFPFm3t(rtlO%N&js9k4`#rQA2!+Xyy zz@kCp(4p;A7@P46k*K8KwObppc=sA?_-QU=N4{qr08J>@fO}^)Lf}&hmzLxpJ*+(a zNL}y>w8HRSQxFiu8)%e%eAAV1$PX>!_H_w$ZUbC{AvLFX6Ase90j!aCgT5^N=rr8X zdEeepkta{0kSCrdT*;EB3{;%J+t~zQ(tGc7^--&W99KWQz7%mu`}qSRb4(BLMP4WE zS$?H4w{YOt*KA9{4V|a9YlA6EDBFT&putChoaR}0tn9o|DlN~z*BFS3G9fGGOC;(N zD=T{_y#Heby36D^_vdTa8@63lmQoCV*Kzpf!~db@OHb+C_pT|(of9}P5Q9GZ81F6o z8jA)DM@J_1`bK=VND7Gzz40$T+q(&;|9KImXa44nSA~Iv6&#FtUw&Cx4sxRE??&u2 zV+fw`@`kM=KY3f6ABL6TVJL~KU&ql9l-*~;%dOJlED9$l!{EUG>gk=4SQi$?^p=m;_4FP9i~i4W0IWb$zuVftI5h@q zzn;s6o5`$$k)UJy0T?^&NmyEGZfIx5e#ZHIdvmS|;!M~tC^*Q+{5KY|(j3T3$Q7nr zyey1i;I6i89(|Mw+*VvIiiGw{Dvzr(L5FNVz>y6Ij0EYR=6cd_8j zPw@VLk!a;VQGLA^=3T_rQ`_+4$vw#4@;YR>d zZ@+^nfez5q=Mx>?jX8vvlRxM@W2K>)6{ffH;c8T?pij`3H99$D>CpqEnL7Ps4N*7h zk?5BatawuM;jD31yq{NZj9UC^wG%1|_Cx>q^>@h0;#YoB8q&vGxpM9lZD5)hiQkvb zM}GcUZr{UuBxbeijzKfatI2Buy6Ad5`2;!zlLaegL)RRv-}XO9V|AbTQ6M_Mn28RB z>go`&0eaY)H)=vGU6Z`=J^X%YKX1V^fgj)DNxbvZPq25W<|b(7xCc97$Y-Bm(M#{* zt$t(R;xU?iZ#J-379lQr8^oAKA#!?A-jC~2g zt@#}9bkF^B{WfHckg-x6@+53*)e%1Q!&7)5iM)`4m^4$T)_i?MUa;cnLs#_0tK+HW z^d8N6cy*8V0K0!agYA(NN=k9tB6JAqjG}=ragS*l>Ah=M{molQ&pgEK+c^)#oM1mZ z{_SjTU(o;$dcxjWHH0h(!#}@#48v?4FGNytCtvreGYky9VQT$oSg*Aqf6v$W z{`_IcWfV0hM&H(t;e~I%f>q;s>U4r{Z-0#a>T4{XH5X$;#>2pbU!6`aoMWLI@a@)> z$l3Nj6f)K7UWTE8CG-t>%R45Q7GpIG-@a||cwk#D=MRyac@o>gPN67+<~?m3``AvcRJ zb4SaYN4j`vCHtbutxH(-{X(Q?P$(l!?DoTR!7g}V&HGxLfTx-Nq3=D7*+CxaLJ84{ z`*8CQ_MWBX7rHKx6y+nM_#qQvTEN`G4GK?n^_)kV;^-^*>i8bW3TS~36C9^8SorI= zu%Mvg{|I!e2aoNASHJ!qiw2H{D_QkyG)Hq^87T*`^!Q$cZ+aEQiF^iBk%<+Ij7d6x zf`%4HRuS^*Jd6+UVK2^yC||yH5{1XUaXnwGGVWSHsq6n-#yB`O4e}>X1cCO4^U}nY-bL1NG=@}Za5!CRR@buzWzETR2 z-)_c07RJ}3&=r!lLz$*kayyetgL*Xj!a8@^)JN&)-_VDo?vjQeWPVe8AZNvQ_8))= zAI_;U9DLvRtB{*>f!n4$@#FpcAUD>m*+T^~R{i-EGSYd0r;x8_I=8~BKP-i%z4q@w z8YOh;WrZo@pM|G8O;xZitt=5MLodQ+&vI^G7Z|4BZItlIcGd>W63}i(+7u|*xdfrn zR4am=YvW)neEBo@wEhp7@YT>}LVvvS*|&JR&v33l2tjid$F6L_?>l~iI5P$omb^Px zp+UI|8r2og2DMVVjFJ-$;?$*0?7ek2f;M2+{bYaN`zK&23mb(}(#0V}+0>wtnxn3F zA$=$kbNY}}^@)Zc6j4K>&+&M_DR6M8{$-Qn3$XqsEkxoIBH25Jps)`ms?;3j=XYb} zKi@NfKfvu9ng-(8&TTROr!QgSth*bKk_e8S(-klG?FXq9jo84&P5*lO6r@*=ar@dv zFDD+wg}M!&q>kI)oIH7tHpLMq@ayGEOh8iEmo~?op2N_2B<195fv~VM#mJ?f;lp8L z;Nm`-9bvk_?)%8woAJxF^Kh`|>_U|4X;$kx&V2*@0^4yBbTooKd?O690~9u`JM;rb z!pcTn)P%k$WdRUeHl+U51eR-_`iMMzIC9lcuS65BG&bUXtpLyd7`b3}wZ3>_&!@j5 zA(b+45@HPXbke9V-tz2r{JME1(o=bXr((d*cJ7MTR(=UPC;HgBh4R)*7uDsku5RwSpCvVAG!680>m<`#llIhtT>@3C-j<`*E+ zT*+@YZlU-t<+0Zd`o3Kd>_kQJ)XpXfXNL^HE33YSt)n{d zM@?onkey4HoOZD^1EziX5e5hKVeiFHMqRwMAK9BehDd`KcU9yRQuw59CwtA!9heC~ z1H4BU`LUmI^kx_nF*!FFLD+lxda9Tv_sc^!8zzdMwONl+cEB_kjA!^_>fa4Cn0f}Hc%a4ig`Nwi)-JLFSY zDjnl(ErLNKRqD|NrH6mS?O48yqP_D#jOsapS!{K=mHxZ584`>!&n(64u7jaZWeOQo zq0PWdZSiUh_aFy67ScmKAC{MO2AjjqqNMP_Rk+%~*x3smE4yq+KWP|4^O+>_wIkX+F<$j(3$-DjlQnw2^etZPH4cWRK`_x%!pwyX zT+I{m;xm{UNcS7>RlX8&g57Ul>m=m?`&t+f)DZ@Tm4Xmxi9NY&s8=IDea#}J0qP?1 z_q%E+!y5Mi$8;Elk+W69qH5T`d>dj?1&dV=qdXiSvuxN8E4^|A8+Wb2y;z}csUCXx zPQcQ?m&41WY8OpK^x3DMxO4$}dwF8}+0*!Q!!rEy^%-WR0nu@6*Ob?mM& zHuZp+m<*Li6UDI~*!95&4!F8^M_}*A>XZw;1s*?tJ^0ODj0K~fN3dM;R=fZiVtuC0 zgNHjGJFh59!pg84D2Ue0B&^bZ1gvb-5m$7jkZCU@EF-_X)?GHW6?uA|5F1au5V>m9 zv4}%hk8G?CqI{6jGT`Ew6DX$Q!3?QoXE?NKSk4%e8|Sfg&+oW>mnLt>Y!soL$9R0V z{(JcPK3w1hyuBot{qlPV@(98HbBD0$;419-bPcw?`aXVN`XUZzoIsSc1U~r&7+~28 zuYdeKp8M@x*pb7ko(z7h172(&1OwyN+t(BrcZJWspfjsAW7F{eWZ_3HD~ zrB=G!e`Z$foFB^X3COGR# z|8bsJ@b(9Acj$(r7xrND-BY-1n#>L9x|Dii>d?3F>@Ppyv9F$jZ!6;lTW;jGnXvSA z^b4doTKWl;xDt60(*0j?k86@5xo$GYxc*4%1JN!nyz@^}A zp?;8nxHJ88<0Ff;N~ifRqo+UB85XhsO0nx^I7-j`!#}7+=77Qxv4+J@MA(`QNL9tgi}Wlo%FO`^z53rBMe7zL zG>Rr!c>%Mv?2q67U4@`F4~9wB2AcR8JkbYVEdCfa7M*e7%vRiuQNpLp8q-$1hv73? zacK(3y`w28;%8$2QLu3!uNNhAx`Xx6sgTCtACvlyf`tw5RxZxJihob;MX3%}!s@wnf~8Fx)gGst zl=FbBdGd^qXwfmwXx0X`5qWwJ^)@tfLERB~qSXrBWBg+^x@rp9Iox&i63R;XoY59m z9Z=S~{$Z1**G^;YwX5u|<15PRNj$Ogk2UDfp?(4{K;L2TWM6#$#s6Sy-2+!bH)BIW zCbqn>6iO{kB941|ET#oGLnPsS*mv%ph4|EJZd+57=u+gxhVV2{K+lW@dyf|K^BZtJ ziWaXaU}4=A!9iWQLf9=dAKE`T9@7F@$E-?Vc0J}ea`$}0ZEF*8+o5oEsI=gd4Caa= zPkP#@B2PmJngym0L?4Wg|6_PNcS7IEe0WlIoZEd17ZS@?Tn0HhAirJxs|8)y^C$AN z&vDzdu;{gyzeWFk4RDZGfGql?G~C3 z>Rr@S|r+N3kmO5(*P}7o4`x*E7UmM@xuk)fW$GDjCc)uSp_LaeB4U;(GD0 z7pC5YJ^;-E3;uX7);Pz-*uajkR8PM?2wA{a|J}%L4qmiqrQ2YiO1YNnOej~hm#4MM zD7^XJAdQw(BbUtJ$$t29(L!`?Jr+?ByYb!S>-hDlPpS;BY$(HD=!Hq%F6>P6BaxW9 zry#zxhTGNzC34-R88$SsgH)oK4!0>$l5iWlVl&tUT?`{>Fb3G#!PKI~jBk$k_YJ_a zZCa~i^ixxg;KsghwH{LGXyXSX<4V>%`7}vW$wNXHlh*w54-;Mx6h2Q45#nHn0;h&eR(aa6r^y^ZfrH)39!K9@z#1y=kh0@66-k(_wt&!IEk9L0-n!&(#G|sT2x_M=XcDvJB@k zqfsi0;wD^sIUAz&nEzOD@@7EXrZre|D;4kWS&x;Uy^4!RQ=q73iHNO>DJFL60}C5I z1#)r0Rctx90SZ+hhc+XB+=9!AG+b1|)VwW%tbH}=9&AA~loVtlFOLe=-^J1E>#_W+*ARB1`U2sdo_`7x z1HG8ak;Vd7qiH3q_Ub&)>ev2`wvb3@&Xt4omE7Ng$ZO3n|1gmk{PBXwt3P2t+rBkM z)nsIpVf)>9_9DEQZDSRToF3D;?S@bk&;>8_9SJ>s@^2|{WcHzpQ3{=KPRA6WMTKkQ&_#|LuPg9ZlX~W+)Dt~zWX88 zpFYXeq@)qb41Y&t449>QM#C`H*PSJACKhRDyxB-7!*=2NJ>FF#HE$0;k51h1wuPpL zo>&i3)B6k>Wj@x0?!>h{7pwHo({C9x^Hp>Tph;NLkqrDD6${y|Q{29$F?0(=dq=f1 zgrK3v{UWdNORTzklHRDI$P0_0I0m`7!=wi7NKP!muGlp8BD|}{!6pDXJtlD5nu2P= z4w*V1wsu3feTp$(bLkRRKmQ(=HL^KCsfxdN@(rv!vyOfKSoUiepi6L1=nZ{S^^Ari z)nTb3sh$KHs>$3WC{DVFl;kLG+s)A(o|C3>+buLb^z`&lRP>;MCo8y)KmPp=acs0) z6|VlanAoZvOw9RJ+6nQ;5r6e>t%OvP*tRh-hhDi~o*GrlLLA9UHp%lLEd zS6W%Tq)DP~mtw)Bx%m71TCTt`6`u$W7>B++XQ0SVbN(P{r6H_#vDDXHhoW_q3S244 zWY^S9b_p9}w2L(y+{oT(p(!F)h>?`Qdk*gB+P$N=^3&(l76uA_;YD=u;@9se%W|;o zP9{n&Z07bg4f*AzW*(|N4q>j7ZXOazjSq&adqFy0QRLMHJFRVlVbit)jL2JG1+o%$ zQm>ywVLqR$hDxK^x8<#EZIC@^7GCVqg{$RCmbQkAcT0ip z#(?hO>}=rr(I}i*xeX^i{|<*1F2?S+-@>Z7FXQ8x&tcZMC-MHSzq!bosvtMa$}8cx z9bUC_-qKov(Om|^$dvz$lj4sd=FUN_So6GJUR@fK3};GeLpP})@*4NcS10oHMl^h& zyOu;=9Xjdi=!t-#-MLJPRgoNDh&>7HS5ffs*-n<4EAdwyC6g9oT<5M_$v+}FVJ}u5 z-itH)%2&rW6^{OM1Auf@!GzjOH_3UYZ@jh?;>oO(^< zwzUoU=ZE-rV^%RAaYM<6{=9k6`+t^1|EL?>Uyz0H5C_8uf~bT9PwP^HOM7Gmq|B$R|5;P*9%k)j&b3?FUu32jo1@Nm=bwS3!XYiP} z1B}i13h7iRd)b-uIJ#bY3#A?g3H;{&?Z*7ozacGopXw+Zqd5=7vt7C&eaIZu5$zy0 zXI6u#njcKV$jTm#&>F>^Lr6~I*LaXW-mdkdmmj~W5pyX9OBTO{RcAM_5fpDEY8tuh z9`;;3fx<%7f%~AWuoGv!p|04Lo_rGVH-yrnnnU5;7dCe4M2%!W+#g|6l`hwt>O`I_ zcj{TFhfzoG8yY#n#K{c?^f9X7c6cgc@-kI>WcBMlSyj%M#TXGBz)l?>rWAAU05<>i zGt$!Wq|RP}5|9PDP%E3l&A)pEb@2Z)V{NyCB+qkP@b$ zkvsMxKToylRAho~ttzeV(g|forts-I2VS14h#qADc0?p%(_725mfxm2PH(-2cbEN$ zjP!%5BY>rCKg{pa38`b=QXSDQrBWvHV)_}ikB)1GYXxPA3}>=SxEq`Zwlar>E!_$& zXal)SjxArlivwZo9#NfhZIYD{jr1$ki#&yfPk;48NDTN@ut^E$kaOc-Zd+53U%r!V zrMWVi@F9^GB5ZUf^7N);0@6@|!@|@KUY)BJ?!saqGxj=UMfbRUQ}Z@Zx>F5P-BZx2 zBcATi0R|>irI2Rok7HNjErcD@ZgGH$q$C#Ml@oL%u3{(k#8WZcx9VN+M4k6*#BVK*Qz(PgN!0v9q%aqm38lDR63 znA|@8>Icx%=Mxb|-?@tX>+88~O<`7FEU7p!>FTaC{1->=xTPj5nRC|L5`8`832 ze{`c zM=SB+hIQC@BL`R4RUb{#n2_i6z(2ntIq@ieK!>Cxry=WHxeK)_eA`%}i>Io{zAOrR zG7C|9hfkZX2}EXM3Bc*a#jLW-z2jt04Y- z7&5bjNfQx5>~t5OT^*SNXQQ(Z@3JA1lL~P3!VbvtwI@b*Zx`Z+ZJW4i@Px$B6OVN4;pPbA{WMte_?NyBlGjV6UOx8RfJQ5jo2Zh)E zUYgOdQau+57)Z=_pQ(M^iRUK(SBA=9<#(GG8!pg$J7D{^;Zo9Ft z*trBdL$9fZ-ePoU+ZiJU&P1F3Rfm8!6gj(IOPOJiEa!9f5U#Ff$n)1;a< z?O|3lpE9DcNJ~pV{=J8)sS*7p4{ z+06%opV4gLl8P9xuF-~#zKrGNZjrFCn=lIdN43{@7??P*j+ZYjs7*@bvPQJc8irC? zh8wJtqQWgi2YY+y)1A;rBwmllg%xXX=F1;(;*)Q1WYK3h_}&LNw)hKNSh)#zE+-?G z-EtLEb|l0U;4RTU=qe1#4-p$Hiwi2I8_(+&udik`nox6C$xKCIvcU;{VZV|;pDPE@xM1-K}_6k)vJ}0laqkF+k7BEV{_-=dECirT!AMPihLBy9-h6BF6S99 zy~61Tk(3yQ?5uETB2AN{tvarSAkh#kc~UBk-ANTip0?@5sTZj%@**)@jV0CQkk83c zpj4Grk+3x?YqUrb+nxWz{_D(wSEV}k_v?q@GhTqbgO(MZ!TLfBdn^R4o%*ScuulK# zajjN?uMNfiUEyFyKc&X5n<^<&GD}#|yvi>MC=)`Fmdg9G$scduR(Cm72h^%wqN9l%dm`0tN6 zdwV_mg&&+uEG~#bYJPmRMKg3sW^@cPGqqcbxmG9(QB+pDXfXfI&TzHnuLrp-2FG*d zC`fL2Y`>wspK6PP)RlCX{*tk+_M$YD`z`s309kAq=^I$^)bsdY z&mV|S*vZ7F?vYVg%oF@Yy4ioD>ec_4gJPht)mY4z@xoWyj<%XkYy zPhthbMoKxGesU$EV^oVA#D*9X7=Vdi&WFB6PuatnFe?C4x;@RV2|AVJ|GS7^&R>9O zc;keO5jJuoW}&K+B54JANiM`GH&lDgizHbVvyl0TxHh(CFf!KeNR+JV<6r%R7v6gV zCnNsh@}z5%xQrqsoG71=Z7fQPGMUk+;~TOXk_E%G7L0x6DHs}45hoxk{Q}~1qfpX7 znJY7|l)#ZLI65)ZXj@|!V8s`rZDG=tEO~Vhr$k~0sZI4(pgcDdWpch(l;~SPU#a^a z(dNL<*m(V}dX=fO;~+ft((ACX(%8AGH8kQ`@XI^s;ysq_@mE4({BE2)@EasWQxR&C!r$Y zqz|Q0EBStrr#GqS8{#lDwuOaz^;V!fF9&j!$m<(QVW4A?H_T4O?(p--&Ezd*iJ=GP z_w9>5V=DQ)DiIl@L}++1SLmd+*0P<-5hw-xlgJMWm?~$dNzZ+JVnmq7BmHatH}T!s(@RN^`E|bgH3vgZ@ogvx%B`)78cZKxx?S)BS zyvX^|s-w7Agb(L@fenkdBEJ?P#5wuE`B3)b)>7n&p%YxNVEi~pE%{Y*(e~?W}Z7ypx5EU%ZXgXHIaX z>~slTdzbR_(b(yQjLq%XeaX8q8yovNhTCjov{O zMV_F2YV_^7H=^)KBYhLJ>QaMsQ&bFvDw?IVBopGwJ(O0FUOtT9uisV2H=9}X#M5(M zhr>fg+%Aqlpsg`J-1s;Ced8C%YM6t`%mKFldD>a^E%>r|Qx$7R5#U2*kq76drvvA6b)d zD+7mrd?_(O7pO0n6 z?X^Ry{kkJkvi4vUqQ*_6itixwB&5*PxHl7tM6fnAgrz#c!Gnm54b_&syd(n(rEX^N zMM?z{ul|G7WYt0skrcB+dSKw>>fJN~R_yz996gZ>Ncj5nNvwNqC8|n?o|*yt`_UGh zy7&{MCf4w(V@e)FHZGd^?FSg$sS89BenC=d>VBO0O}8nB32TWx%q$;%ZW=;CaUQ^> zl4u^#Po7hjiBcJFB=|`skQkA-QgcwbxzJS~VO@9P6LF<6zY~ z5Dt#oo%5&-Mkd}cHL81hlfnJ_b171Y)TB@-?lwT=sfoyO!olrUXc|Khd3sUW7DT?n zJBwgvY5=_&jB|adITUi$l6GYt@=EIF>#8s2=XPNKolN$g{Nf5<*MS)K{sJb(+-{9f zO#9%;zId)}0OI4W;)e^Tn3!)=J<5~LM)3bG_yuQDcfqb$4^#d(pZzGR2kU^FmnmjU zeFp8?kbj;|q!OoZ??K7h&$!2Rh1{_%?Cq%tjrJ%i%7&a>{LO_DQ5mk&^~$axB@EDx z5|(HXT9JD3=QY?6mxGLy`U`?4$jiBh!sN#F%X@Y6Lti`AiRvI>UQL*jTQ0b}YOnUR znKBZt4!k>$Ru=DyOGT0L!L<@%o0XFn7#k1h=id6-J|zUi0RoN81qGxV8)5?A(RDzg<9XJ`kG- z{Po6<*b#RDHmnbs@cJUyI@N!|XxKB|@T7kr+`UI}`xFKA*O|T0lj+X2L4J8pSM^1u z6^iojAw`kgZ2aB zZhdVqsZCF~cnrj~n_Kb8cR%35u_XLB|643Sb`Sw3N{pELCW5;+Y#|Yi0H%LC4@3Nn zA(rsgbWuSlV*gskZR-N^%LlL_jGn&s(m+!gx2pwW6vtDR!KM#op3KhXXCsYG9bo3I zxuj&Y)+TA!PdImjoeO1CD5#>qxwJeK)NY}t*6g%>R>02DHWno~S7D^ll`PG>)R#Ij)Nli%ntjkkv#cb~ez{ z<3o#xw0!O4Yp;m(?7v9QU5l ze82ZAbaJuA=%+tG$Z-E^^XS(FGjn}(aP9(AGv231>^a4aJkg?YLQm3$bnIr&JvSDuQvF>vsA z#7Jj0jBX^CjiE^}S_gHkx1zS0IvS3uDY=qo`E-R*S;W42AiT+_tx`6LM}|uCX|8O&GJUH~q`^ zF})qR;s4x@;#!eX3Yb08Ac89qJ!^^Gxe3^w;Zc53^glby4p~R(=)+fF)((j=# zr*V_A)s|v;4vLES9P+j%#;|m$RLG%0#2q?}bCKi&Z%hYCWpZ*7k#V{4SH1=gei9a{ za75iALXXx#2zqR)YQOG?^(7eQP8KDv%XgDfp}1c6ET8nRMS5y&*V;vLzsQr%h2opk ziY=n3cS+4Y5Mwh#=TyFdN;Ob?LNl(tj z#O|%Qo}S{ESM1(`%R4Wtp3!hpQ%dpOAAhS$0}|Z=MyR%QLtdvL@a2-VY0YpzQGNt& z>lLCzQU3`kT|h(Z0+9?Q#r%lS$J_u0b#(Vt2f1o)Up|2R?8X<=iWKEXAwMQUwbyvG z9^4620)m)QXy}C35;LnF7}=^l8&x-KXx^bq4`vARk!m@aS0Rc%#ckIG?he#J+Ezj2 z>7D3X3qETOIAJ+c60z22maFekO>AFhTO}}>jmpB)7T6Ki=ss=u2|KdW`o^GH5 z=Rs^_CNWUwpVK%Zl;B395{es#RY#g8)CI+rD)>|wVgm$NYR-L-#|CIulBl?ZZX(TU zjj{lzA{&3cgcd3ee(7!4+GwxF!vnD2xKEH90%p!q9jHG6lSaeNO3E0j&hW%uyg<;&Rl-j670 zfC1Tgs6SqL|9#94X$@chp=^W@h7*4;#ktTjF5;#>P%^4bOZH+#=#}zBR3Z~h?(sPH zJ#>wcxi=ds3yV6mLlWz4m9dSVE2SosvV7dj=kH93#1&FQ&4o|q5=41}%o- zrB}ODCGsTj>amPSkLrWgEJP-zU^hs4bd3%^H%I=+xft7qSpupF9Lfdq&B+tk_eaAD zB#S zwXa)>gNDG-QM;A9vNIx(QJPS%r~_T0h>t{8mMXGF>If5a&CONFN`TCQdR#C%AT{?M zikm{P4`akzFJMUU6KqSnmZ70Z8$8+C4c*^fD`B8XL~hqaB@zpd+a>?2l_nG1J3ox2yk*=JbqaHJ+9o?!A>xLPFx&@q07r(aNCVRzDFB$ zw4r`ndp8#4(La-`L{WN@>PXW=oEd{Wm9=eXD20)I!@^`MLW`heB~3F~o4hm=MU7va ziG$`qUix||0$r(KwstTy4a5`O+hXW~Pc$m3e&ff$+L8~sC71Ep>)~~B&QVykvc8ju zb)e#1E(m?4^B@xG!Pu$q6-T39}Q{Nf{=2;a+oP5!Q;WMz*}o6E(lHzr<&HqbZJ zZYbrQ=v0Vr>NZ5QE{OAzP*|ABZA;jQ)xuMAE-b$GY7_8@RDnW~hun;&lV8Z54WPdM zYZW?pKF)4n&7~n$LIMYuVR*eqKTMdr2qu75$#}7I2n>w*OqpdR@%aAYIc#3C zTL;q`Rg;iZhF@QM2RlO#sdL;LnYP2+exs4!uDm>8W1(>D081O~O}-MDsaGJ&XuMo4 z52LIo6D2bKI@Q;gz$(C>+tvhBuv)2TtgG)KC<~Ak!$%Y~6)Y{q`1YTlFn{_A7@Kxb z9jzxKDJ*RIU|h$k`1tuZF#3N>VeRdrdQfwing9cO^iU`L%gzahvJMH$IFZ*up0BDb zc{P-`21-4cSk^o-LU~{uOz(@pNz?5xJ!`_dn9;r~>y&s4IXB}JzS^)J7mw=xVou8d zetz~HtO-2=Dx^UeN!#JozQd6}<`r(gvB~Q^9FDe{&C_x}G=$xsl*8=VrfJcZ$%~+n z^A@dvf*CUwnzJJHIq#+Rt+e_fLVjwpYP;!R$RuRiTa)nTpL_7!;P>HbJ50SgyhcPu zY*gw8OWVF^<394MDV~F(17m^s{r>eGe0~HRpp1cTM0|&D`{%b|t-Gd(&e}a@W z-Q8@IwQ}XNA7I7#qg>Hn!bsW{FL&#W+)26(m#hi7u{rwM7_gB}g}D?h5Sf?)`K1OH zSI`n=Y-p_%+-PD+jY;_G3i+DF;)X|YXakYh5~6&5blKD(;c@krV%GoO#=i$QV)K?T z%%8jrLxN@^&}{;IoyQ``eF8f9Oh(UusTd#f7#0nE2LGG(BEETd36`z?9h1KL4k6D? z=fX6#1-&+1FYP*6A=plx1qK)5YoBADf)bQ8>j?<+r-fcM#g-PR>p1~Npa)E2p6>JfoFR6=iDPiY4L4*efAVKe!fpN8Kxb`g8%3B z<@nz|xWr0n-*VAF(Sb7qZ-@7fzU4Qhq;~ z8W?kKM@?a7DuRQ#b_=xB7eixf7`SRK5!-)}iir^IS_Q-0im(4#lz$UtS8EqaNsC>T z<*UN!V$_KxPgLp%DS?iu&BJ}SHm!UimGYfIX-Pg5CA!VYQjv_o&tpcrZtTPJ6UJF- zN3m?n-}raQUM_B3)1)x{+W6it`0UVb6y%&|?`s0T){`*5Pk$6X_B9j_SuxyL6b5#M zkL|-pX$|FGVgf`L*QxfJ4mq?8S%s;F3x!y)Tfy62^OJp=z}nma)I-w-NZE&-zk z!Od0W#ZVUG?9K36rP@}o|CHs?ID*J)TS2Snc8jWtI$VZ*|IxX6c!(0(5KxU{16Qm733HpXK2AJ=f=U^>Dh zspvP5kit5TY;g5ph1G)em(nO~`(!P?*te4_Bu*1RL%WW_gaOl#Hu5$8iA|V(4s4hy z(yq|wWrrdz>qe8ChRi7~AKIBcWL0-vA=gj8p<~q5z?u^69i*_e)u3bXWS8C>FJOu9v;6fVWV56@`1ZT<7VhX2dEEdhA#{ z`SMK6e0wg&&lrpTy?SCm&yg6`YdRk5|11_wT!7zS`UU?kJ%nS2GqCH+ZTMp6CS<0a zfrrmHyw;}|+D!Tod7Y^WRMSV#p$(*#+9_PEMp=Zz$uh`pH*V><`^lG=U)bj4%+)a7 zy+dGU-buCBG@xje4s2v@t=$FBk2Hn`yf0pW_FawbVQSun+t1Fq3xq#Bl(Hp)3UmKC9@Tb;Niqv4wFUv)~j_SPDM3KE09veAF9rZ);^LzH~$I9?z zd~)n8US0Vu-q`Rz{BYuDoQwDeiHW>5PA(Tpkx*Q4m227A#}J*k11BTb;pgMuujPkyNb3S8F4N-7=eb32O#(3+-Fn+0WX@Bn11;1C z>3vyV=y!JQKbRQ`g8D0P>Q2l<-IL;?O4=g`yztL7CiK+xe!Ndn?Y#rIT_QtzlPVweTg@o{1b1_+ljZI zUW1p$EWxmLbK&hc8m4Al)T`U>hbDKIZut?XzMBKveJdb((AU;v@v?H&dWr+Ok}lm% zN7?zx#nKxSS!wx&EjH4;B~SRYbHLcZcI?}0cRfOFu(s`qKD|b2G>YiI291F3x8w;& zA1k<5NYWUUl>*tr7CIGGTB|@$rVCOKdaa6l`HQnHMJ)?Ia3XK%025Pw$U@<1lA^{- zwJN=P8K+LH!H&a6IM+)#BF?hv(0VdHA2T1z-~1nT?c0ovM}Nbzogd+y)h}SdvhjH9 zqkeenn~C`7&$;;P=o0KcwGLZ%9>DhtR^ipbZ^FxYv^oQw0L8|KhHt=^dp9Cy^<3^V zGzSX3dcwxeN42LtGE&YVBkqi9ukk6ZAo2=H%}My`3%M}|3>X6krva)Xjf6;wu|Dny zo;F%_poRKC33;hx=>5gL2AZFYGh{@!YI9Q@?)4LMq=yVU*qJZ&1@8+TE5MOs@C=cFfL>| zzJB}#tlzN`&;I^4X1vi0!2x0}37LrzjYjwwSei_sj@f_>#c1D^8SvdZ>*2|F`(n|? zMcB_of5j_bVnpzhu%N;RqVnjBf}9KZ>+E)H->?j3m-efUHeCwZ_JOy9=G_$!pv=Rz zm~0fJ#HtQ7G8L}&5-o{5(Qf)Myx6%b8!c&f;aN@idJn;fC!bO6wNPKotFYn;5`JeJ zCL$^wpe(EzH%@Td)lpJZsiHcGJB8#_6nc$Noh8q`0j0rIDoblM@k-bRwT7hy|0TKN z$c>O@);K*D+kX8Rx1zT)o4tfPCNc2F;E?h7{K?t)WzR2o^6Mwy;;dI`K|@_2ADk2G zv>*FmIDR|w16F%M`-kv~*Nn{^$Q6+Il1XCiM+=Ay%j}X5cf(VuPCGZ9<=vxJUV#7&W!(7 zMTM!**Kz(DN^iVt#X{V=x0UVDhZe!saWG!#-5-lT{|t|PI2I2{jbCS|D%P-x?)Y^3 zQvCAfy9jALrIO!1C-Vfp+xaJ~c6_Ni(ln9S1j0<)p_Gi$;yc)toB&yFmg-33VOU|@ zEi2M)>Rn@Yos4|UDk*d>*ZP*v41KyKuK{t z407sOFRz}Q|N0f&ytjpIvCklqqFwM5EF3fh3pOr8yRNm!X4e2{R50e1f%tdl_jqsc zbl5r3LM*<+%gZ{0Z;$MS&8CmJ{iaA>=P_`!*Ud8gn=x^~nO{|VjfbJ6{PHhM)4prb z#>o`ZpId;T-G-{mR%#P^k5~Fm#_0E7QXOfbA=H$NSIh>5nT3_t24xqeptPEx53=y@ z<*AFxkf@X5Q$isUd9voUYRS`!my5ij1bLaY3zea2N)}2ZNc5fAKP*6WvZ|h`4k-Kc zeVmKl&9+G3jnKKnD9j%_3A28jQ*XCSeXw;h!D|~n#z#}1fUnPZc7%5c0j76S+=@AfaD=a4Rg&HqF$#Zq}YKH02AgXcL+%IdL-$@)N66 zM;eEUB41QkDbhi!I0rgm{L*DuG-d|8J;!M@^??!waB~=mFJE1b$zQyuI@&@*q1gDE zvXsi1YGxExnn>mJ==SZZi{_NXMc2`pH@4u zrf3ussU1vIrS0PLwV%JnC_5SdoH@cpw9wZH@E?meCrrkeSBG=GZ(~6dF%w?xhsE>f zp?ABn>Q%Sox>>d3FPKJ!a{En*f`CD=vC&)!yqd^JJAu%otL&mvUurtXlzL+oMU|~8 ztwSMqJztrL#qWNBc|9k<-g%hX7^oLw1DKk3!DAs$W6AR0&}CY0)dMYP0*i;FcPeA* znO*vz2mWd*GiIvLA#r*nie+k%Cqb_$@TA2W7kPqSoP0xq<6lJya%1CF+ciU?YGUsQ zb5%{)oZ@U463ATb(9nsoOvvn}5MNxtyw^C4YX07Ug?r}3NzjKpv^EG!@)e^cy z{kphf%2&&=aLfce)9rCMIu2)}sUY@Qs+r-dh)Bw%)DG~NfVYOci6!r^!R%#U!Q0x`xNK*t9lH28l8JPLqxA<}8T6{k9J-j#cbqoxA3L)N8(8hBjd_Bg&%WVwW zdQZfNc8_Czzqwd4?iGCf>Qa3C;~yBY_#Jc}>{jJ!%twXHOyKm|(>T6-J+gEdoMpxY z{$Xz4xX@^17d$BDd3lLY-gwY2uPn%@xX_etfr|ckY8pfM=+tuvRy_HJ)Js*V&}?36 z?L#5``?Z6U$`~vyiH8vzmS_V?8*uyDW^UL|_iR- zUO731mDUiE=29b}*P10yJqf)yy$RVfRKJSrW)w_J^RjE-ZCY9Bp_N#V_dzQQ5FcN= zQdo7x^3+Bgie#M+8)?vDr#GfgLaR2qi$AU_G`e`~^BH)xM=$R45|rp{Rp=#{U)aO# zHw7p~vWKJYDjMZvoWhkWyJ}oCqBR@>U}MXtrpV2_f*ad5bK6xxcU>{N4YQfCmYPN@ zy4lV0;OJaqa4X1-+5Kb**xB$!NNB$?0tuQ5MG+^MQFoCGU?@(~j`4UuG})@oJ5~*p z=Pp0t4oZruSjnv{Mp$xXcQ;+@cd{xO6x2J=1SSc+V9jd`y~T?cq;0BoLVXt1 z@S|+gRL@E*7Kst!-U$O{3}m|xhbACyY(I@taj7Vir^C$H7Y@BNzYrs7-}g9oYY(#q z%h1t#FqZuBR=sWj89Sxdt`_3R%6&Nf`!4KVvmYB*?ZnOPCvbK5Ropmn8(D>>u=BKr zL3#e6dO-^*eERi9^6_i9mK4Q~l(NxIBA2JgttUH2O`t@eQY=A0%pshPzQgvksA6iA zh%Yuncc}p+E_^<2U7@tFfjs9RqVC2qV<8XMlN4~ea~rY8j^iF{F-MOh`fz!p{Y(1~ z;_Ti7IIwLuwyxiXo$GfY^-=^={bCrJnZwxP5%!lfB1O#fJhf{bF2rkMBNTf$#p_ zgzaaJVb|5eI1#lMm*Xzt@a<3>j*7&QTT$43^dwGgIe_$omtbh+0(%dedi(t;ZMn5m zJ6yUPi}Z|}+_9A0bo44KgFHZcanp6dJUJFu5_8z^r=3yQ{gf0XKq4`QXS?ALi)x5d z(=ha2iiqtRrtXK+37 zJfhPsAvx5A3Yogk1QmT|; z^S=w(<7BvVRy*}vw^^e|404;$0~&ML2=7ZTNIgfz$ye@8$X%0FTF6=GwSk5==B?YQ z>%C^lvNfJ#U36|eGSm2^{D#Ip81nKoZo4*+<-YFS^>}B-^H})rzu0ho9b%%lA}8Yn z%1UXt#h+V><)(cK%}h($kAs)jLXk;h;@{{LW+G>7lAwsS^}985s%1;qO)Zx`BqxW(q^-R|ut)6^0`*b{CT3 zc5^iY3Ue=V#o(z9K~=^T#ii3kwUXVq_wH=P%ELSH@xJ|7v2ZRfeEYNNS^o|096Ey^ zUYdg&TQr}PFZu9xB&P8?Vk`}Sd7HW!b@hQp-jufFVMLryKvHhi^Oj}Ic*`ryLP>Ir z>Il2PB12JJq+XXn7ZuIelDme!pw@iy1icx(fgtkS6-_-+o`PAKoQJ-4v>MO}7OJyD z5jG~_r?_oRAYz|o`#mOxNF&5@&=cvHVdNu#Xn z`oY0vI9xnO!_8|9Y#ax{)FPx(LaD;sP+YpW9$%a|g*QL{25VmY9wl`+FBKX8`STdk z%ANh1yqS=haRf1cf5B}xHZm(G^t0C2X^s^-w}0ITLs6z~CO-=^laaRZHT-rh8FwNm z6~=?mC{a(}jVmGPVmk~Sy`R8{&TnAmxaD~7oul~nlau)V>94S0&}fVqH5ktBBia2@ zgyi@=Sa;|kmY+L=O>e!3#Eblh?NKJ;awfKHTZO;Q9Kf6}7viTE<|95#GucMTRg)5T z0de=jxP1o;4|w!emG#sj51Vimh&gcvnduR#Jx)U@pSJoyVSX~iskc;nKz2-}blYUL zlbDf^r>^r7}LOGsL489)DrvL)|9{_X_a+tFPn73%3w^cQ?Bk3t(m2 zALD}hVp_+3=o#1*Jv$9YNZ*<0+PWKhJG-F2lM{Nnxv}rsxR82zC1UcG=;hK1r4E(z)HepU(Km1< zy+rldbVEjVI=krdU>Yz;bwu+}75$Q9KjGlnzwz(+6UfgdKWh0sMd<*XTzjBTt4?^T z(+E8I)N6SC(}kEje+r&_VH74l)ej>lc0jM*&hTsJ2#;PJ;5TRlY|Gr>l@o_jdv7E& zi?ggWp56F2a4}JaJF(HQxN!$A-Mg}mvz~swCQN=laQxc^`19OZZuDDLnuaS0Q3z!s zZygZ{W0%&D+SG5Zf&~8LCm-O~Yca^pJkO5kVO*P@=sjsT{{$^Tdg8`x$A|g+DY9nI zkN;r*HA<<%LEaEbSVNXMg>H$J>`8-sJ3`{gmx8>o|0_f$a(w^^I;lT>G4hR4(O3F+ zTAO$!1t;^HM4rk!b!3fO0$^`W39Y>){ukq-~MklESrpU<4W}QSnH|KCE zF&p>J-$FpUp0IbS+lA~A?1GGgS8ySoStv?2T4J4t3;SB8okyvj(TJGYAuBZuNeK^z ze`yP)q5!uFEzrrW74+F?Leo$o@zi4I?XcqcC)(3%IU14v-&!tcfBpSlg^-Yb!bbbzFC=Ic4 zSLEizaIUz7#OpYdScoe$t1Wf1$c6YwH`bW0mZG0mM;Q3^4wH4yJ+qRq4YXj+oum2COG;({n6(ib@ccDdwzPMOii?lkVW!$$RM6zYi>IYvIIG zK@&Q%5<6zi-(y2Mie_QMQaS5@+JvA~+PLNPOU-QHb9*<=MY9o;Qg;is3iI#5yr=}O zokrD43~Ec!`SmzY@s$5w98$Ld; zi;al*s25M)vGDclzyw|o^3oLU{>aFAT|1-hWd4r;;Tz5 zT#+kRitz2vpF>v6CAcHYoNi>{hH+<2j~f-(2EnQQa1<70WBay`_(yry_zBkN-;irU zm~_=B6-AydIU$UaYWPu<4b_AbLn%QICpUDP+@I|}9QsmsZ_98p{5tY;qfjbwM&}N_ zAUCN+*H{B)G2u9Td?V{P_;djJhEACI#MAH$sD6d!j%5e%)uAIuh@k~HVgv?_!`R+4 zVN_4|ksdou2CaIalcN*j(u$FpeU}}PAwDx2aTnt;eCi13>r;POJ3N9Nk+kmwF2?a8 zl|{uF=x1n(;=snwJz^CbiSYDd6ctocBUdXZ5uIC#p4K+dcMeb;t3NckT6}s9{yKjM z7cTBXSxEx7hQrNsC?4w=fXUqk^by-&^s{`aig!1;WCn4`v2AqML&sTHEWR z)GmnKA7XBVs#N44Co=-KlEQH)G7LwU;Gd3)!I2ARuzT-L>^pi0M?){-P*@mtUO9%7 zkw=l162^5BJYw_-9)>4B`4sv_+C6DBooH7}8*9fm|6g3-zbo~hD7bgsjJ@M)r&%?%! zn>u)aG&cD8c;Ocay-AB)l(2Ceh&jRSpxiJ2k< zcNhRYgZeM!($On8nsWWv>GI{cY@x z(QVQI1X_3^C%zcbnVIa`%|%gRG>%6_BKKk>d^+}n)IzsPe`)2vh}g0Pc}D)QaguVQ zkh*eZ%|^Vn>KEi@o#gfnO#?Bvb34pivkbka^+5Ltqu^^5h+w%GCZnLEdfTWZmMzSF>3Mkd<*0c{!1+Q%GfoQ)Ra_xoKXVJ`1iL{ZvP^hqFEJihCFo zjPlcG-{NZA71a?gV*rUfi8qNnxfrHzY63N0MF%a_Uf*+r>p6om4q&N{gdW7~k*+gRHDXCIIDm zbo3>ruyv_^bg=V_^*DK#X5`s<6G<_ql_MmBU*{jx0FuCdc&6ttu68UzD_Fljb__Sp z*RAZ~z`3I^%*UGj0Q~syc4`EqS807j<5FznihdRb+#H6kiHtvtgdHE(dnGLWyP^}T zvHZv$RTLF1O6iQJd-lX@vtPy&-_3y)ALXl6NI(Za6@uRn{(?oLo`<;=A2v%9lp90; z!0XS?M);vy+|8#e^5XB}_q%tndHs7h`SIsSxL*6~wSdXcrnf)FYpcFPZg#oAKNk{$ z$G-fGS**OI(ELTe@xFNex7YFQ=3lY={g3e3s96})ZaSP@N5RszFN~yZIhR>w8F~=v znCEtEi{MGMTaeihC|x?`s;Ld}%=SKVCtS71i8jSil33GZ5A|kh!OkttN137&d&2jr zj&MIY4UvWLN$MapF7U+tmOQBp%^p&MYr3;(wE<^l=vlWxC%-Om^{$=Eg%)q#-f;rQ zqhq;AN=Q85)N73D(fV=g=tkU0QJ++pOZ_o#{LgV8$k49?t3=DugKEDiQaGW4ES`v?*G{K?0ebr+M)te9jb02%0DYz4H_ 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 E2j`LGcmMzZ From 134a94e86d9ca21565b9a537860e2abef1d6556b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 May 2020 10:02:47 +0900 Subject: [PATCH 0702/1117] Rename enum members (no idea what a TaikoDon is) --- .../Skinning/TestSceneTaikoScroller.cs | 2 +- .../Skinning/TaikoLegacySkinTransformer.cs | 4 ++-- osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs | 4 ++-- osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs | 2 +- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.cs index e26f410b71..520961d3ce 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning { public TestSceneTaikoScroller() { - AddStep("Load scroller", () => SetContents(() => new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.TaikoScroller), _ => Empty()))); + AddStep("Load scroller", () => SetContents(() => new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.Scroller), _ => Empty()))); AddToggleStep("Toggle passing", passing => this.ChildrenOfType().ForEach(s => s.LastResult.Value = new JudgementResult(null, new Judgement()) { Type = passing ? HitResult.Perfect : HitResult.Miss })); } diff --git a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs index 1096b8db00..6e9a37eb93 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs @@ -87,13 +87,13 @@ namespace osu.Game.Rulesets.Taiko.Skinning return null; - case TaikoSkinComponents.TaikoScroller: + case TaikoSkinComponents.Scroller: if (GetTexture("taiko-slider") != null) return new LegacyTaikoScroller(); return null; - case TaikoSkinComponents.TaikoDon: + case TaikoSkinComponents.Mascot: if (GetTexture("pippidonclear0") != null) return new DrawableTaikoMascot(); diff --git a/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs b/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs index edad36f7d6..ac4fb51661 100644 --- a/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs +++ b/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Taiko TaikoExplosionMiss, TaikoExplosionGood, TaikoExplosionGreat, - TaikoScroller, - TaikoDon, + Scroller, + Mascot, } } diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs index c0a6c4582c..9b37af1111 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs @@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Taiko.UI { new BarLineGenerator(Beatmap).BarLines.ForEach(bar => Playfield.Add(bar.Major ? new DrawableBarLineMajor(bar) : new DrawableBarLine(bar))); - AddInternal(scroller = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.TaikoScroller), _ => Empty()) + AddInternal(scroller = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.Scroller), _ => Empty()) { RelativeSizeAxes = Axes.X, Depth = float.MaxValue diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index ded1fc0933..dabdfe6f44 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -127,7 +127,7 @@ namespace osu.Game.Rulesets.Taiko.UI }, } }, - mascot = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.TaikoDon), _ => Empty()) + mascot = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.Mascot), _ => Empty()) { Origin = Anchor.BottomLeft, Anchor = Anchor.TopLeft, From 149cb93e8cb314a87c7a0f7a97293ebac98372cf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 May 2020 13:18:37 +0900 Subject: [PATCH 0703/1117] Add very basic error handling when a directory cannot be enumerated --- .../UserInterfaceV2/DirectorySelector.cs | 36 ++++++++++++------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/osu.Game/Graphics/UserInterfaceV2/DirectorySelector.cs b/osu.Game/Graphics/UserInterfaceV2/DirectorySelector.cs index 59de931df5..ee428c0047 100644 --- a/osu.Game/Graphics/UserInterfaceV2/DirectorySelector.cs +++ b/osu.Game/Graphics/UserInterfaceV2/DirectorySelector.cs @@ -16,6 +16,7 @@ using osu.Framework.Platform; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osuTK; +using osuTK.Graphics; namespace osu.Game.Graphics.UserInterfaceV2 { @@ -74,22 +75,31 @@ namespace osu.Game.Graphics.UserInterfaceV2 { directoryFlow.Clear(); - if (directory.NewValue == null) + try { - var drives = DriveInfo.GetDrives(); - - foreach (var drive in drives) - directoryFlow.Add(new DirectoryPiece(drive.RootDirectory)); - } - else - { - directoryFlow.Add(new ParentDirectoryPiece(currentDirectory.Value.Parent)); - - foreach (var dir in currentDirectory.Value.GetDirectories().OrderBy(d => d.Name)) + if (directory.NewValue == null) { - if ((dir.Attributes & FileAttributes.Hidden) == 0) - directoryFlow.Add(new DirectoryPiece(dir)); + var drives = DriveInfo.GetDrives(); + + foreach (var drive in drives) + directoryFlow.Add(new DirectoryPiece(drive.RootDirectory)); } + else + { + directoryFlow.Add(new ParentDirectoryPiece(currentDirectory.Value.Parent)); + + foreach (var dir in currentDirectory.Value.GetDirectories().OrderBy(d => d.Name)) + { + if ((dir.Attributes & FileAttributes.Hidden) == 0) + directoryFlow.Add(new DirectoryPiece(dir)); + } + } + } + catch (Exception) + { + currentDirectory.Value = directory.OldValue; + + this.FlashColour(Color4.Red, 300); } } From ff6642190f0d6e85cdff7eb3b2998fef0f9fb974 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 14 May 2020 07:26:47 +0300 Subject: [PATCH 0704/1117] Update colour retrieval logic --- .../CatchSkinColourDecodingTest.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/CatchSkinColourDecodingTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchSkinColourDecodingTest.cs index 57228210d6..7deeec527f 100644 --- a/osu.Game.Rulesets.Catch.Tests/CatchSkinColourDecodingTest.cs +++ b/osu.Game.Rulesets.Catch.Tests/CatchSkinColourDecodingTest.cs @@ -19,9 +19,9 @@ namespace osu.Game.Rulesets.Catch.Tests var rawSkin = new TestLegacySkin(new SkinInfo { Name = "special-skin" }, store); var skin = new CatchLegacySkinTransformer(rawSkin); - Assert.AreEqual(new Color4(232, 185, 35, 255), skin.GetHyperDashCatcherColour()?.Value); - Assert.AreEqual(new Color4(232, 74, 35, 255), skin.GetHyperDashCatcherAfterImageColour()?.Value); - Assert.AreEqual(new Color4(0, 255, 255, 255), skin.GetHyperDashFruitColour()?.Value); + Assert.AreEqual(new Color4(232, 185, 35, 255), skin.GetConfig(CatchSkinColour.HyperDash)?.Value); + Assert.AreEqual(new Color4(232, 74, 35, 255), skin.GetConfig(CatchSkinColour.HyperDashAfterImage)?.Value); + Assert.AreEqual(new Color4(0, 255, 255, 255), skin.GetConfig(CatchSkinColour.HyperDashFruit)?.Value); } private class TestLegacySkin : LegacySkin From 5e09a1b33485663dbb69fa53b44b12861849831c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 May 2020 14:23:12 +0900 Subject: [PATCH 0705/1117] Use Action rather than custom handler --- osu.Game/Overlays/BeatmapListing/BeatmapListingPager.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingPager.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingPager.cs index f55e37ebc7..dc9f30cab3 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingPager.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingPager.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.Game.Beatmaps; @@ -20,7 +21,8 @@ namespace osu.Game.Overlays.BeatmapListing private readonly SortCriteria sortCriteria; private readonly SortDirection sortDirection; - public event PageFetchHandler PageFetched; + public event Action> PageFetched; + private SearchBeatmapSetsRequest getSetsRequest; private SearchBeatmapSetsResponse lastResponse; @@ -82,7 +84,5 @@ namespace osu.Game.Overlays.BeatmapListing getSetsRequest?.Cancel(); getSetsRequest = null; } - - public delegate void PageFetchHandler(List sets); } } From fa3373e5f306e0f64afeccefa9977c02584b2f5c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 May 2020 14:24:43 +0900 Subject: [PATCH 0706/1117] Reorder file and change naming slightly --- .../BeatmapListingFilterControl.cs | 30 +++++++++---------- osu.Game/Overlays/BeatmapListingOverlay.cs | 2 +- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs index ac5ad96f7c..92822794b7 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs @@ -36,6 +36,9 @@ namespace osu.Game.Overlays.BeatmapListing private BeatmapListingPager beatmapListingPager; + private ScheduledDelegate queryChangedDebounce; + private ScheduledDelegate queryPagingDebounce; + public BeatmapListingFilterControl() { RelativeSizeAxes = Axes.X; @@ -113,8 +116,17 @@ namespace osu.Game.Overlays.BeatmapListing sortDirection.BindValueChanged(_ => queueUpdateSearch()); } - private ScheduledDelegate queryChangedDebounce; - private ScheduledDelegate queryPagingDebounce; + public void TakeFocus() => searchControl.TakeFocus(); + + public void ShowMore() + { + if (beatmapListingPager == null || !beatmapListingPager.CanFetchNextPage) + return; + if (queryPagingDebounce != null) + return; + + beatmapListingPager.FetchNextPage(); + } private void queueUpdateSearch(bool queryTextChanged = false) { @@ -142,7 +154,7 @@ namespace osu.Game.Overlays.BeatmapListing queryPagingDebounce = null; beatmapListingPager.PageFetched += onSearchFinished; - AddPageToResult(); + ShowMore(); } private void onSearchFinished(List beatmaps) @@ -165,17 +177,5 @@ namespace osu.Game.Overlays.BeatmapListing base.Dispose(isDisposing); } - - public void TakeFocus() => searchControl.TakeFocus(); - - public void AddPageToResult() - { - if (beatmapListingPager == null || !beatmapListingPager.CanFetchNextPage) - return; - if (queryPagingDebounce != null) - return; - - beatmapListingPager.FetchNextPage(); - } } } diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index e26f084ea4..4aa754491c 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -247,7 +247,7 @@ namespace osu.Game.Overlays base.Update(); if (shouldAddNextPage) - filterControl.AddPageToResult(); + filterControl.ShowMore(); } } } From 04c99735264be3cd769bc085d7958727c6b83bda Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 May 2020 14:33:31 +0900 Subject: [PATCH 0707/1117] Clean up cancellation logic --- .../BeatmapListingFilterControl.cs | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs index 92822794b7..3df4d5d588 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs @@ -122,6 +122,7 @@ namespace osu.Game.Overlays.BeatmapListing { if (beatmapListingPager == null || !beatmapListingPager.CanFetchNextPage) return; + if (queryPagingDebounce != null) return; @@ -132,14 +133,15 @@ namespace osu.Game.Overlays.BeatmapListing { SearchStarted?.Invoke(); - beatmapListingPager?.Reset(); + cancelSearch(); - queryChangedDebounce?.Cancel(); queryChangedDebounce = Scheduler.AddDelayed(updateSearch, queryTextChanged ? 500 : 100); } private void updateSearch() { + cancelSearch(); + beatmapListingPager = new BeatmapListingPager( api, rulesets, @@ -150,13 +152,20 @@ namespace osu.Game.Overlays.BeatmapListing sortControl.SortDirection.Value ); - queryPagingDebounce?.Cancel(); - queryPagingDebounce = null; beatmapListingPager.PageFetched += onSearchFinished; ShowMore(); } + private void cancelSearch() + { + beatmapListingPager?.Reset(); + queryChangedDebounce?.Cancel(); + + queryPagingDebounce?.Cancel(); + queryPagingDebounce = null; + } + private void onSearchFinished(List beatmaps) { queryPagingDebounce = Scheduler.AddDelayed(() => queryPagingDebounce = null, 1000); @@ -171,9 +180,7 @@ namespace osu.Game.Overlays.BeatmapListing protected override void Dispose(bool isDisposing) { - beatmapListingPager?.Reset(); - queryChangedDebounce?.Cancel(); - queryPagingDebounce?.Cancel(); + cancelSearch(); base.Dispose(isDisposing); } From c836c9319bb0b6d28fa378e4412b9d4fc7d47e71 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 May 2020 15:35:11 +0900 Subject: [PATCH 0708/1117] Combine pagination logic into BeatmapListingFilterControl --- .../BeatmapListingFilterControl.cs | 118 +++++++++++------- .../BeatmapListing/BeatmapListingPager.cs | 88 ------------- osu.Game/Overlays/BeatmapListingOverlay.cs | 69 +++++----- 3 files changed, 111 insertions(+), 164 deletions(-) delete mode 100644 osu.Game/Overlays/BeatmapListing/BeatmapListingPager.cs diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs index 3df4d5d588..41c99d5d03 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -12,6 +13,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Threading; using osu.Game.Beatmaps; using osu.Game.Online.API; +using osu.Game.Online.API.Requests; using osu.Game.Rulesets; using osuTK; using osuTK.Graphics; @@ -20,9 +22,34 @@ namespace osu.Game.Overlays.BeatmapListing { public class BeatmapListingFilterControl : CompositeDrawable { + ///

    + /// Fired when a search finishes. Contains only new items in the case of pagination. + /// public Action> SearchFinished; + + /// + /// Fired when search criteria change. + /// public Action SearchStarted; - private List currentBeatmaps; + + /// + /// True when pagination has reached the end of available results. + /// + private bool noMoreResults; + + /// + /// The current page fetched of results (zero index). + /// + public int CurrentPage { get; private set; } + + private readonly BeatmapListingSearchControl searchControl; + private readonly BeatmapListingSortTabControl sortControl; + private readonly Box sortControlBackground; + + private ScheduledDelegate queryChangedDebounce; + + private SearchBeatmapSetsRequest getSetsRequest; + private SearchBeatmapSetsResponse lastResponse; [Resolved] private IAPIProvider api { get; set; } @@ -30,19 +57,11 @@ namespace osu.Game.Overlays.BeatmapListing [Resolved] private RulesetStore rulesets { get; set; } - private readonly BeatmapListingSearchControl searchControl; - private readonly BeatmapListingSortTabControl sortControl; - private readonly Box sortControlBackground; - - private BeatmapListingPager beatmapListingPager; - - private ScheduledDelegate queryChangedDebounce; - private ScheduledDelegate queryPagingDebounce; - public BeatmapListingFilterControl() { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; + InternalChild = new FillFlowContainer { RelativeSizeAxes = Axes.X, @@ -118,69 +137,80 @@ namespace osu.Game.Overlays.BeatmapListing public void TakeFocus() => searchControl.TakeFocus(); - public void ShowMore() + /// + /// Fetch the next page of results. May result in a no-op if a fetch is already in progress, or if there are no results left. + /// + public void FetchNextPage() { - if (beatmapListingPager == null || !beatmapListingPager.CanFetchNextPage) + // there may be no results left. + if (noMoreResults) return; - if (queryPagingDebounce != null) + // there may already be an active request. + if (getSetsRequest != null) return; - beatmapListingPager.FetchNextPage(); + if (lastResponse != null) + CurrentPage++; + + performRequest(); } private void queueUpdateSearch(bool queryTextChanged = false) { SearchStarted?.Invoke(); - cancelSearch(); + resetSearch(); - queryChangedDebounce = Scheduler.AddDelayed(updateSearch, queryTextChanged ? 500 : 100); + queryChangedDebounce = Scheduler.AddDelayed(() => + { + resetSearch(); + FetchNextPage(); + }, queryTextChanged ? 500 : 100); } - private void updateSearch() + private void performRequest() { - cancelSearch(); - - beatmapListingPager = new BeatmapListingPager( - api, - rulesets, + getSetsRequest = new SearchBeatmapSetsRequest( searchControl.Query.Value, searchControl.Ruleset.Value, + lastResponse?.Cursor, searchControl.Category.Value, sortControl.Current.Value, - sortControl.SortDirection.Value - ); + sortControl.SortDirection.Value); - beatmapListingPager.PageFetched += onSearchFinished; + getSetsRequest.Success += response => + { + var sets = response.BeatmapSets.Select(responseJson => responseJson.ToBeatmapSet(rulesets)).ToList(); - ShowMore(); + if (sets.Count == 0) + noMoreResults = true; + + lastResponse = response; + getSetsRequest = null; + + SearchFinished?.Invoke(sets); + }; + + api.Queue(getSetsRequest); } - private void cancelSearch() + private void resetSearch() { - beatmapListingPager?.Reset(); + noMoreResults = false; + CurrentPage = 0; + + lastResponse = null; + + getSetsRequest?.Cancel(); + getSetsRequest = null; + queryChangedDebounce?.Cancel(); - - queryPagingDebounce?.Cancel(); - queryPagingDebounce = null; - } - - private void onSearchFinished(List beatmaps) - { - queryPagingDebounce = Scheduler.AddDelayed(() => queryPagingDebounce = null, 1000); - - if (currentBeatmaps == null || !beatmapListingPager.IsPastFirstPage) - currentBeatmaps = beatmaps; - else - currentBeatmaps.AddRange(beatmaps); - - SearchFinished?.Invoke(beatmaps); } protected override void Dispose(bool isDisposing) { - cancelSearch(); + resetSearch(); base.Dispose(isDisposing); } diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingPager.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingPager.cs deleted file mode 100644 index dc9f30cab3..0000000000 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingPager.cs +++ /dev/null @@ -1,88 +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.Online.API; -using osu.Game.Online.API.Requests; -using osu.Game.Rulesets; - -namespace osu.Game.Overlays.BeatmapListing -{ - public class BeatmapListingPager - { - private readonly IAPIProvider api; - private readonly RulesetStore rulesets; - private readonly string query; - private readonly RulesetInfo ruleset; - private readonly SearchCategory searchCategory; - private readonly SortCriteria sortCriteria; - private readonly SortDirection sortDirection; - - public event Action> PageFetched; - - private SearchBeatmapSetsRequest getSetsRequest; - private SearchBeatmapSetsResponse lastResponse; - - private bool isLastPageFetched; - private bool isFetching => getSetsRequest != null; - public bool IsPastFirstPage { get; private set; } - public bool CanFetchNextPage => !isLastPageFetched && !isFetching; - - public BeatmapListingPager(IAPIProvider api, RulesetStore rulesets, string query, RulesetInfo ruleset, SearchCategory searchCategory = SearchCategory.Any, SortCriteria sortCriteria = SortCriteria.Ranked, SortDirection sortDirection = SortDirection.Descending) - { - this.api = api; - this.rulesets = rulesets; - this.query = query; - this.ruleset = ruleset; - this.searchCategory = searchCategory; - this.sortCriteria = sortCriteria; - this.sortDirection = sortDirection; - } - - public void FetchNextPage() - { - if (isFetching) - return; - - if (lastResponse != null) - IsPastFirstPage = true; - - getSetsRequest = new SearchBeatmapSetsRequest( - query, - ruleset, - lastResponse?.Cursor, - searchCategory, - sortCriteria, - sortDirection); - - getSetsRequest.Success += response => - { - var sets = response.BeatmapSets.Select(responseJson => responseJson.ToBeatmapSet(rulesets)).ToList(); - - if (sets.Count == 0) - isLastPageFetched = true; - - lastResponse = response; - getSetsRequest = null; - - PageFetched?.Invoke(sets); - }; - - api.Queue(getSetsRequest); - } - - public void Reset() - { - isLastPageFetched = false; - IsPastFirstPage = false; - - lastResponse = null; - - getSetsRequest?.Cancel(); - getSetsRequest = null; - } - } -} diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index 4aa754491c..225a8a0578 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -4,7 +4,9 @@ using System.Collections.Generic; 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.Shapes; @@ -34,8 +36,6 @@ namespace osu.Game.Overlays private NotFoundDrawable notFoundContent; private OverlayScrollContainer resultScrollContainer; - private const int pagination_scroll_distance = 500; - private bool shouldAddNextPage => resultScrollContainer.ScrollableExtent > 0 && resultScrollContainer.IsScrolledToEnd(pagination_scroll_distance); public BeatmapListingOverlay() : base(OverlayColourScheme.Blue) @@ -121,51 +121,45 @@ namespace osu.Game.Overlays loadingLayer.Show(); } + private Task panelLoadDelegate; + private void onSearchFinished(List beatmaps) { - //No matches case - if (!beatmaps.Any()) + var newPanels = beatmaps.Select(b => new GridBeatmapPanel(b) { - LoadComponentAsync(notFoundContent, addContentToPlaceholder, (cancellationToken = new CancellationTokenSource()).Token); - return; - } + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + }); - //New query case - if (!shouldAddNextPage) + if (filterControl.CurrentPage == 0) { - //Spawn new child - var newPanels = new FillFlowContainer + //No matches case + if (!newPanels.Any()) + { + LoadComponentAsync(notFoundContent, addContentToPlaceholder, (cancellationToken = new CancellationTokenSource()).Token); + return; + } + + // spawn new children with the contained so we only clear old content at the last moment. + var content = new FillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Spacing = new Vector2(10), Alpha = 0, Margin = new MarginPadding { Vertical = 15 }, - ChildrenEnumerable = beatmaps.Select(b => new GridBeatmapPanel(b) - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - }) + ChildrenEnumerable = newPanels }; - foundContent = newPanels; - LoadComponentAsync(foundContent, addContentToPlaceholder, (cancellationToken = new CancellationTokenSource()).Token); + panelLoadDelegate = LoadComponentAsync(foundContent = content, addContentToPlaceholder, (cancellationToken = new CancellationTokenSource()).Token); } - - //Pagination case else { - beatmaps.ForEach(x => + panelLoadDelegate = LoadComponentsAsync(newPanels, loaded => { - LoadComponentAsync(new GridBeatmapPanel(x) - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - }, loaded => - { - foundContent.Add(loaded); - loaded.FadeIn(200, Easing.OutQuint); - }); + lastFetchDisplayedTime = Time.Current; + foundContent.AddRange(loaded); + loaded.ForEach(p => p.FadeIn(200, Easing.OutQuint)); }); } } @@ -173,6 +167,7 @@ namespace osu.Game.Overlays private void addContentToPlaceholder(Drawable content) { loadingLayer.Hide(); + lastFetchDisplayedTime = Time.Current; var lastContent = currentContent; @@ -242,12 +237,22 @@ namespace osu.Game.Overlays } } + private const double time_between_fetches = 500; + + private double lastFetchDisplayedTime; + protected override void Update() { base.Update(); - if (shouldAddNextPage) - filterControl.ShowMore(); + const int pagination_scroll_distance = 500; + + bool shouldShowMore = panelLoadDelegate?.IsCompleted != false + && Time.Current - lastFetchDisplayedTime > time_between_fetches + && (resultScrollContainer.ScrollableExtent > 0 && resultScrollContainer.IsScrolledToEnd(pagination_scroll_distance)); + + if (shouldShowMore) + filterControl.FetchNextPage(); } } } From facde2c8e17edd2804dfee0921f29caafcd04648 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 May 2020 16:01:07 +0900 Subject: [PATCH 0709/1117] Remove unnecessary generic specification on cursor --- osu.Game/Extensions/WebRequestExtensions.cs | 15 ++++---------- osu.Game/Online/API/Requests/Cursor.cs | 20 +++++++++++++++++++ .../Online/API/Requests/ResponseWithCursor.cs | 11 +--------- .../API/Requests/SearchBeatmapSetsResponse.cs | 3 +-- 4 files changed, 26 insertions(+), 23 deletions(-) create mode 100644 osu.Game/Online/API/Requests/Cursor.cs diff --git a/osu.Game/Extensions/WebRequestExtensions.cs b/osu.Game/Extensions/WebRequestExtensions.cs index 80c8b147bf..b940c7498b 100644 --- a/osu.Game/Extensions/WebRequestExtensions.cs +++ b/osu.Game/Extensions/WebRequestExtensions.cs @@ -3,22 +3,15 @@ using osu.Framework.IO.Network; using osu.Framework.Extensions.IEnumerableExtensions; -using System.Collections.Generic; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using JetBrains.Annotations; +using osu.Game.Online.API.Requests; namespace osu.Game.Extensions { - public class Cursor - { - [UsedImplicitly] - [JsonExtensionData] - public IDictionary Properties; - } - public static class WebRequestExtensions { + /// + /// Add a pagination cursor to the web request in the format required by osu-web. + /// public static void AddCursor(this WebRequest webRequest, Cursor cursor) { cursor?.Properties.ForEach(x => diff --git a/osu.Game/Online/API/Requests/Cursor.cs b/osu.Game/Online/API/Requests/Cursor.cs new file mode 100644 index 0000000000..f21445ca32 --- /dev/null +++ b/osu.Game/Online/API/Requests/Cursor.cs @@ -0,0 +1,20 @@ +// 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 JetBrains.Annotations; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace osu.Game.Online.API.Requests +{ + /// + /// A collection of parameters which should be passed to the search endpoint to fetch the next page. + /// + public class Cursor + { + [UsedImplicitly] + [JsonExtensionData] + public IDictionary Properties; + } +} diff --git a/osu.Game/Online/API/Requests/ResponseWithCursor.cs b/osu.Game/Online/API/Requests/ResponseWithCursor.cs index b0fe9eea28..d52e999722 100644 --- a/osu.Game/Online/API/Requests/ResponseWithCursor.cs +++ b/osu.Game/Online/API/Requests/ResponseWithCursor.cs @@ -7,16 +7,7 @@ namespace osu.Game.Online.API.Requests { public abstract class ResponseWithCursor { - /// - /// A collection of parameters which should be passed to the search endpoint to fetch the next page. - /// [JsonProperty("cursor")] - public dynamic CursorJson; - } - - public abstract class ResponseWithCursor : ResponseWithCursor where T : class - { - [JsonProperty("cursor")] - public T Cursor; + public Cursor Cursor; } } diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsResponse.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsResponse.cs index a4d2c0e871..3c4fb11ed1 100644 --- a/osu.Game/Online/API/Requests/SearchBeatmapSetsResponse.cs +++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsResponse.cs @@ -3,12 +3,11 @@ using System.Collections.Generic; using Newtonsoft.Json; -using osu.Game.Extensions; using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Online.API.Requests { - public class SearchBeatmapSetsResponse : ResponseWithCursor + public class SearchBeatmapSetsResponse : ResponseWithCursor { [JsonProperty("beatmapsets")] public IEnumerable BeatmapSets; From 6bb06e9d611aeb9255c28a5936f59c0cdc2559f1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 May 2020 22:57:31 +0900 Subject: [PATCH 0710/1117] Expose CurrentDirectory bindable for consumption --- .../Graphics/UserInterfaceV2/DirectorySelector.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/osu.Game/Graphics/UserInterfaceV2/DirectorySelector.cs b/osu.Game/Graphics/UserInterfaceV2/DirectorySelector.cs index ee428c0047..6ea026ad3d 100644 --- a/osu.Game/Graphics/UserInterfaceV2/DirectorySelector.cs +++ b/osu.Game/Graphics/UserInterfaceV2/DirectorySelector.cs @@ -28,11 +28,11 @@ namespace osu.Game.Graphics.UserInterfaceV2 private GameHost host { get; set; } [Cached] - private readonly Bindable currentDirectory = new Bindable(); + public readonly Bindable CurrentDirectory = new Bindable(); public DirectorySelector(string initialPath = null) { - currentDirectory.Value = new DirectoryInfo(initialPath ?? Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)); + CurrentDirectory.Value = new DirectoryInfo(initialPath ?? Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)); } [BackgroundDependencyLoader] @@ -68,7 +68,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 }, }; - currentDirectory.BindValueChanged(updateDisplay, true); + CurrentDirectory.BindValueChanged(updateDisplay, true); } private void updateDisplay(ValueChangedEvent directory) @@ -86,9 +86,9 @@ namespace osu.Game.Graphics.UserInterfaceV2 } else { - directoryFlow.Add(new ParentDirectoryPiece(currentDirectory.Value.Parent)); + directoryFlow.Add(new ParentDirectoryPiece(CurrentDirectory.Value.Parent)); - foreach (var dir in currentDirectory.Value.GetDirectories().OrderBy(d => d.Name)) + foreach (var dir in CurrentDirectory.Value.GetDirectories().OrderBy(d => d.Name)) { if ((dir.Attributes & FileAttributes.Hidden) == 0) directoryFlow.Add(new DirectoryPiece(dir)); @@ -97,8 +97,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 } catch (Exception) { - currentDirectory.Value = directory.OldValue; - + CurrentDirectory.Value = directory.OldValue; this.FlashColour(Color4.Red, 300); } } From cb0b25ac55c7db5e6b1cc4a941971676690388ee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 May 2020 22:57:41 +0900 Subject: [PATCH 0711/1117] Throw better exceptions from OsuStorage --- osu.Game/IO/OsuStorage.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/IO/OsuStorage.cs b/osu.Game/IO/OsuStorage.cs index 71b01ce479..8109631ef9 100644 --- a/osu.Game/IO/OsuStorage.cs +++ b/osu.Game/IO/OsuStorage.cs @@ -48,11 +48,14 @@ namespace osu.Game.IO var source = new DirectoryInfo(GetFullPath(".")); var destination = new DirectoryInfo(newLocation); + if (source.FullName == destination.FullName) + throw new ArgumentException("Destination provided is already the current location", nameof(newLocation)); + // ensure the new location has no files present, else hard abort if (destination.Exists) { if (destination.GetFiles().Length > 0 || destination.GetDirectories().Length > 0) - throw new InvalidOperationException("Migration destination already has files present"); + throw new ArgumentException("Destination provided already has files or directories present", nameof(newLocation)); deleteRecursive(destination); } From 0b73063a89ba56edc8fe0d364fe105375fd63c3f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 May 2020 22:58:05 +0900 Subject: [PATCH 0712/1117] Add basic (working) migration UI --- .../Sections/Maintenance/GeneralSettings.cs | 9 +- .../Maintenance/MigrationRunScreen.cs | 88 +++++++++++++++++++ .../Maintenance/MigrationSelectScreen.cs | 57 ++++++++++++ 3 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Overlays/Settings/Sections/Maintenance/MigrationRunScreen.cs create mode 100644 osu.Game/Overlays/Settings/Sections/Maintenance/MigrationSelectScreen.cs diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs index 832673703b..8bdeadae5c 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Screens; using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; using osu.Game.Scoring; @@ -26,8 +27,14 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance private TriangleButton undeleteButton; [BackgroundDependencyLoader] - private void load(BeatmapManager beatmaps, ScoreManager scores, SkinManager skins, DialogOverlay dialogOverlay) + private void load(BeatmapManager beatmaps, ScoreManager scores, SkinManager skins, DialogOverlay dialogOverlay, OsuGame game) { + Add(importBeatmapsButton = new SettingsButton + { + Text = "Migrate storage to new location", + Action = () => game.PerformFromScreen(menu => menu.Push(new MigrationSelectScreen())) + }); + if (beatmaps.SupportsImportFromStable) { Add(importBeatmapsButton = new SettingsButton diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationRunScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationRunScreen.cs new file mode 100644 index 0000000000..76f01dc4b9 --- /dev/null +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationRunScreen.cs @@ -0,0 +1,88 @@ +// 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.Threading.Tasks; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Logging; +using osu.Framework.Screens; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Screens; + +namespace osu.Game.Overlays.Settings.Sections.Maintenance +{ + public class MigrationRunScreen : OsuScreen + { + private readonly DirectoryInfo destination; + + [Resolved] + private OsuGame game { get; set; } + + public override bool AllowBackButton => false; + + public override bool AllowExternalScreenChange => false; + + public override bool DisallowExternalBeatmapRulesetChanges => true; + + private Task migrationTask; + + public MigrationRunScreen(DirectoryInfo destination) + { + this.destination = destination; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + InternalChildren = new Drawable[] + { + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Children = new Drawable[] + { + new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = "Migration in progress", + Font = OsuFont.Default.With(size: 48) + }, + new LoadingSpinner(true) + { + State = { Value = Visibility.Visible } + } + } + }, + }; + + Beatmap.Value = Beatmap.Default; + + migrationTask = Task.Run(() => game.Migrate(destination.FullName)) + .ContinueWith(t => + { + if (t.IsFaulted) + Logger.Log($"Error during migration: {t.Exception?.Message}", level: LogLevel.Error); + + Schedule(this.Exit); + }); + } + + public override bool OnExiting(IScreen next) + { + // block until migration is finished + if (migrationTask?.IsCompleted == false) + return true; + + return base.OnExiting(next); + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationSelectScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationSelectScreen.cs new file mode 100644 index 0000000000..d1c2f6d6ee --- /dev/null +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationSelectScreen.cs @@ -0,0 +1,57 @@ +// 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.Screens; +using osu.Game.Graphics.UserInterface; +using osu.Game.Graphics.UserInterfaceV2; +using osu.Game.Screens; + +namespace osu.Game.Overlays.Settings.Sections.Maintenance +{ + public class MigrationSelectScreen : OsuScreen + { + private DirectorySelector directorySelector; + + [BackgroundDependencyLoader] + private void load() + { + InternalChild = new GridContainer + { + RelativeSizeAxes = Axes.Both, + RowDimensions = new[] + { + new Dimension(GridSizeMode.Relative, 0.8f), + new Dimension(), + }, + Content = new[] + { + new Drawable[] { directorySelector = new DirectorySelector { RelativeSizeAxes = Axes.Both } }, + new Drawable[] + { + new OsuButton + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Width = 300, + Text = "Start", + Action = start + }, + } + } + }; + } + + private void start() + { + var target = directorySelector.CurrentDirectory.Value; + if (target.GetDirectories().Length > 0 || target.GetFiles().Length > 0) + target = target.CreateSubdirectory("osu-lazer"); + + ValidForResume = false; + this.Push(new MigrationRunScreen(target)); + } + } +} From 06f507496a0d4bb8865d226ea9af6d220a5ab4fb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 May 2020 23:02:28 +0900 Subject: [PATCH 0713/1117] Delete migration source if no files exist after completion --- osu.Game/IO/OsuStorage.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/IO/OsuStorage.cs b/osu.Game/IO/OsuStorage.cs index 8109631ef9..443f4fdb69 100644 --- a/osu.Game/IO/OsuStorage.cs +++ b/osu.Game/IO/OsuStorage.cs @@ -87,6 +87,9 @@ namespace osu.Game.IO dir.Delete(true); } + + if (target.GetFiles().Length == 0 && target.GetDirectories().Length == 0) + target.Delete(); } private static void copyRecursive(DirectoryInfo source, DirectoryInfo destination, bool topLevelExcludes = true) From d04079f6ab7911b5d7f8fd633019993137b0fc65 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 May 2020 17:40:30 +0900 Subject: [PATCH 0714/1117] Fix directory selector not masking properly --- .../UserInterfaceV2/DirectorySelector.cs | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/osu.Game/Graphics/UserInterfaceV2/DirectorySelector.cs b/osu.Game/Graphics/UserInterfaceV2/DirectorySelector.cs index 6ea026ad3d..ae34281bfb 100644 --- a/osu.Game/Graphics/UserInterfaceV2/DirectorySelector.cs +++ b/osu.Game/Graphics/UserInterfaceV2/DirectorySelector.cs @@ -40,19 +40,25 @@ namespace osu.Game.Graphics.UserInterfaceV2 { Padding = new MarginPadding(10); - InternalChildren = new Drawable[] + InternalChild = new GridContainer { - new FillFlowContainer + RelativeSizeAxes = Axes.Both, + RowDimensions = new[] { - RelativeSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Children = new Drawable[] + new Dimension(GridSizeMode.Absolute, 50), + new Dimension(), + }, + Content = new[] + { + new Drawable[] { new CurrentDirectoryDisplay { - RelativeSizeAxes = Axes.X, - Height = 50, + RelativeSizeAxes = Axes.Both, }, + }, + new Drawable[] + { new OsuScrollContainer { RelativeSizeAxes = Axes.Both, @@ -65,7 +71,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 } } } - }, + } }; CurrentDirectory.BindValueChanged(updateDisplay, true); From 4e4a779d6827d76323702fad328c451c3e3fb000 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 May 2020 17:40:43 +0900 Subject: [PATCH 0715/1117] Improve overall UI --- .../Sections/General/UpdateSettings.cs | 10 +- .../Sections/Maintenance/GeneralSettings.cs | 7 -- .../Maintenance/MigrationRunScreen.cs | 29 ++++- .../Maintenance/MigrationSelectScreen.cs | 105 +++++++++++++++--- 4 files changed, 123 insertions(+), 28 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs index 188c9c05ef..b5d07ee7b1 100644 --- a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs @@ -4,7 +4,9 @@ using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Platform; +using osu.Framework.Screens; using osu.Game.Configuration; +using osu.Game.Overlays.Settings.Sections.Maintenance; namespace osu.Game.Overlays.Settings.Sections.General { @@ -13,7 +15,7 @@ namespace osu.Game.Overlays.Settings.Sections.General protected override string Header => "Updates"; [BackgroundDependencyLoader] - private void load(Storage storage, OsuConfigManager config) + private void load(Storage storage, OsuConfigManager config, OsuGame game) { Add(new SettingsEnumDropdown { @@ -28,6 +30,12 @@ namespace osu.Game.Overlays.Settings.Sections.General Text = "Open osu! folder", Action = storage.OpenInNativeExplorer, }); + + Add(new SettingsButton + { + Text = "Change folder location...", + Action = () => game.PerformFromScreen(menu => menu.Push(new MigrationSelectScreen())) + }); } } } diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs index 8bdeadae5c..1dd079a8ab 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs @@ -5,7 +5,6 @@ using System.Linq; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Screens; using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; using osu.Game.Scoring; @@ -29,12 +28,6 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance [BackgroundDependencyLoader] private void load(BeatmapManager beatmaps, ScoreManager scores, SkinManager skins, DialogOverlay dialogOverlay, OsuGame game) { - Add(importBeatmapsButton = new SettingsButton - { - Text = "Migrate storage to new location", - Action = () => game.PerformFromScreen(menu => menu.Push(new MigrationSelectScreen())) - }); - if (beatmaps.SupportsImportFromStable) { Add(importBeatmapsButton = new SettingsButton diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationRunScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationRunScreen.cs index 76f01dc4b9..b29cd0d630 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationRunScreen.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationRunScreen.cs @@ -12,6 +12,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Screens; +using osuTK; namespace osu.Game.Overlays.Settings.Sections.Maintenance { @@ -28,6 +29,8 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance public override bool DisallowExternalBeatmapRulesetChanges => true; + public override bool HideOverlaysOnEnter => true; + private Task migrationTask; public MigrationRunScreen(DirectoryInfo destination) @@ -47,6 +50,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance Direction = FillDirection.Vertical, Anchor = Anchor.Centre, Origin = Anchor.Centre, + Spacing = new Vector2(10), Children = new Drawable[] { new OsuSpriteText @@ -54,12 +58,26 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance Anchor = Anchor.Centre, Origin = Anchor.Centre, Text = "Migration in progress", - Font = OsuFont.Default.With(size: 48) + Font = OsuFont.Default.With(size: 40) + }, + new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = "This could take a few minutes depending on the speed of your disk(s).", + Font = OsuFont.Default.With(size: 30) }, new LoadingSpinner(true) { State = { Value = Visibility.Visible } - } + }, + new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = "Please avoid interacting with the game!", + Font = OsuFont.Default.With(size: 30) + }, } }, }; @@ -76,6 +94,13 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance }); } + public override void OnEntering(IScreen last) + { + base.OnEntering(last); + + this.FadeOut().Delay(250).Then().FadeIn(250); + } + public override bool OnExiting(IScreen next) { // block until migration is finished diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationSelectScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationSelectScreen.cs index d1c2f6d6ee..c1aa7f095c 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationSelectScreen.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationSelectScreen.cs @@ -1,13 +1,21 @@ // 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 osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Logging; +using osu.Framework.Platform; using osu.Framework.Screens; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Screens; +using osuTK; namespace osu.Game.Overlays.Settings.Sections.Maintenance { @@ -15,40 +23,101 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance { private DirectorySelector directorySelector; + public override bool AllowExternalScreenChange => false; + + public override bool DisallowExternalBeatmapRulesetChanges => true; + + public override bool HideOverlaysOnEnter => true; + [BackgroundDependencyLoader] - private void load() + private void load(OsuGame game, Storage storage, OsuColour colours) { - InternalChild = new GridContainer + game.Toolbar.Hide(); + + // begin selection in the parent directory of the current storage location + var initialPath = new DirectoryInfo(storage.GetFullPath(string.Empty)).Parent?.FullName; + + InternalChild = new Container { + Masking = true, + CornerRadius = 10, RelativeSizeAxes = Axes.Both, - RowDimensions = new[] + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(0.5f, 0.8f), + Children = new Drawable[] { - new Dimension(GridSizeMode.Relative, 0.8f), - new Dimension(), - }, - Content = new[] - { - new Drawable[] { directorySelector = new DirectorySelector { RelativeSizeAxes = Axes.Both } }, - new Drawable[] + new Box { - new OsuButton + Colour = colours.GreySeafoamDark, + RelativeSizeAxes = Axes.Both, + }, + new GridContainer + { + RelativeSizeAxes = Axes.Both, + RowDimensions = new[] { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Width = 300, - Text = "Start", - Action = start + new Dimension(), + new Dimension(GridSizeMode.Relative, 0.8f), + new Dimension(), }, + Content = new[] + { + new Drawable[] + { + new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = "Please select a new location", + Font = OsuFont.Default.With(size: 40) + }, + }, + new Drawable[] + { + directorySelector = new DirectorySelector(initialPath) + { + RelativeSizeAxes = Axes.Both, + } + }, + new Drawable[] + { + new TriangleButton + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Width = 300, + Text = "Begin folder migration", + Action = start + }, + } + } } } }; } + public override void OnSuspending(IScreen next) + { + base.OnSuspending(next); + + this.FadeOut(250); + } + private void start() { var target = directorySelector.CurrentDirectory.Value; - if (target.GetDirectories().Length > 0 || target.GetFiles().Length > 0) - target = target.CreateSubdirectory("osu-lazer"); + + try + { + if (target.GetDirectories().Length > 0 || target.GetFiles().Length > 0) + target = target.CreateSubdirectory("osu-lazer"); + } + catch (Exception e) + { + Logger.Log($"Error during migration: {e?.Message}", level: LogLevel.Error); + return; + } ValidForResume = false; this.Push(new MigrationRunScreen(target)); From 0ef3bae26a4483fd12c21a34ff38d3fe6f69775d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 14 May 2020 18:34:51 +0900 Subject: [PATCH 0716/1117] Expose playfield from IManiaHitObjectComposer --- .../ManiaPlacementBlueprintTestScene.cs | 2 +- .../ManiaSelectionBlueprintTestScene.cs | 2 +- osu.Game.Rulesets.Mania/Edit/IManiaHitObjectComposer.cs | 2 +- osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs | 2 -- osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs | 2 +- 5 files changed, 4 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestScene.cs b/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestScene.cs index aac77c9c1c..39d5f50459 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestScene.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestScene.cs @@ -49,6 +49,6 @@ namespace osu.Game.Rulesets.Mania.Tests public Column ColumnAt(Vector2 screenSpacePosition) => column; - public int TotalColumns => 1; + public ManiaPlayfield Playfield => null; } } diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaSelectionBlueprintTestScene.cs b/osu.Game.Rulesets.Mania.Tests/ManiaSelectionBlueprintTestScene.cs index b598893e8c..d6dee92ba6 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaSelectionBlueprintTestScene.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaSelectionBlueprintTestScene.cs @@ -33,6 +33,6 @@ namespace osu.Game.Rulesets.Mania.Tests public Column ColumnAt(Vector2 screenSpacePosition) => column; - public int TotalColumns => 1; + public ManiaPlayfield Playfield => null; } } diff --git a/osu.Game.Rulesets.Mania/Edit/IManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/IManiaHitObjectComposer.cs index f64bab1fae..9b5d290fa8 100644 --- a/osu.Game.Rulesets.Mania/Edit/IManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/IManiaHitObjectComposer.cs @@ -10,6 +10,6 @@ namespace osu.Game.Rulesets.Mania.Edit { Column ColumnAt(Vector2 screenSpacePosition); - int TotalColumns { get; } + ManiaPlayfield Playfield { get; } } } diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index dfa933baad..d7c0889c0d 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -42,8 +42,6 @@ namespace osu.Game.Rulesets.Mania.Edit public IScrollingInfo ScrollingInfo => drawableRuleset.ScrollingInfo; - public int TotalColumns => Playfield.TotalColumns; - public override (Vector2 position, double time) GetSnappedPosition(Vector2 position, double time) { var hoc = Playfield.GetColumn(0).HitObjectContainer; diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs index 55245198c8..83049ff959 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs @@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Mania.Edit maxColumn = obj.Column; } - columnDelta = Math.Clamp(columnDelta, -minColumn, composer.TotalColumns - 1 - maxColumn); + columnDelta = Math.Clamp(columnDelta, -minColumn, composer.Playfield.TotalColumns - 1 - maxColumn); foreach (var obj in SelectedHitObjects.OfType()) obj.Column += columnDelta; From a5826116479b547084fa2d3d0fab0211ab83089a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 May 2020 19:05:35 +0900 Subject: [PATCH 0717/1117] Add test coverage --- .../Settings/TestSceneMigrationScreens.cs | 36 +++++++++++++++++++ .../Maintenance/MigrationRunScreen.cs | 6 ++-- .../Maintenance/MigrationSelectScreen.cs | 10 +++--- 3 files changed, 46 insertions(+), 6 deletions(-) create mode 100644 osu.Game.Tests/Visual/Settings/TestSceneMigrationScreens.cs diff --git a/osu.Game.Tests/Visual/Settings/TestSceneMigrationScreens.cs b/osu.Game.Tests/Visual/Settings/TestSceneMigrationScreens.cs new file mode 100644 index 0000000000..2883e54385 --- /dev/null +++ b/osu.Game.Tests/Visual/Settings/TestSceneMigrationScreens.cs @@ -0,0 +1,36 @@ +// 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.Threading; +using osu.Framework.Screens; +using osu.Game.Overlays.Settings.Sections.Maintenance; + +namespace osu.Game.Tests.Visual.Settings +{ + public class TestSceneMigrationScreens : ScreenTestScene + { + public TestSceneMigrationScreens() + { + AddStep("Push screen", () => Stack.Push(new TestMigrationSelectScreen())); + } + + private class TestMigrationSelectScreen : MigrationSelectScreen + { + protected override void BeginMigration(DirectoryInfo target) => this.Push(new TestMigrationRunScreen()); + + private class TestMigrationRunScreen : MigrationRunScreen + { + protected override void PerformMigration() + { + Thread.Sleep(3000); + } + + public TestMigrationRunScreen() + : base(null) + { + } + } + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationRunScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationRunScreen.cs index b29cd0d630..b0b61554eb 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationRunScreen.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationRunScreen.cs @@ -20,7 +20,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance { private readonly DirectoryInfo destination; - [Resolved] + [Resolved(canBeNull: true)] private OsuGame game { get; set; } public override bool AllowBackButton => false; @@ -84,7 +84,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance Beatmap.Value = Beatmap.Default; - migrationTask = Task.Run(() => game.Migrate(destination.FullName)) + migrationTask = Task.Run(PerformMigration) .ContinueWith(t => { if (t.IsFaulted) @@ -94,6 +94,8 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance }); } + protected virtual void PerformMigration() => game?.Migrate(destination.FullName); + public override void OnEntering(IScreen last) { base.OnEntering(last); diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationSelectScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationSelectScreen.cs index c1aa7f095c..79d842a617 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationSelectScreen.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationSelectScreen.cs @@ -29,10 +29,10 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance public override bool HideOverlaysOnEnter => true; - [BackgroundDependencyLoader] + [BackgroundDependencyLoader(true)] private void load(OsuGame game, Storage storage, OsuColour colours) { - game.Toolbar.Hide(); + game?.Toolbar.Hide(); // begin selection in the parent directory of the current storage location var initialPath = new DirectoryInfo(storage.GetFullPath(string.Empty)).Parent?.FullName; @@ -115,12 +115,14 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance } catch (Exception e) { - Logger.Log($"Error during migration: {e?.Message}", level: LogLevel.Error); + Logger.Log($"Error during migration: {e.Message}", level: LogLevel.Error); return; } ValidForResume = false; - this.Push(new MigrationRunScreen(target)); + BeginMigration(target); } + + protected virtual void BeginMigration(DirectoryInfo target) => this.Push(new MigrationRunScreen(target)); } } From 16585f767edb0f877cd04eba61ef662f7ecdca59 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 14 May 2020 19:17:24 +0900 Subject: [PATCH 0718/1117] Add initial beat snap grid implementation --- .../TestSceneManiaBeatSnapGrid.cs | 73 ++++++ .../Edit/ManiaBeatSnapGrid.cs | 233 ++++++++++++++++++ osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs | 2 + 3 files changed, 308 insertions(+) create mode 100644 osu.Game.Rulesets.Mania.Tests/TestSceneManiaBeatSnapGrid.cs create mode 100644 osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaBeatSnapGrid.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaBeatSnapGrid.cs new file mode 100644 index 0000000000..84419313e6 --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaBeatSnapGrid.cs @@ -0,0 +1,73 @@ +// 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.Graphics; +using osu.Framework.Input.Events; +using osu.Framework.Timing; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Configuration; +using osu.Game.Rulesets.Mania.Beatmaps; +using osu.Game.Rulesets.Mania.Edit; +using osu.Game.Rulesets.Mania.UI; +using osu.Game.Rulesets.UI.Scrolling; +using osu.Game.Screens.Edit; +using osu.Game.Tests.Visual; +using osuTK; + +namespace osu.Game.Rulesets.Mania.Tests +{ + [Cached(typeof(IManiaHitObjectComposer))] + public class TestSceneManiaBeatSnapGrid : EditorClockTestScene, IManiaHitObjectComposer + { + [Cached(typeof(IScrollingInfo))] + private ScrollingTestContainer.TestScrollingInfo scrollingInfo = new ScrollingTestContainer.TestScrollingInfo(); + + [Cached(typeof(EditorBeatmap))] + private EditorBeatmap editorBeatmap = new EditorBeatmap(new ManiaBeatmap(new StageDefinition())); + + private readonly ManiaBeatSnapGrid beatSnapGrid; + + public TestSceneManiaBeatSnapGrid() + { + editorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = 200 }); + editorBeatmap.ControlPointInfo.Add(10000, new TimingControlPoint { BeatLength = 200 }); + + BeatDivisor.Value = 3; + + // Some sane defaults + scrollingInfo.Algorithm.Algorithm = ScrollVisualisationMethod.Constant; + scrollingInfo.Direction.Value = ScrollingDirection.Up; + scrollingInfo.TimeRange.Value = 1000; + + Children = new Drawable[] + { + Playfield = new ManiaPlayfield(new List + { + new StageDefinition { Columns = 4 }, + new StageDefinition { Columns = 3 } + }) + { + Clock = new FramedClock(new StopwatchClock()) + }, + beatSnapGrid = new ManiaBeatSnapGrid() + }; + } + + protected override bool OnMouseMove(MouseMoveEvent e) + { + // We're providing a constant scroll algorithm. + float relativePosition = Playfield.Stages[0].HitObjectContainer.ToLocalSpace(e.ScreenSpaceMousePosition).Y / Playfield.Stages[0].HitObjectContainer.DrawHeight; + double timeValue = scrollingInfo.TimeRange.Value * relativePosition; + + beatSnapGrid.SetRange(timeValue, timeValue); + + return true; + } + + public Column ColumnAt(Vector2 screenSpacePosition) => null; + + public ManiaPlayfield Playfield { get; } + } +} diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs new file mode 100644 index 0000000000..5a3fe29770 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs @@ -0,0 +1,233 @@ +// 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; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; +using osu.Game.Rulesets.Mania.UI; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.UI.Scrolling; +using osu.Game.Screens.Edit; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Mania.Edit +{ + public class ManiaBeatSnapGrid : CompositeDrawable + { + [Resolved] + private IManiaHitObjectComposer composer { get; set; } + + [Resolved] + private EditorBeatmap beatmap { get; set; } + + [Resolved] + private IScrollingInfo scrollingInfo { get; set; } + + [Resolved] + private Bindable working { get; set; } + + [Resolved] + private OsuColour colours { get; set; } + + [Resolved] + private BindableBeatDivisor beatDivisor { get; set; } + + private readonly List grids = new List(); + + [BackgroundDependencyLoader] + private void load() + { + foreach (var stage in composer.Playfield.Stages) + { + var grid = new Grid(stage); + grids.Add(grid); + + AddInternal(grid); + } + + beatDivisor.BindValueChanged(_ => createLines(), true); + } + + private void createLines() + { + foreach (var grid in grids) + grid.Clear(); + + for (int i = 0; i < beatmap.ControlPointInfo.TimingPoints.Count; i++) + { + var point = beatmap.ControlPointInfo.TimingPoints[i]; + var until = i + 1 < beatmap.ControlPointInfo.TimingPoints.Count ? beatmap.ControlPointInfo.TimingPoints[i + 1].Time : working.Value.Track.Length; + + int beat = 0; + + for (double t = point.Time; t < until; t += point.BeatLength / beatDivisor.Value) + { + var indexInBeat = beat % beatDivisor.Value; + Color4 colour; + + if (indexInBeat == 0) + colour = BindableBeatDivisor.GetColourFor(1, colours); + else + { + var divisor = BindableBeatDivisor.GetDivisorForBeatIndex(beat, beatDivisor.Value); + colour = BindableBeatDivisor.GetColourFor(divisor, colours); + } + + foreach (var grid in grids) + grid.Add(new DrawableGridLine(t, colour)); + + beat++; + } + } + } + + public (Vector2 position, double time)? GetSnappedPosition(Vector2 position) + { + float minDist = float.PositiveInfinity; + DrawableGridLine minDistLine = null; + Vector2 minDistLinePosition = Vector2.Zero; + + foreach (var grid in grids) + { + foreach (var line in grid.AliveObjects.OfType()) + { + Vector2 linePos = line.ToSpaceOfOtherDrawable(line.OriginPosition, this); + float d = Vector2.Distance(position, linePos); + + if (d < minDist) + { + minDist = d; + minDistLine = line; + minDistLinePosition = linePos; + } + } + } + + if (minDistLine == null) + return null; + + float noteOffset = (scrollingInfo.Direction.Value == ScrollingDirection.Up ? 1 : -1) * DefaultNotePiece.NOTE_HEIGHT / 2; + return (new Vector2(position.X, minDistLinePosition.Y + noteOffset), minDistLine.HitObject.StartTime); + } + + public void SetRange(double minTime, double maxTime) => Schedule(() => + { + var linesBefore = new List(); + var linesDuring = new List(); + var linesAfter = new List(); + + foreach (var grid in grids) + { + linesBefore.Clear(); + linesDuring.Clear(); + linesAfter.Clear(); + + foreach (var line in grid.Objects.OfType()) + { + if (line.HitObject.StartTime < minTime) + linesBefore.Add(line); + else if (line.HitObject.StartTime <= maxTime) + linesDuring.Add(line); + else + linesAfter.Add(line); + } + + foreach (var l in linesDuring) + l.Colour = OsuColour.Gray(0.5f); + + for (int i = 0; i < linesBefore.Count; i++) + { + int offset = (linesBefore.Count - i - 1) / beatDivisor.Value; + linesBefore[i].Colour = OsuColour.Gray(0.5f / (offset + 1)); + } + + for (int i = 0; i < linesAfter.Count; i++) + { + int offset = i / beatDivisor.Value; + linesAfter[i].Colour = OsuColour.Gray(0.5f / (offset + 1)); + } + } + }); + + private class Grid : ScrollingHitObjectContainer + { + [Resolved] + private IManiaHitObjectComposer composer { get; set; } + + private readonly Stage stage; + + public Grid(Stage stage) + { + this.stage = stage; + + RelativeSizeAxes = Axes.None; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Clock = composer.Playfield.Clock; + } + + protected override void Update() + { + base.Update(); + + var parentQuad = Parent.ToLocalSpace(stage.HitObjectContainer.ScreenSpaceDrawQuad); + Position = parentQuad.TopLeft; + Size = parentQuad.Size; + } + } + + private class DrawableGridLine : DrawableHitObject + { + [Resolved] + private IScrollingInfo scrollingInfo { get; set; } + + private readonly IBindable direction = new Bindable(); + + public DrawableGridLine(double startTime, Color4 colour) + : base(new HitObject { StartTime = startTime }) + { + RelativeSizeAxes = Axes.X; + Height = 2; + + AddInternal(new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colour + }); + } + + [BackgroundDependencyLoader] + private void load() + { + direction.BindTo(scrollingInfo.Direction); + direction.BindValueChanged(onDirectionChanged, true); + } + + private void onDirectionChanged(ValueChangedEvent direction) + { + Origin = Anchor = direction.NewValue == ScrollingDirection.Up + ? Anchor.TopLeft + : Anchor.BottomLeft; + } + + protected override void UpdateStateTransforms(ArmedState state) + { + using (BeginAbsoluteSequence(HitObject.StartTime + 1000)) + this.FadeOut(); + } + } + } +} diff --git a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs index 1af7d06998..271e432e8d 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs @@ -18,6 +18,8 @@ namespace osu.Game.Rulesets.Mania.UI [Cached] public class ManiaPlayfield : ScrollingPlayfield { + public IReadOnlyList Stages => stages; + private readonly List stages = new List(); public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => stages.Any(s => s.ReceivePositionalInputAt(screenSpacePos)); From 91d1b15d5ad141444259933678490df5db29794c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 14 May 2020 19:55:07 +0900 Subject: [PATCH 0719/1117] Integrate grid with the mania composer --- .../Edit/ManiaHitObjectComposer.cs | 58 +++++++++++++++++-- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index d7c0889c0d..11523dd384 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -6,9 +6,12 @@ using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Mania.Objects; using System.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Input; 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; @@ -20,12 +23,27 @@ namespace osu.Game.Rulesets.Mania.Edit public class ManiaHitObjectComposer : HitObjectComposer, IManiaHitObjectComposer { private DrawableManiaEditRuleset drawableRuleset; + private ManiaBeatSnapGrid beatSnapGrid; + private InputManager inputManager; public ManiaHitObjectComposer(Ruleset ruleset) : base(ruleset) { } + [BackgroundDependencyLoader] + private void load() + { + AddInternal(beatSnapGrid = new ManiaBeatSnapGrid()); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + inputManager = GetContainingInputManager(); + } + /// /// Retrieves the column that intersects a screen-space position. /// @@ -42,11 +60,43 @@ namespace osu.Game.Rulesets.Mania.Edit public IScrollingInfo ScrollingInfo => drawableRuleset.ScrollingInfo; + protected override void Update() + { + base.Update(); + + if (BlueprintContainer.CurrentTool is SelectTool) + { + if (EditorBeatmap.SelectedHitObjects.Any()) + { + beatSnapGrid.SetRange(EditorBeatmap.SelectedHitObjects.Min(h => h.StartTime), EditorBeatmap.SelectedHitObjects.Max(h => h.GetEndTime())); + beatSnapGrid.Show(); + } + else + beatSnapGrid.Hide(); + } + else + { + var placementTime = GetSnappedPosition(ToLocalSpace(inputManager.CurrentState.Mouse.Position), 0).time; + beatSnapGrid.SetRange(placementTime, placementTime); + + beatSnapGrid.Show(); + } + } + public override (Vector2 position, double time) GetSnappedPosition(Vector2 position, double time) { - var hoc = Playfield.GetColumn(0).HitObjectContainer; + var beatSnapped = beatSnapGrid.GetSnappedPosition(position); - float targetPosition = hoc.ToLocalSpace(ToScreenSpace(position)).Y; + if (beatSnapped != null) + return beatSnapped.Value; + + return base.GetSnappedPosition(position, getTimeFromPosition(ToScreenSpace(position))); + } + + private double getTimeFromPosition(Vector2 screenSpacePosition) + { + var hoc = Playfield.Stages[0].HitObjectContainer; + float targetPosition = hoc.ToLocalSpace(screenSpacePosition).Y; if (drawableRuleset.ScrollingInfo.Direction.Value == ScrollingDirection.Down) { @@ -56,12 +106,10 @@ namespace osu.Game.Rulesets.Mania.Edit targetPosition = hoc.DrawHeight - targetPosition; } - double targetTime = drawableRuleset.ScrollingInfo.Algorithm.TimeAt(targetPosition, + return drawableRuleset.ScrollingInfo.Algorithm.TimeAt(targetPosition, EditorClock.CurrentTime, drawableRuleset.ScrollingInfo.TimeRange.Value, hoc.DrawHeight); - - return base.GetSnappedPosition(position, targetTime); } protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) From 42c3d892cd93a3671de896af87136244b36856c2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 14 May 2020 19:55:14 +0900 Subject: [PATCH 0720/1117] Only update alive lines --- osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs index 5a3fe29770..320912ed5b 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs @@ -131,7 +131,7 @@ namespace osu.Game.Rulesets.Mania.Edit linesDuring.Clear(); linesAfter.Clear(); - foreach (var line in grid.Objects.OfType()) + foreach (var line in grid.AliveObjects.OfType()) { if (line.HitObject.StartTime < minTime) linesBefore.Add(line); From 0e334940745c5958a30a877083907ca391282953 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 14 May 2020 19:58:39 +0900 Subject: [PATCH 0721/1117] Fix flashing when changing beat divisor --- osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs | 12 ++++++++++-- .../Edit/ManiaHitObjectComposer.cs | 4 ++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs index 320912ed5b..9cb9256a7e 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs @@ -119,7 +119,15 @@ namespace osu.Game.Rulesets.Mania.Edit return (new Vector2(position.X, minDistLinePosition.Y + noteOffset), minDistLine.HitObject.StartTime); } - public void SetRange(double minTime, double maxTime) => Schedule(() => + public void SetRange(double minTime, double maxTime) + { + if (LoadState >= LoadState.Ready) + setRange(minTime, maxTime); + else + Schedule(() => setRange(minTime, maxTime)); + } + + private void setRange(double minTime, double maxTime) { var linesBefore = new List(); var linesDuring = new List(); @@ -156,7 +164,7 @@ namespace osu.Game.Rulesets.Mania.Edit linesAfter[i].Colour = OsuColour.Gray(0.5f / (offset + 1)); } } - }); + } private class Grid : ScrollingHitObjectContainer { diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index 11523dd384..1266761d12 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -60,9 +60,9 @@ namespace osu.Game.Rulesets.Mania.Edit public IScrollingInfo ScrollingInfo => drawableRuleset.ScrollingInfo; - protected override void Update() + protected override void UpdateAfterChildren() { - base.Update(); + base.UpdateAfterChildren(); if (BlueprintContainer.CurrentTool is SelectTool) { From 3441ab457d7d05dffa990117aa7ab3b84ceaa709 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 14 May 2020 20:06:34 +0900 Subject: [PATCH 0722/1117] Fix hitobjects placed at non-beatsnapped times --- .../Edit/Blueprints/ManiaPlacementBlueprint.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs index 3fb03d642f..5fe53557b3 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs @@ -24,10 +24,15 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints protected Column Column; /// - /// The current mouse position, snapped to the closest column. + /// The current beat-snapped mouse position, snapped to the closest column. /// protected Vector2 SnappedMousePosition { get; private set; } + /// + /// The gameplay time at the current beat-snapped mouse position (). + /// + protected double SnappedTime { get; private set; } + /// /// The width of the closest column to the current mouse position. /// @@ -39,6 +44,9 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints [Resolved] private IScrollingInfo scrollingInfo { get; set; } + [Resolved] + private IDistanceSnapProvider snapProvider { get; set; } + protected ManiaPlacementBlueprint(T hitObject) : base(hitObject) { @@ -54,7 +62,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints return base.OnMouseDown(e); HitObject.Column = Column.Index; - BeginPlacement(TimeAt(e.ScreenSpaceMousePosition), true); + BeginPlacement(SnappedTime, true); return true; } @@ -70,6 +78,10 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints // Snap to the column var parentPos = Parent.ToLocalSpace(Column.ToScreenSpace(new Vector2(Column.DrawWidth / 2, 0))); SnappedMousePosition = new Vector2(parentPos.X, Parent.ToLocalSpace(screenSpacePosition).Y); + + SnappedTime = TimeAt(screenSpacePosition); + if (snapProvider != null) + (SnappedMousePosition, SnappedTime) = snapProvider.GetSnappedPosition(SnappedMousePosition, SnappedTime); } protected double TimeAt(Vector2 screenSpacePosition) From f3b1c32a85050ace4ad7f410913ea3665312e3de Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 May 2020 20:18:57 +0900 Subject: [PATCH 0723/1117] Update test logic for new exception type --- osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs index ef2b20de64..7a20bd364b 100644 --- a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs +++ b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs @@ -211,7 +211,7 @@ namespace osu.Game.Tests.NonVisual var osu = loadOsu(host); Assert.DoesNotThrow(() => osu.Migrate(customPath)); - Assert.Throws(() => osu.Migrate(customPath)); + Assert.Throws(() => osu.Migrate(customPath)); } finally { From 42f446faa9a8d19a0a49d970b8139e7bf28cf4c1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 May 2020 21:10:04 +0900 Subject: [PATCH 0724/1117] Fix remaining test failure --- osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs index b5d07ee7b1..95a1868392 100644 --- a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs @@ -14,7 +14,7 @@ namespace osu.Game.Overlays.Settings.Sections.General { protected override string Header => "Updates"; - [BackgroundDependencyLoader] + [BackgroundDependencyLoader(true)] private void load(Storage storage, OsuConfigManager config, OsuGame game) { Add(new SettingsEnumDropdown @@ -34,7 +34,7 @@ namespace osu.Game.Overlays.Settings.Sections.General Add(new SettingsButton { Text = "Change folder location...", - Action = () => game.PerformFromScreen(menu => menu.Push(new MigrationSelectScreen())) + Action = () => game?.PerformFromScreen(menu => menu.Push(new MigrationSelectScreen())) }); } } From e390d70b707c97909404b8f596dc2908a66605b4 Mon Sep 17 00:00:00 2001 From: Fukashi13 <48766178+Fukashi13@users.noreply.github.com> Date: Thu, 14 May 2020 14:33:12 +0200 Subject: [PATCH 0725/1117] bestMatch changes on entering section with screen top border --- osu.Game/Graphics/Containers/SectionsContainer.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index a3125614aa..8b866c8d21 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -189,15 +189,15 @@ namespace osu.Game.Graphics.Containers headerBackgroundContainer.Height = (ExpandableHeader?.LayoutSize.Y ?? 0) + (FixedHeader?.LayoutSize.Y ?? 0); headerBackgroundContainer.Y = ExpandableHeader?.Y ?? 0; - T bestMatch = null; - float minDiff = float.MaxValue; + T bestMatch = Children.FirstOrDefault(); + float minDiff = float.MinValue; float scrollOffset = FixedHeader?.LayoutSize.Y ?? 0; foreach (var section in Children) { - float diff = Math.Abs(scrollContainer.GetChildPosInContent(section) - currentScroll - scrollOffset); + float diff = scrollContainer.GetChildPosInContent(section) - currentScroll - scrollOffset; - if (diff < minDiff) + if ((minDiff < diff) & (diff < 0)) { minDiff = diff; bestMatch = section; From 155e918ca3063b4c03146144cc3b8f9a035d5566 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 May 2020 21:40:26 +0900 Subject: [PATCH 0726/1117] Remove unused parameter --- .../Overlays/Settings/Sections/Maintenance/GeneralSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs index 1dd079a8ab..832673703b 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs @@ -26,7 +26,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance private TriangleButton undeleteButton; [BackgroundDependencyLoader] - private void load(BeatmapManager beatmaps, ScoreManager scores, SkinManager skins, DialogOverlay dialogOverlay, OsuGame game) + private void load(BeatmapManager beatmaps, ScoreManager scores, SkinManager skins, DialogOverlay dialogOverlay) { if (beatmaps.SupportsImportFromStable) { From ef8375b442d78dea8737ef91b6bb5b3ecbf6e7ee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 May 2020 22:42:42 +0900 Subject: [PATCH 0727/1117] Add protection against migrating to a nested folder --- .../NonVisual/CustomDataDirectoryTest.cs | 24 +++++++++++++++++++ osu.Game/IO/OsuStorage.cs | 6 +++++ 2 files changed, 30 insertions(+) diff --git a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs index ef2b20de64..433067ffdd 100644 --- a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs +++ b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs @@ -220,6 +220,30 @@ namespace osu.Game.Tests.NonVisual } } + [Test] + public void TestMigrationToNestedTargetFails() + { + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestMigrationToSameTargetFails))) + { + try + { + var osu = loadOsu(host); + + Assert.DoesNotThrow(() => osu.Migrate(customPath)); + + string subFolder = Path.Combine(customPath, "sub"); + + Directory.CreateDirectory(subFolder); + + Assert.Throws(() => osu.Migrate(subFolder)); + } + finally + { + host.Exit(); + } + } + } + private OsuGameBase loadOsu(GameHost host) { var osu = new OsuGameBase(); diff --git a/osu.Game/IO/OsuStorage.cs b/osu.Game/IO/OsuStorage.cs index 71b01ce479..7c0af16a63 100644 --- a/osu.Game/IO/OsuStorage.cs +++ b/osu.Game/IO/OsuStorage.cs @@ -48,6 +48,12 @@ namespace osu.Game.IO var source = new DirectoryInfo(GetFullPath(".")); var destination = new DirectoryInfo(newLocation); + if (source.FullName == destination.FullName) + throw new ArgumentException("Destination provided is already the current location", nameof(newLocation)); + + if (destination.FullName.Contains(source.FullName)) + throw new ArgumentException("Destination provided is inside the source", nameof(newLocation)); + // ensure the new location has no files present, else hard abort if (destination.Exists) { From 827d75b152f17af984b9823dc4322a185fdaaa55 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 May 2020 22:44:27 +0900 Subject: [PATCH 0728/1117] Revert "Add protection against migrating to a nested folder" This reverts commit ef8375b442d78dea8737ef91b6bb5b3ecbf6e7ee. --- .../NonVisual/CustomDataDirectoryTest.cs | 24 ------------------- osu.Game/IO/OsuStorage.cs | 6 ----- 2 files changed, 30 deletions(-) diff --git a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs index 433067ffdd..ef2b20de64 100644 --- a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs +++ b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs @@ -220,30 +220,6 @@ namespace osu.Game.Tests.NonVisual } } - [Test] - public void TestMigrationToNestedTargetFails() - { - using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestMigrationToSameTargetFails))) - { - try - { - var osu = loadOsu(host); - - Assert.DoesNotThrow(() => osu.Migrate(customPath)); - - string subFolder = Path.Combine(customPath, "sub"); - - Directory.CreateDirectory(subFolder); - - Assert.Throws(() => osu.Migrate(subFolder)); - } - finally - { - host.Exit(); - } - } - } - private OsuGameBase loadOsu(GameHost host) { var osu = new OsuGameBase(); diff --git a/osu.Game/IO/OsuStorage.cs b/osu.Game/IO/OsuStorage.cs index 7c0af16a63..71b01ce479 100644 --- a/osu.Game/IO/OsuStorage.cs +++ b/osu.Game/IO/OsuStorage.cs @@ -48,12 +48,6 @@ namespace osu.Game.IO var source = new DirectoryInfo(GetFullPath(".")); var destination = new DirectoryInfo(newLocation); - if (source.FullName == destination.FullName) - throw new ArgumentException("Destination provided is already the current location", nameof(newLocation)); - - if (destination.FullName.Contains(source.FullName)) - throw new ArgumentException("Destination provided is inside the source", nameof(newLocation)); - // ensure the new location has no files present, else hard abort if (destination.Exists) { From 364aa5aa129af4dbf996d4942b2f4884b6d53dbd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 May 2020 22:42:42 +0900 Subject: [PATCH 0729/1117] Add protection against migrating to a nested folder --- .../NonVisual/CustomDataDirectoryTest.cs | 24 +++++++++++++++++++ osu.Game/IO/OsuStorage.cs | 3 +++ 2 files changed, 27 insertions(+) diff --git a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs index 7a20bd364b..e8f052cdeb 100644 --- a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs +++ b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs @@ -220,6 +220,30 @@ namespace osu.Game.Tests.NonVisual } } + [Test] + public void TestMigrationToNestedTargetFails() + { + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestMigrationToSameTargetFails))) + { + try + { + var osu = loadOsu(host); + + Assert.DoesNotThrow(() => osu.Migrate(customPath)); + + string subFolder = Path.Combine(customPath, "sub"); + + Directory.CreateDirectory(subFolder); + + Assert.Throws(() => osu.Migrate(subFolder)); + } + finally + { + host.Exit(); + } + } + } + private OsuGameBase loadOsu(GameHost host) { var osu = new OsuGameBase(); diff --git a/osu.Game/IO/OsuStorage.cs b/osu.Game/IO/OsuStorage.cs index 443f4fdb69..e3888c0c28 100644 --- a/osu.Game/IO/OsuStorage.cs +++ b/osu.Game/IO/OsuStorage.cs @@ -51,6 +51,9 @@ namespace osu.Game.IO if (source.FullName == destination.FullName) throw new ArgumentException("Destination provided is already the current location", nameof(newLocation)); + if (destination.FullName.Contains(source.FullName)) + throw new ArgumentException("Destination provided is inside the source", nameof(newLocation)); + // ensure the new location has no files present, else hard abort if (destination.Exists) { From 6ec55eb400a480dbf475565fdf48589b7e0ea50d Mon Sep 17 00:00:00 2001 From: Shivam Date: Thu, 14 May 2020 21:51:39 +0200 Subject: [PATCH 0730/1117] Give mappool scene its own video --- 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 b4c6d589d7..2c4fed8d86 100644 --- a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs +++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs @@ -42,7 +42,7 @@ namespace osu.Game.Tournament.Screens.MapPool { InternalChildren = new Drawable[] { - new TourneyVideo("gameplay") + new TourneyVideo("mappool") { Loop = true, RelativeSizeAxes = Axes.Both, From 1768beb690a9733f3ebf966c6576b8006c011984 Mon Sep 17 00:00:00 2001 From: Shivam Date: Thu, 14 May 2020 21:52:10 +0200 Subject: [PATCH 0731/1117] Rename class SeeingEditorScreen to SeedingEditorScreen --- .../Screens/Editors/SeedingEditorScreen.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tournament/Screens/Editors/SeedingEditorScreen.cs b/osu.Game.Tournament/Screens/Editors/SeedingEditorScreen.cs index 46bb7b83e3..52f761e50a 100644 --- a/osu.Game.Tournament/Screens/Editors/SeedingEditorScreen.cs +++ b/osu.Game.Tournament/Screens/Editors/SeedingEditorScreen.cs @@ -19,7 +19,7 @@ using osuTK; namespace osu.Game.Tournament.Screens.Editors { - public class SeedingEditorScreen : TournamentEditorScreen + public class SeedingEditorScreen : TournamentEditorScreen { private readonly TournamentTeam team; @@ -30,14 +30,14 @@ namespace osu.Game.Tournament.Screens.Editors this.team = team; } - public class SeeingResultRow : CompositeDrawable, IModelBacked + public class SeedingResultRow : CompositeDrawable, IModelBacked { public SeedingResult Model { get; } [Resolved] private LadderInfo ladderInfo { get; set; } - public SeeingResultRow(TournamentTeam team, SeedingResult round) + public SeedingResultRow(TournamentTeam team, SeedingResult round) { Model = round; @@ -281,6 +281,6 @@ namespace osu.Game.Tournament.Screens.Editors } } - protected override SeeingResultRow CreateDrawable(SeedingResult model) => new SeeingResultRow(team, model); + protected override SeedingResultRow CreateDrawable(SeedingResult model) => new SeedingResultRow(team, model); } } From 097fcfd9ad35832a190a9bee0f7bfd14f33aa146 Mon Sep 17 00:00:00 2001 From: Fukashi13 <48766178+Fukashi13@users.noreply.github.com> Date: Fri, 15 May 2020 00:06:58 +0200 Subject: [PATCH 0732/1117] Update osu.Game/Graphics/Containers/SectionsContainer.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Graphics/Containers/SectionsContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 8b866c8d21..5192a7ddea 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -197,7 +197,7 @@ namespace osu.Game.Graphics.Containers { float diff = scrollContainer.GetChildPosInContent(section) - currentScroll - scrollOffset; - if ((minDiff < diff) & (diff < 0)) + if (minDiff < diff && diff < 0) { minDiff = diff; bestMatch = section; From 25bbb02999481de9adecaa38f51e3e6293018bcd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 May 2020 22:57:41 +0900 Subject: [PATCH 0733/1117] Throw better exceptions from OsuStorage --- osu.Game/IO/OsuStorage.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/IO/OsuStorage.cs b/osu.Game/IO/OsuStorage.cs index 71b01ce479..8109631ef9 100644 --- a/osu.Game/IO/OsuStorage.cs +++ b/osu.Game/IO/OsuStorage.cs @@ -48,11 +48,14 @@ namespace osu.Game.IO var source = new DirectoryInfo(GetFullPath(".")); var destination = new DirectoryInfo(newLocation); + if (source.FullName == destination.FullName) + throw new ArgumentException("Destination provided is already the current location", nameof(newLocation)); + // ensure the new location has no files present, else hard abort if (destination.Exists) { if (destination.GetFiles().Length > 0 || destination.GetDirectories().Length > 0) - throw new InvalidOperationException("Migration destination already has files present"); + throw new ArgumentException("Destination provided already has files or directories present", nameof(newLocation)); deleteRecursive(destination); } From 19f117ae53fe3058ed251bd687a7cb8266e75cf8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 May 2020 20:18:57 +0900 Subject: [PATCH 0734/1117] Update test logic for new exception type --- osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs index ef2b20de64..7a20bd364b 100644 --- a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs +++ b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs @@ -211,7 +211,7 @@ namespace osu.Game.Tests.NonVisual var osu = loadOsu(host); Assert.DoesNotThrow(() => osu.Migrate(customPath)); - Assert.Throws(() => osu.Migrate(customPath)); + Assert.Throws(() => osu.Migrate(customPath)); } finally { From 0690d81bbb1bc7ee969b7e07050a565138b5b5c0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 May 2020 22:42:42 +0900 Subject: [PATCH 0735/1117] Add protection against migrating to a nested folder --- .../NonVisual/CustomDataDirectoryTest.cs | 48 +++++++++++++++++++ osu.Game/IO/OsuStorage.cs | 9 +++- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs index 7a20bd364b..2a98a6dbc6 100644 --- a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs +++ b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs @@ -220,6 +220,54 @@ namespace osu.Game.Tests.NonVisual } } + [Test] + public void TestMigrationToNestedTargetFails() + { + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestMigrationToSameTargetFails))) + { + try + { + var osu = loadOsu(host); + + Assert.DoesNotThrow(() => osu.Migrate(customPath)); + + string subFolder = Path.Combine(customPath, "sub"); + + Directory.CreateDirectory(subFolder); + + Assert.Throws(() => osu.Migrate(subFolder)); + } + finally + { + host.Exit(); + } + } + } + + [Test] + public void TestMigrationToSeeminglyNestedTarget() + { + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestMigrationToSeeminglyNestedTarget))) + { + try + { + var osu = loadOsu(host); + + Assert.DoesNotThrow(() => osu.Migrate(customPath)); + + string subFolder = customPath + "sub"; + + Directory.CreateDirectory(subFolder); + + osu.Migrate(subFolder); + } + finally + { + host.Exit(); + } + } + } + private OsuGameBase loadOsu(GameHost host) { var osu = new OsuGameBase(); diff --git a/osu.Game/IO/OsuStorage.cs b/osu.Game/IO/OsuStorage.cs index 8109631ef9..ac28a05375 100644 --- a/osu.Game/IO/OsuStorage.cs +++ b/osu.Game/IO/OsuStorage.cs @@ -48,9 +48,16 @@ namespace osu.Game.IO var source = new DirectoryInfo(GetFullPath(".")); var destination = new DirectoryInfo(newLocation); - if (source.FullName == destination.FullName) + // using Uri is the easiest way to check equality and contains (https://stackoverflow.com/a/7710620) + var sourceUri = new Uri(source.FullName + Path.DirectorySeparatorChar); + var destinationUri = new Uri(destination.FullName + Path.DirectorySeparatorChar); + + if (sourceUri == destinationUri) throw new ArgumentException("Destination provided is already the current location", nameof(newLocation)); + if (sourceUri.IsBaseOf(destinationUri)) + throw new ArgumentException("Destination provided is inside the source", nameof(newLocation)); + // ensure the new location has no files present, else hard abort if (destination.Exists) { From 7641507c90f770bab9e096dad63132001cc532d5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 May 2020 10:45:57 +0900 Subject: [PATCH 0736/1117] Ensure test directories are deleted before subsequent run --- osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs index 2a98a6dbc6..d69bf94ee2 100644 --- a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs +++ b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs @@ -233,6 +233,9 @@ namespace osu.Game.Tests.NonVisual string subFolder = Path.Combine(customPath, "sub"); + if (Directory.Exists(subFolder)) + Directory.Delete(subFolder, true); + Directory.CreateDirectory(subFolder); Assert.Throws(() => osu.Migrate(subFolder)); @@ -255,11 +258,14 @@ namespace osu.Game.Tests.NonVisual Assert.DoesNotThrow(() => osu.Migrate(customPath)); - string subFolder = customPath + "sub"; + string seeminglySubFolder = customPath + "sub"; - Directory.CreateDirectory(subFolder); + if (Directory.Exists(seeminglySubFolder)) + Directory.Delete(seeminglySubFolder, true); - osu.Migrate(subFolder); + Directory.CreateDirectory(seeminglySubFolder); + + osu.Migrate(seeminglySubFolder); } finally { From 94cf99bf978305b7f45eb0bfb1323c34e3525bb5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 May 2020 12:21:02 +0900 Subject: [PATCH 0737/1117] Fix mute button falling off the screen when UI scaling is used --- osu.Game/Overlays/VolumeOverlay.cs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/osu.Game/Overlays/VolumeOverlay.cs b/osu.Game/Overlays/VolumeOverlay.cs index 676d2c941a..eb639431ae 100644 --- a/osu.Game/Overlays/VolumeOverlay.cs +++ b/osu.Game/Overlays/VolumeOverlay.cs @@ -46,6 +46,13 @@ namespace osu.Game.Overlays Width = 300, Colour = ColourInfo.GradientHorizontal(Color4.Black.Opacity(0.75f), Color4.Black.Opacity(0)) }, + muteButton = new MuteButton + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Margin = new MarginPadding(10), + Current = { BindTarget = IsMuted } + }, new FillFlowContainer { Direction = FillDirection.Vertical, @@ -56,19 +63,11 @@ namespace osu.Game.Overlays Margin = new MarginPadding { Left = offset }, Children = new Drawable[] { - volumeMeterEffect = new VolumeMeter("EFFECTS", 125, colours.BlueDarker) - { - Margin = new MarginPadding { Top = 100 + MuteButton.HEIGHT } // to counter the mute button and re-center the volume meters - }, + volumeMeterEffect = new VolumeMeter("EFFECTS", 125, colours.BlueDarker), volumeMeterMaster = new VolumeMeter("MASTER", 150, colours.PinkDarker), volumeMeterMusic = new VolumeMeter("MUSIC", 125, colours.BlueDarker), - muteButton = new MuteButton - { - Margin = new MarginPadding { Top = 100 }, - Current = { BindTarget = IsMuted } - } } - }, + } }); volumeMeterMaster.Bindable.BindTo(audio.Volume); From aea192080a51f014d79da5caad8ce70507d65a74 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 May 2020 13:02:46 +0900 Subject: [PATCH 0738/1117] Fix incorrect storage name --- osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs index d69bf94ee2..743c924bbd 100644 --- a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs +++ b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs @@ -223,7 +223,7 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestMigrationToNestedTargetFails() { - using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestMigrationToSameTargetFails))) + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestMigrationToNestedTargetFails))) { try { From 4cbd51feb965fe78ba3429728205715788a83558 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 15 May 2020 13:08:15 +0900 Subject: [PATCH 0739/1117] Fix test errors --- .../Edit/Blueprints/ManiaPlacementBlueprint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs index 5fe53557b3..184356b89c 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs @@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints [Resolved] private IScrollingInfo scrollingInfo { get; set; } - [Resolved] + [Resolved(CanBeNull = true)] private IDistanceSnapProvider snapProvider { get; set; } protected ManiaPlacementBlueprint(T hitObject) From 6ca102bc3fb44d751986c3daa141825d8ed094ec Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 May 2020 13:19:03 +0900 Subject: [PATCH 0740/1117] Attempt delete operations more than once --- osu.Game/IO/OsuStorage.cs | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/osu.Game/IO/OsuStorage.cs b/osu.Game/IO/OsuStorage.cs index 5393cbf7ae..499bcb4063 100644 --- a/osu.Game/IO/OsuStorage.cs +++ b/osu.Game/IO/OsuStorage.cs @@ -84,7 +84,7 @@ namespace osu.Game.IO if (topLevelExcludes && IGNORE_FILES.Contains(fi.Name)) continue; - fi.Delete(); + attemptOperation(() => fi.Delete()); } foreach (DirectoryInfo dir in target.GetDirectories()) @@ -92,11 +92,11 @@ namespace osu.Game.IO if (topLevelExcludes && IGNORE_DIRECTORIES.Contains(dir.Name)) continue; - dir.Delete(true); + attemptOperation(() => dir.Delete(true)); } if (target.GetFiles().Length == 0 && target.GetDirectories().Length == 0) - target.Delete(); + attemptOperation(target.Delete); } private static void copyRecursive(DirectoryInfo source, DirectoryInfo destination, bool topLevelExcludes = true) @@ -109,7 +109,7 @@ namespace osu.Game.IO if (topLevelExcludes && IGNORE_FILES.Contains(fi.Name)) continue; - attemptCopy(fi, Path.Combine(destination.FullName, fi.Name)); + attemptOperation(() => fi.CopyTo(Path.Combine(destination.FullName, fi.Name), true)); } foreach (DirectoryInfo dir in source.GetDirectories()) @@ -121,24 +121,27 @@ namespace osu.Game.IO } } - private static void attemptCopy(System.IO.FileInfo fileInfo, string destination) + /// + /// Attempt an IO operation multiple times and only throw if none of the attempts succeed. + /// + /// The action to perform. + /// The number of attempts (250ms wait between each). + private static void attemptOperation(Action action, int attempts = 10) { - int tries = 5; - while (true) { try { - fileInfo.CopyTo(destination, true); + action(); return; } catch (Exception) { - if (tries-- == 0) + if (attempts-- == 0) throw; } - Thread.Sleep(50); + Thread.Sleep(250); } } } From 392d44e1fbe8fcf6f334fe31134821a01e7adbf5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 15 May 2020 15:49:50 +0900 Subject: [PATCH 0741/1117] Always fully display one beat --- .../Edit/ManiaBeatSnapGrid.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs index 9cb9256a7e..63e887714b 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.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; @@ -149,6 +150,29 @@ namespace osu.Game.Rulesets.Mania.Edit linesAfter.Add(line); } + // Snapping will always happen on one of the two lines around minTime (the "target" line). + // One of those lines may exist in linesBefore and the other may exist in linesAfter, depending on whether such a line exists, and the target changes when the mid-point is crossed. + // For display purposes, one complete beat is shown at the maximum brightness such that the target line should always be bright. + bool targetLineIsLastLineBefore = false; + + if (linesBefore.Count > 0 && linesAfter.Count > 0) + targetLineIsLastLineBefore = Math.Abs(linesBefore[^1].HitObject.StartTime - minTime) <= Math.Abs(linesAfter[0].HitObject.StartTime - minTime); + else if (linesBefore.Count > 0) + targetLineIsLastLineBefore = true; + + if (targetLineIsLastLineBefore) + { + // Move the last line before to linesDuring + linesDuring.Insert(0, linesBefore[^1]); + linesBefore.RemoveAt(linesBefore.Count - 1); + } + else if (linesAfter.Count > 0) // = false does not guarantee that a line after exists (maybe at the bottom of the screen) + { + // Move the first line after to linesDuring + linesDuring.Insert(0, linesAfter[0]); + linesAfter.RemoveAt(0); + } + foreach (var l in linesDuring) l.Colour = OsuColour.Gray(0.5f); From 1c6c128d1100ca3aa8ddd151c2f46a08701c30ed Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 15 May 2020 15:51:54 +0900 Subject: [PATCH 0742/1117] Add const --- osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs index 63e887714b..77d42a0927 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs @@ -24,6 +24,11 @@ namespace osu.Game.Rulesets.Mania.Edit { public class ManiaBeatSnapGrid : CompositeDrawable { + /// + /// The brightness of bar lines one beat around the time range from . + /// + private const float first_beat_brightness = 0.5f; + [Resolved] private IManiaHitObjectComposer composer { get; set; } @@ -174,18 +179,18 @@ namespace osu.Game.Rulesets.Mania.Edit } foreach (var l in linesDuring) - l.Colour = OsuColour.Gray(0.5f); + l.Colour = OsuColour.Gray(first_beat_brightness); for (int i = 0; i < linesBefore.Count; i++) { int offset = (linesBefore.Count - i - 1) / beatDivisor.Value; - linesBefore[i].Colour = OsuColour.Gray(0.5f / (offset + 1)); + linesBefore[i].Colour = OsuColour.Gray(first_beat_brightness / (offset + 1)); } for (int i = 0; i < linesAfter.Count; i++) { int offset = i / beatDivisor.Value; - linesAfter[i].Colour = OsuColour.Gray(0.5f / (offset + 1)); + linesAfter[i].Colour = OsuColour.Gray(first_beat_brightness / (offset + 1)); } } } From 238d87f97611be6b137e8bcc9c30073e318518a8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 15 May 2020 15:56:32 +0900 Subject: [PATCH 0743/1117] Add comment about gray usage --- osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs index 77d42a0927..31ebb7bc1c 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs @@ -178,6 +178,8 @@ namespace osu.Game.Rulesets.Mania.Edit linesAfter.RemoveAt(0); } + // Grays are used rather than transparency since the lines appear on a coloured mania playfield. + foreach (var l in linesDuring) l.Colour = OsuColour.Gray(first_beat_brightness); From aec2520ef41155ea14e7bdf0c68fce4a45c7e3e1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 May 2020 17:31:06 +0900 Subject: [PATCH 0744/1117] Avoid disabling a host-level bindable from osu! code --- .../Settings/Sections/Input/MouseSettings.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs index e7f2f21465..65fc07d1d9 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs @@ -17,12 +17,20 @@ namespace osu.Game.Overlays.Settings.Sections.Input protected override string Header => "Mouse"; private readonly BindableBool rawInputToggle = new BindableBool(); + private Bindable sensitivityBindable = new BindableDouble(); private Bindable ignoredInputHandler; private SensitivitySetting sensitivity; [BackgroundDependencyLoader] private void load(OsuConfigManager osuConfig, FrameworkConfigManager config) { + var configSensitivity = config.GetBindable(FrameworkSetting.CursorSensitivity); + + // use local bindable to avoid changing enabled state of game host's bindable. + sensitivityBindable = configSensitivity.GetUnboundCopy(); + configSensitivity.BindValueChanged(val => sensitivityBindable.Value = val.NewValue); + sensitivityBindable.BindValueChanged(val => configSensitivity.Value = val.NewValue); + Children = new Drawable[] { new SettingsCheckbox @@ -33,7 +41,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input sensitivity = new SensitivitySetting { LabelText = "Cursor sensitivity", - Bindable = config.GetBindable(FrameworkSetting.CursorSensitivity) + Bindable = sensitivityBindable }, new SettingsCheckbox { @@ -60,7 +68,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input if (RuntimeInfo.OS != RuntimeInfo.Platform.Windows) { rawInputToggle.Disabled = true; - sensitivity.Bindable.Disabled = true; + sensitivityBindable.Disabled = true; } else { From 98125102a768b5a711b5cab4367969f68e38b95f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 15 May 2020 18:07:41 +0900 Subject: [PATCH 0745/1117] Add cancellation token support to CreateNestedHitObjects() --- osu.Game.Rulesets.Catch/Objects/BananaShower.cs | 5 +++-- osu.Game.Rulesets.Catch/Objects/JuiceStream.cs | 5 +++-- osu.Game.Rulesets.Mania/Objects/HoldNote.cs | 5 +++-- osu.Game.Rulesets.Osu/Objects/Slider.cs | 5 +++-- osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs | 5 +++-- osu.Game.Rulesets.Taiko/Objects/Swell.cs | 5 +++-- .../Objects/TaikoHitObject.cs | 5 +++-- osu.Game/Rulesets/Edit/PlacementBlueprint.cs | 3 ++- osu.Game/Rulesets/Objects/HitObject.cs | 17 ++++++++++++++--- 9 files changed, 37 insertions(+), 18 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/BananaShower.cs b/osu.Game.Rulesets.Catch/Objects/BananaShower.cs index c3488aec11..96ab66048a 100644 --- a/osu.Game.Rulesets.Catch/Objects/BananaShower.cs +++ b/osu.Game.Rulesets.Catch/Objects/BananaShower.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.Threading; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects.Types; @@ -14,9 +15,9 @@ namespace osu.Game.Rulesets.Catch.Objects public override Judgement CreateJudgement() => new IgnoreJudgement(); - protected override void CreateNestedHitObjects() + protected override void CreateNestedHitObjects(CancellationToken cancellationToken) { - base.CreateNestedHitObjects(); + base.CreateNestedHitObjects(cancellationToken); createBananas(); } diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs index 01011645bd..4f9a289739 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; +using System.Threading; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; @@ -45,9 +46,9 @@ namespace osu.Game.Rulesets.Catch.Objects TickDistance = scoringDistance / difficulty.SliderTickRate; } - protected override void CreateNestedHitObjects() + protected override void CreateNestedHitObjects(CancellationToken cancellationToken) { - base.CreateNestedHitObjects(); + base.CreateNestedHitObjects(cancellationToken); var dropletSamples = Samples.Select(s => new HitSampleInfo { diff --git a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs index eea2c31260..d8bdaa071b 100644 --- a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/HoldNote.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 osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; @@ -91,9 +92,9 @@ namespace osu.Game.Rulesets.Mania.Objects tickSpacing = timingPoint.BeatLength / difficulty.SliderTickRate; } - protected override void CreateNestedHitObjects() + protected override void CreateNestedHitObjects(CancellationToken cancellationToken) { - base.CreateNestedHitObjects(); + base.CreateNestedHitObjects(cancellationToken); createTicks(); diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index e5d6c20738..bc6fca2338 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -6,6 +6,7 @@ using osu.Game.Rulesets.Objects.Types; using System.Collections.Generic; using osu.Game.Rulesets.Objects; using System.Linq; +using System.Threading; using osu.Framework.Caching; using osu.Game.Audio; using osu.Game.Beatmaps; @@ -133,9 +134,9 @@ namespace osu.Game.Rulesets.Osu.Objects TickDistance = scoringDistance / difficulty.SliderTickRate * TickDistanceMultiplier; } - protected override void CreateNestedHitObjects() + protected override void CreateNestedHitObjects(CancellationToken cancellationToken) { - base.CreateNestedHitObjects(); + base.CreateNestedHitObjects(cancellationToken); foreach (var e in SliderEventGenerator.Generate(StartTime, SpanDuration, Velocity, TickDistance, Path.Distance, this.SpanCount(), LegacyLastTickOffset)) diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs index dc2f277e58..8bbad220ac 100644 --- a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs @@ -4,6 +4,7 @@ using osu.Game.Rulesets.Objects.Types; using System; using System.Collections.Generic; +using System.Threading; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; @@ -73,14 +74,14 @@ namespace osu.Game.Rulesets.Taiko.Objects overallDifficulty = difficulty.OverallDifficulty; } - protected override void CreateNestedHitObjects() + protected override void CreateNestedHitObjects(CancellationToken cancellationToken) { createTicks(); RequiredGoodHits = NestedHitObjects.Count * Math.Min(0.15, 0.05 + 0.10 / 6 * overallDifficulty); RequiredGreatHits = NestedHitObjects.Count * Math.Min(0.30, 0.10 + 0.20 / 6 * overallDifficulty); - base.CreateNestedHitObjects(); + base.CreateNestedHitObjects(cancellationToken); } private void createTicks() diff --git a/osu.Game.Rulesets.Taiko/Objects/Swell.cs b/osu.Game.Rulesets.Taiko/Objects/Swell.cs index 2f06066a16..3a4023b3e5 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Swell.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Swell.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Threading; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; @@ -29,9 +30,9 @@ namespace osu.Game.Rulesets.Taiko.Objects set => throw new NotSupportedException($"{nameof(Swell)} cannot be a strong hitobject."); } - protected override void CreateNestedHitObjects() + protected override void CreateNestedHitObjects(CancellationToken cancellationToken) { - base.CreateNestedHitObjects(); + base.CreateNestedHitObjects(cancellationToken); for (int i = 0; i < RequiredHits; i++) AddNested(new SwellTick()); diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs index c41727557b..206bfcfdb2 100644 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.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.Threading; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; @@ -32,9 +33,9 @@ namespace osu.Game.Rulesets.Taiko.Objects ///
  • public virtual bool IsStrong { get; set; } - protected override void CreateNestedHitObjects() + protected override void CreateNestedHitObjects(CancellationToken cancellationToken) { - base.CreateNestedHitObjects(); + base.CreateNestedHitObjects(cancellationToken); if (IsStrong) AddNested(new StrongHitObject { StartTime = this.GetEndTime() }); diff --git a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs index fb1eb7adbf..49b57c3f30 100644 --- a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs +++ b/osu.Game/Rulesets/Edit/PlacementBlueprint.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.Threading; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -90,7 +91,7 @@ namespace osu.Game.Rulesets.Edit public abstract void UpdatePosition(Vector2 screenSpacePosition); /// - /// Invokes , + /// Invokes , /// refreshing and parameters for the . /// protected void ApplyDefaultsToHitObject() => HitObject.ApplyDefaults(beatmap.Value.Beatmap.ControlPointInfo, beatmap.Value.Beatmap.BeatmapInfo.BaseDifficulty); diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs index cffbdbae08..8ff2bdefb3 100644 --- a/osu.Game/Rulesets/Objects/HitObject.cs +++ b/osu.Game/Rulesets/Objects/HitObject.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using JetBrains.Annotations; using Newtonsoft.Json; using osu.Framework.Bindables; @@ -99,7 +100,8 @@ namespace osu.Game.Rulesets.Objects ///
    /// The control points. /// The difficulty settings to use. - public void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) + /// The cancellation token. + public void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty, CancellationToken cancellationToken = default) { ApplyDefaultsToSelf(controlPointInfo, difficulty); @@ -108,7 +110,7 @@ namespace osu.Game.Rulesets.Objects nestedHitObjects.Clear(); - CreateNestedHitObjects(); + CreateNestedHitObjects(cancellationToken); if (this is IHasComboInformation hasCombo) { @@ -122,7 +124,7 @@ namespace osu.Game.Rulesets.Objects nestedHitObjects.Sort((h1, h2) => h1.StartTime.CompareTo(h2.StartTime)); foreach (var h in nestedHitObjects) - h.ApplyDefaults(controlPointInfo, difficulty); + h.ApplyDefaults(controlPointInfo, difficulty, cancellationToken); DefaultsApplied?.Invoke(this); } @@ -136,6 +138,15 @@ namespace osu.Game.Rulesets.Objects HitWindows?.SetDifficulty(difficulty.OverallDifficulty); } + protected virtual void CreateNestedHitObjects(CancellationToken cancellationToken) + { + // ReSharper disable once MethodSupportsCancellation (https://youtrack.jetbrains.com/issue/RIDER-44520) +#pragma warning disable 618 + CreateNestedHitObjects(); +#pragma warning restore 618 + } + + [Obsolete("Use the overload with cancellation support instead.")] // can be removed 20201115 protected virtual void CreateNestedHitObjects() { } From 4079642d58572f7b342071e572b0b21c08e15b8b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 15 May 2020 18:13:47 +0900 Subject: [PATCH 0746/1117] Actually pass in the cancellation token --- osu.Game/Beatmaps/WorkingBeatmap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index bf2b9944a4..9ea023a030 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -134,7 +134,7 @@ namespace osu.Game.Beatmaps if (cancellationSource.IsCancellationRequested) throw new BeatmapLoadTimeoutException(BeatmapInfo); - obj.ApplyDefaults(converted.ControlPointInfo, converted.BeatmapInfo.BaseDifficulty); + obj.ApplyDefaults(converted.ControlPointInfo, converted.BeatmapInfo.BaseDifficulty, cancellationSource.Token); } foreach (var mod in mods.OfType()) From 4719fcc2913eaa002f0ac62f7f47b0a670c78f42 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 15 May 2020 18:17:39 +0900 Subject: [PATCH 0747/1117] Actually use the cancellation token --- osu.Game.Rulesets.Catch/Objects/JuiceStream.cs | 4 +++- osu.Game.Rulesets.Mania/Objects/HoldNote.cs | 6 ++++-- osu.Game.Rulesets.Osu/Objects/Slider.cs | 2 +- osu.Game/Beatmaps/WorkingBeatmap.cs | 15 +++++++++++---- osu.Game/Rulesets/Objects/SliderEventGenerator.cs | 10 +++++++--- 5 files changed, 26 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs index 4f9a289739..d32595c2e1 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs @@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Catch.Objects SliderEventDescriptor? lastEvent = null; - foreach (var e in SliderEventGenerator.Generate(StartTime, SpanDuration, Velocity, TickDistance, Path.Distance, this.SpanCount(), LegacyLastTickOffset)) + foreach (var e in SliderEventGenerator.Generate(StartTime, SpanDuration, Velocity, TickDistance, Path.Distance, this.SpanCount(), LegacyLastTickOffset, cancellationToken)) { // generate tiny droplets since the last point if (lastEvent != null) @@ -74,6 +74,8 @@ namespace osu.Game.Rulesets.Catch.Objects for (double t = timeBetweenTiny; t < sinceLastTick; t += timeBetweenTiny) { + cancellationToken.ThrowIfCancellationRequested(); + AddNested(new TinyDroplet { StartTime = t + lastEvent.Value.Time, diff --git a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs index d8bdaa071b..e6f722a8a9 100644 --- a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs @@ -96,7 +96,7 @@ namespace osu.Game.Rulesets.Mania.Objects { base.CreateNestedHitObjects(cancellationToken); - createTicks(); + createTicks(cancellationToken); AddNested(Head = new Note { @@ -113,13 +113,15 @@ namespace osu.Game.Rulesets.Mania.Objects }); } - private void createTicks() + private void createTicks(CancellationToken cancellationToken) { if (tickSpacing == 0) return; for (double t = StartTime + tickSpacing; t <= EndTime - tickSpacing; t += tickSpacing) { + cancellationToken.ThrowIfCancellationRequested(); + AddNested(new HoldNoteTick { StartTime = t, diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index bc6fca2338..6ba0e1c6aa 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -139,7 +139,7 @@ namespace osu.Game.Rulesets.Osu.Objects base.CreateNestedHitObjects(cancellationToken); foreach (var e in - SliderEventGenerator.Generate(StartTime, SpanDuration, Velocity, TickDistance, Path.Distance, this.SpanCount(), LegacyLastTickOffset)) + SliderEventGenerator.Generate(StartTime, SpanDuration, Velocity, TickDistance, Path.Distance, this.SpanCount(), LegacyLastTickOffset, cancellationToken)) { switch (e.Type) { diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 9ea023a030..8126311cbd 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -129,12 +129,19 @@ namespace osu.Game.Beatmaps processor?.PreProcess(); // Compute default values for hitobjects, including creating nested hitobjects in-case they're needed - foreach (var obj in converted.HitObjects) + try { - if (cancellationSource.IsCancellationRequested) - throw new BeatmapLoadTimeoutException(BeatmapInfo); + foreach (var obj in converted.HitObjects) + { + if (cancellationSource.IsCancellationRequested) + throw new BeatmapLoadTimeoutException(BeatmapInfo); - obj.ApplyDefaults(converted.ControlPointInfo, converted.BeatmapInfo.BaseDifficulty, cancellationSource.Token); + obj.ApplyDefaults(converted.ControlPointInfo, converted.BeatmapInfo.BaseDifficulty, cancellationSource.Token); + } + } + catch (OperationCanceledException) + { + throw new BeatmapLoadTimeoutException(BeatmapInfo); } foreach (var mod in mods.OfType()) diff --git a/osu.Game/Rulesets/Objects/SliderEventGenerator.cs b/osu.Game/Rulesets/Objects/SliderEventGenerator.cs index e9ee3833b7..5f1c1cf6a0 100644 --- a/osu.Game/Rulesets/Objects/SliderEventGenerator.cs +++ b/osu.Game/Rulesets/Objects/SliderEventGenerator.cs @@ -4,13 +4,14 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; namespace osu.Game.Rulesets.Objects { public static class SliderEventGenerator { public static IEnumerable Generate(double startTime, double spanDuration, double velocity, double tickDistance, double totalDistance, int spanCount, - double? legacyLastTickOffset) + double? legacyLastTickOffset, CancellationToken cancellationToken = default) { // A very lenient maximum length of a slider for ticks to be generated. // This exists for edge cases such as /b/1573664 where the beatmap has been edited by the user, and should never be reached in normal usage. @@ -37,7 +38,7 @@ namespace osu.Game.Rulesets.Objects var spanStartTime = startTime + span * spanDuration; var reversed = span % 2 == 1; - var ticks = generateTicks(span, spanStartTime, spanDuration, reversed, length, tickDistance, minDistanceFromEnd); + var ticks = generateTicks(span, spanStartTime, spanDuration, reversed, length, tickDistance, minDistanceFromEnd, cancellationToken); if (reversed) { @@ -108,12 +109,15 @@ namespace osu.Game.Rulesets.Objects /// The length of the path. /// The distance between each tick. /// The distance from the end of the path at which ticks are not allowed to be added. + /// The cancellation token. /// A for each tick. If is true, the ticks will be returned in reverse-StartTime order. private static IEnumerable generateTicks(int spanIndex, double spanStartTime, double spanDuration, bool reversed, double length, double tickDistance, - double minDistanceFromEnd) + double minDistanceFromEnd, CancellationToken cancellationToken = default) { for (var d = tickDistance; d <= length; d += tickDistance) { + cancellationToken.ThrowIfCancellationRequested(); + if (d >= length - minDistanceFromEnd) break; From 9b6525bb03a09323b38ec1e2296d63ce1a73a5d8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 May 2020 18:44:47 +0900 Subject: [PATCH 0748/1117] Fix applied platform/user offsets being incorrect when rate adjust mods are active --- osu.Game/Screens/Play/GameplayClockContainer.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index 591e969ad8..2f85d6ad1e 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -78,10 +78,10 @@ namespace osu.Game.Screens.Play // Lazer's audio timings in general doesn't match stable. This is the result of user testing, albeit limited. // This only seems to be required on windows. We need to eventually figure out why, with a bit of luck. - platformOffsetClock = new FramedOffsetClock(adjustableClock) { Offset = RuntimeInfo.OS == RuntimeInfo.Platform.Windows ? 15 : 0 }; + platformOffsetClock = new HardwareCorrectionOffsetClock(adjustableClock) { Offset = RuntimeInfo.OS == RuntimeInfo.Platform.Windows ? 15 : 0 }; // the final usable gameplay clock with user-set offsets applied. - userOffsetClock = new FramedOffsetClock(platformOffsetClock); + userOffsetClock = new HardwareCorrectionOffsetClock(platformOffsetClock); // the clock to be exposed via DI to children. GameplayClock = new GameplayClock(userOffsetClock); @@ -248,5 +248,16 @@ namespace osu.Game.Screens.Play speedAdjustmentsApplied = false; } } + + private class HardwareCorrectionOffsetClock : FramedOffsetClock + { + // we always want to apply the same real-time offset, so it should be adjusted by the playback rate to achieve this. + public override double CurrentTime => SourceTime + Offset * Rate; + + public HardwareCorrectionOffsetClock(IClock source, bool processSource = true) + : base(source, processSource) + { + } + } } } From 6cd1753459451bfe6672e149c1099ee5e514dd55 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 15 May 2020 18:51:44 +0900 Subject: [PATCH 0749/1117] Add overload to prevent crashes (bosu) --- osu.Game.Tests/Beatmaps/SliderEventGenerationTest.cs | 10 +++++----- osu.Game/Rulesets/Objects/SliderEventGenerator.cs | 8 ++++++++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/SliderEventGenerationTest.cs b/osu.Game.Tests/Beatmaps/SliderEventGenerationTest.cs index 9fba0f1668..6c8133660f 100644 --- a/osu.Game.Tests/Beatmaps/SliderEventGenerationTest.cs +++ b/osu.Game.Tests/Beatmaps/SliderEventGenerationTest.cs @@ -16,7 +16,7 @@ namespace osu.Game.Tests.Beatmaps [Test] public void TestSingleSpan() { - var events = SliderEventGenerator.Generate(start_time, span_duration, 1, span_duration / 2, span_duration, 1, null).ToArray(); + var events = SliderEventGenerator.Generate(start_time, span_duration, 1, span_duration / 2, span_duration, 1, null, default).ToArray(); Assert.That(events[0].Type, Is.EqualTo(SliderEventType.Head)); Assert.That(events[0].Time, Is.EqualTo(start_time)); @@ -31,7 +31,7 @@ namespace osu.Game.Tests.Beatmaps [Test] public void TestRepeat() { - var events = SliderEventGenerator.Generate(start_time, span_duration, 1, span_duration / 2, span_duration, 2, null).ToArray(); + var events = SliderEventGenerator.Generate(start_time, span_duration, 1, span_duration / 2, span_duration, 2, null, default).ToArray(); Assert.That(events[0].Type, Is.EqualTo(SliderEventType.Head)); Assert.That(events[0].Time, Is.EqualTo(start_time)); @@ -52,7 +52,7 @@ namespace osu.Game.Tests.Beatmaps [Test] public void TestNonEvenTicks() { - var events = SliderEventGenerator.Generate(start_time, span_duration, 1, 300, span_duration, 2, null).ToArray(); + var events = SliderEventGenerator.Generate(start_time, span_duration, 1, 300, span_duration, 2, null, default).ToArray(); Assert.That(events[0].Type, Is.EqualTo(SliderEventType.Head)); Assert.That(events[0].Time, Is.EqualTo(start_time)); @@ -85,7 +85,7 @@ namespace osu.Game.Tests.Beatmaps [Test] public void TestLegacyLastTickOffset() { - var events = SliderEventGenerator.Generate(start_time, span_duration, 1, span_duration / 2, span_duration, 1, 100).ToArray(); + var events = SliderEventGenerator.Generate(start_time, span_duration, 1, span_duration / 2, span_duration, 1, 100, default).ToArray(); Assert.That(events[2].Type, Is.EqualTo(SliderEventType.LegacyLastTick)); Assert.That(events[2].Time, Is.EqualTo(900)); @@ -97,7 +97,7 @@ namespace osu.Game.Tests.Beatmaps const double velocity = 5; const double min_distance = velocity * 10; - var events = SliderEventGenerator.Generate(start_time, span_duration, velocity, velocity, span_duration, 2, 0).ToArray(); + var events = SliderEventGenerator.Generate(start_time, span_duration, velocity, velocity, span_duration, 2, 0, default).ToArray(); Assert.Multiple(() => { diff --git a/osu.Game/Rulesets/Objects/SliderEventGenerator.cs b/osu.Game/Rulesets/Objects/SliderEventGenerator.cs index 5f1c1cf6a0..6df0041e7a 100644 --- a/osu.Game/Rulesets/Objects/SliderEventGenerator.cs +++ b/osu.Game/Rulesets/Objects/SliderEventGenerator.cs @@ -10,6 +10,14 @@ namespace osu.Game.Rulesets.Objects { public static class SliderEventGenerator { + [Obsolete("Use the overload with cancellation support instead.")] // can be removed 20201115 + public static IEnumerable Generate(double startTime, double spanDuration, double velocity, double tickDistance, double totalDistance, int spanCount, + double? legacyLastTickOffset) + { + return Generate(startTime, spanDuration, velocity, tickDistance, totalDistance, spanCount, legacyLastTickOffset, default); + } + + // ReSharper disable once MethodOverloadWithOptionalParameter public static IEnumerable Generate(double startTime, double spanDuration, double velocity, double tickDistance, double totalDistance, int spanCount, double? legacyLastTickOffset, CancellationToken cancellationToken = default) { From 65345109991272e138f5e6f9658ee1da9802b127 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 15 May 2020 19:25:14 +0900 Subject: [PATCH 0750/1117] Use cancellation token in taiko swell/drumroll --- osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs | 6 ++++-- osu.Game.Rulesets.Taiko/Objects/Swell.cs | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs index 8bbad220ac..7b11bce520 100644 --- a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs @@ -76,7 +76,7 @@ namespace osu.Game.Rulesets.Taiko.Objects protected override void CreateNestedHitObjects(CancellationToken cancellationToken) { - createTicks(); + createTicks(cancellationToken); RequiredGoodHits = NestedHitObjects.Count * Math.Min(0.15, 0.05 + 0.10 / 6 * overallDifficulty); RequiredGreatHits = NestedHitObjects.Count * Math.Min(0.30, 0.10 + 0.20 / 6 * overallDifficulty); @@ -84,7 +84,7 @@ namespace osu.Game.Rulesets.Taiko.Objects base.CreateNestedHitObjects(cancellationToken); } - private void createTicks() + private void createTicks(CancellationToken cancellationToken) { if (tickSpacing == 0) return; @@ -93,6 +93,8 @@ namespace osu.Game.Rulesets.Taiko.Objects for (double t = StartTime; t < EndTime + tickSpacing / 2; t += tickSpacing) { + cancellationToken.ThrowIfCancellationRequested(); + AddNested(new DrumRollTick { FirstTick = first, diff --git a/osu.Game.Rulesets.Taiko/Objects/Swell.cs b/osu.Game.Rulesets.Taiko/Objects/Swell.cs index 3a4023b3e5..390f8d1f3b 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Swell.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Swell.cs @@ -35,7 +35,10 @@ namespace osu.Game.Rulesets.Taiko.Objects base.CreateNestedHitObjects(cancellationToken); for (int i = 0; i < RequiredHits; i++) + { + cancellationToken.ThrowIfCancellationRequested(); AddNested(new SwellTick()); + } } public override Judgement CreateJudgement() => new TaikoSwellJudgement(); From c55eb8335135359ee20ee7a3900392b35981b6da Mon Sep 17 00:00:00 2001 From: Fukashi13 <48766178+Fukashi13@users.noreply.github.com> Date: Fri, 15 May 2020 14:03:45 +0200 Subject: [PATCH 0751/1117] last section gets selected when scrolling to bottom of list --- .../Graphics/Containers/SectionsContainer.cs | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 5192a7ddea..b9ed9b90fc 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -189,23 +189,18 @@ namespace osu.Game.Graphics.Containers headerBackgroundContainer.Height = (ExpandableHeader?.LayoutSize.Y ?? 0) + (FixedHeader?.LayoutSize.Y ?? 0); headerBackgroundContainer.Y = ExpandableHeader?.Y ?? 0; - T bestMatch = Children.FirstOrDefault(); - float minDiff = float.MinValue; float scrollOffset = FixedHeader?.LayoutSize.Y ?? 0; + Func diff = section => scrollContainer.GetChildPosInContent(section) - currentScroll - scrollOffset; - foreach (var section in Children) + if (scrollContainer.IsScrolledToEnd()) { - float diff = scrollContainer.GetChildPosInContent(section) - currentScroll - scrollOffset; - - if (minDiff < diff && diff < 0) - { - minDiff = diff; - bestMatch = section; - } + SelectedSection.Value = Children.LastOrDefault(); + } + else + { + SelectedSection.Value = Children.TakeWhile(section => diff(section) <= 0).LastOrDefault() + ?? Children.FirstOrDefault(); } - - if (bestMatch != null) - SelectedSection.Value = bestMatch; } } From 6416ace70d07f8a5d67bfd469ee93e73f1af5266 Mon Sep 17 00:00:00 2001 From: Fukashi13 <48766178+Fukashi13@users.noreply.github.com> Date: Fri, 15 May 2020 14:31:05 +0200 Subject: [PATCH 0752/1117] fixed indent --- osu.Game/Graphics/Containers/SectionsContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index b9ed9b90fc..d739f56828 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -199,7 +199,7 @@ namespace osu.Game.Graphics.Containers else { SelectedSection.Value = Children.TakeWhile(section => diff(section) <= 0).LastOrDefault() - ?? Children.FirstOrDefault(); + ?? Children.FirstOrDefault(); } } } From 4096463d02d8ac096f6e4c7d1d23d89b9575e0c9 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 15 May 2020 19:43:01 +0200 Subject: [PATCH 0753/1117] Move SkipOverlay internal alpha manipulation to a nested container and adjust visual tests. --- .../Visual/Gameplay/TestSceneSkipOverlay.cs | 30 ++++++++++++------- osu.Game/Screens/Play/Player.cs | 1 - osu.Game/Screens/Play/SkipOverlay.cs | 27 +++++++++-------- 3 files changed, 34 insertions(+), 24 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs index 6a0f86fe53..7c4ae4fc52 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs @@ -2,9 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; using osu.Game.Screens.Play; @@ -16,7 +16,7 @@ namespace osu.Game.Tests.Visual.Gameplay [TestFixture] public class TestSceneSkipOverlay : OsuManualInputManagerTestScene { - private SkipOverlay skip; + private TestSkipOverlay skip; private int requestCount; private double increment; @@ -37,7 +37,7 @@ namespace osu.Game.Tests.Visual.Gameplay RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - skip = new SkipOverlay(skip_time) + skip = new TestSkipOverlay(skip_time) { RequestSkip = () => { @@ -56,19 +56,19 @@ namespace osu.Game.Tests.Visual.Gameplay public void TestFadeOnIdle() { AddStep("move mouse", () => InputManager.MoveMouseTo(Vector2.Zero)); - AddUntilStep("fully visible", () => skip.Children.First().Alpha == 1); - AddUntilStep("wait for fade", () => skip.Children.First().Alpha < 1); + AddUntilStep("fully visible", () => skip.OverlayContents.Alpha == 1); + AddUntilStep("wait for fade", () => skip.OverlayContents.Alpha < 1); AddStep("move mouse", () => InputManager.MoveMouseTo(skip.ScreenSpaceDrawQuad.Centre)); - AddUntilStep("fully visible", () => skip.Children.First().Alpha == 1); - AddUntilStep("wait for fade", () => skip.Children.First().Alpha < 1); + AddUntilStep("fully visible", () => skip.OverlayContents.Alpha == 1); + AddUntilStep("wait for fade", () => skip.OverlayContents.Alpha < 1); } [Test] public void TestClickableAfterFade() { AddStep("move mouse", () => InputManager.MoveMouseTo(skip.ScreenSpaceDrawQuad.Centre)); - AddUntilStep("wait for fade", () => skip.Children.First().Alpha == 0); + AddUntilStep("wait for fade", () => skip.OverlayContents.Alpha == 0); AddStep("click", () => InputManager.Click(MouseButton.Left)); checkRequestCount(1); } @@ -105,13 +105,23 @@ namespace osu.Game.Tests.Visual.Gameplay { AddStep("move mouse", () => InputManager.MoveMouseTo(skip.ScreenSpaceDrawQuad.Centre)); AddStep("button down", () => InputManager.PressButton(MouseButton.Left)); - AddUntilStep("wait for overlay disappear", () => !skip.IsPresent); - AddAssert("ensure button didn't disappear", () => skip.Children.First().Alpha > 0); + AddUntilStep("wait for overlay disappear", () => !skip.Child.IsPresent); + AddAssert("ensure button didn't disappear", () => skip.OverlayContents.Alpha > 0); AddStep("button up", () => InputManager.ReleaseButton(MouseButton.Left)); checkRequestCount(0); } private void checkRequestCount(int expected) => AddAssert($"request count is {expected}", () => requestCount == expected); + + private class TestSkipOverlay : SkipOverlay + { + public TestSkipOverlay(double startTime) + : base(startTime) + { + } + + public Drawable OverlayContents => (Child as Container).Child; + } } } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index b24cef8eae..02c7b671a3 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -191,7 +191,6 @@ namespace osu.Game.Screens.Play HUDOverlay.ShowHud.Value = false; HUDOverlay.ShowHud.Disabled = true; BreakOverlay.Hide(); - skipOverlay.Disabled = true; skipOverlay.Hide(); } diff --git a/osu.Game/Screens/Play/SkipOverlay.cs b/osu.Game/Screens/Play/SkipOverlay.cs index 757dcd21ed..fec35df4e3 100644 --- a/osu.Game/Screens/Play/SkipOverlay.cs +++ b/osu.Game/Screens/Play/SkipOverlay.cs @@ -24,13 +24,14 @@ using osu.Game.Input.Bindings; namespace osu.Game.Screens.Play { - public class SkipOverlay : VisibilityContainer, IKeyBindingHandler + public class SkipOverlay : Container, IKeyBindingHandler { private readonly double startTime; public Action RequestSkip; private Button button; + private ButtonContainer buttonContainer; private Box remainingTimeBox; private FadeContainer fadeContainer; @@ -39,8 +40,6 @@ namespace osu.Game.Screens.Play [Resolved] private GameplayClock gameplayClock { get; set; } - public bool Disabled { get; set; } - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; /// @@ -63,9 +62,10 @@ namespace osu.Game.Screens.Play [BackgroundDependencyLoader(true)] private void load(OsuColour colours) { - Children = new Drawable[] + Child = buttonContainer = new ButtonContainer { - fadeContainer = new FadeContainer + RelativeSizeAxes = Axes.Both, + Child = fadeContainer = new FadeContainer { RelativeSizeAxes = Axes.Both, Children = new Drawable[] @@ -106,15 +106,8 @@ namespace osu.Game.Screens.Play button.Action = () => RequestSkip?.Invoke(); displayTime = gameplayClock.CurrentTime; - - if (!Disabled) - Show(); } - protected override void PopIn() => this.FadeIn(fade_time); - - protected override void PopOut() => this.FadeOut(fade_time); - protected override void Update() { base.Update(); @@ -124,13 +117,14 @@ namespace osu.Game.Screens.Play remainingTimeBox.Width = (float)Interpolation.Lerp(remainingTimeBox.Width, progress, Math.Clamp(Time.Elapsed / 40, 0, 1)); button.Enabled.Value = progress > 0; - State.Value = progress > 0 && !Disabled ? Visibility.Visible : Visibility.Hidden; + buttonContainer.State.Value = progress > 0 ? Visibility.Visible : Visibility.Hidden; } protected override bool OnMouseMove(MouseMoveEvent e) { if (!e.HasAnyButtonPressed) fadeContainer.Show(); + return base.OnMouseMove(e); } @@ -217,6 +211,13 @@ namespace osu.Game.Screens.Play public override void Show() => State = Visibility.Visible; } + private class ButtonContainer : VisibilityContainer + { + protected override void PopIn() => this.FadeIn(fade_time); + + protected override void PopOut() => this.FadeOut(fade_time); + } + private class Button : OsuClickableContainer { private Color4 colourNormal; From ed9d6f28297c2801e1e6ad87bb81205e8d66c5e0 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 15 May 2020 22:58:15 +0200 Subject: [PATCH 0754/1117] Fix CI inspection. --- osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs index 7c4ae4fc52..e093542d1e 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs @@ -121,7 +121,7 @@ namespace osu.Game.Tests.Visual.Gameplay { } - public Drawable OverlayContents => (Child as Container).Child; + public Drawable OverlayContents => (Child as Container)?.Child; } } } From c47f02c3b72cd1f499817cd133a34dfbf9777c55 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 16 May 2020 08:24:02 +0900 Subject: [PATCH 0755/1117] Update second instance of disabling bindable --- osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs index 65fc07d1d9..f139a704bc 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs @@ -86,7 +86,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input { bool raw = !handler.NewValue.Contains("Raw"); rawInputToggle.Value = raw; - sensitivity.Bindable.Disabled = !raw; + sensitivityBindable.Disabled = !raw; }; ignoredInputHandler.TriggerChange(); From c931bae70ec13b10f45c89ad67d223c635046186 Mon Sep 17 00:00:00 2001 From: Shivam Date: Sat, 16 May 2020 03:07:27 +0200 Subject: [PATCH 0756/1117] Add back button to TournamentScreen and the inputhandler for it --- .../Screens/Editors/TournamentEditorScreen.cs | 45 ++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Screens/Editors/TournamentEditorScreen.cs b/osu.Game.Tournament/Screens/Editors/TournamentEditorScreen.cs index 8e5df72cc8..11cb072c6b 100644 --- a/osu.Game.Tournament/Screens/Editors/TournamentEditorScreen.cs +++ b/osu.Game.Tournament/Screens/Editors/TournamentEditorScreen.cs @@ -9,6 +9,9 @@ using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics.UserInterface; +using osu.Game.Input.Bindings; +using osu.Framework.Input.Bindings; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Overlays.Settings; @@ -17,7 +20,7 @@ using osuTK; namespace osu.Game.Tournament.Screens.Editors { - public abstract class TournamentEditorScreen : TournamentScreen, IProvideVideo + public abstract class TournamentEditorScreen : TournamentScreen, IProvideVideo, IKeyBindingHandler where TDrawable : Drawable, IModelBacked where TModel : class, new() { @@ -25,8 +28,19 @@ namespace osu.Game.Tournament.Screens.Editors private FillFlowContainer flow; + [Resolved(canBeNull: true)] + private TournamentSceneManager sceneManager { get; set; } + protected ControlPanel ControlPanel; + protected virtual bool IsSubScreen => false; + + protected virtual System.Type ParentScreen { get; set; } + + private BackButton backButton; + + private System.Action backAction => () => sceneManager?.SetScreen(ParentScreen); + [BackgroundDependencyLoader] private void load() { @@ -70,6 +84,19 @@ namespace osu.Game.Tournament.Screens.Editors } }); + if (IsSubScreen) + { + BackButton.Receptor receptor = new BackButton.Receptor(); + backButton = new BackButton(receptor) + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Action = () => { backAction.Invoke(); } + }; + AddInternal(backButton); + backButton.Show(); + } + Storage.CollectionChanged += (_, args) => { switch (args.Action) @@ -88,6 +115,22 @@ namespace osu.Game.Tournament.Screens.Editors flow.Add(CreateDrawable(model)); } + public bool OnPressed(GlobalAction action) + { + switch (action) + { + case GlobalAction.Back: + backAction.Invoke(); + return true; + } + + return false; + } + + public void OnReleased(GlobalAction action) + { + } + protected abstract TDrawable CreateDrawable(TModel model); } } From bf6ce390ff39d6db4c82897daf561af332491502 Mon Sep 17 00:00:00 2001 From: Shivam Date: Sat, 16 May 2020 03:07:51 +0200 Subject: [PATCH 0757/1117] Add sub screen implementation to SeedingEditorScreen --- osu.Game.Tournament/Screens/Editors/SeedingEditorScreen.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game.Tournament/Screens/Editors/SeedingEditorScreen.cs b/osu.Game.Tournament/Screens/Editors/SeedingEditorScreen.cs index 52f761e50a..0f980ec9a3 100644 --- a/osu.Game.Tournament/Screens/Editors/SeedingEditorScreen.cs +++ b/osu.Game.Tournament/Screens/Editors/SeedingEditorScreen.cs @@ -25,6 +25,13 @@ namespace osu.Game.Tournament.Screens.Editors protected override BindableList Storage => team.SeedingResults; + [Resolved(canBeNull: true)] + private TournamentSceneManager sceneManager { get; set; } + + protected override bool IsSubScreen => true; + + protected override System.Type ParentScreen => typeof(TeamEditorScreen); + public SeedingEditorScreen(TournamentTeam team) { this.team = team; From 9d3df14179b740231e25bbb45f1d7b51e94b5840 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 16 May 2020 11:03:27 +0900 Subject: [PATCH 0758/1117] Remove unused variable --- osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs index f139a704bc..d27ab63fb7 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs @@ -19,7 +19,6 @@ namespace osu.Game.Overlays.Settings.Sections.Input private readonly BindableBool rawInputToggle = new BindableBool(); private Bindable sensitivityBindable = new BindableDouble(); private Bindable ignoredInputHandler; - private SensitivitySetting sensitivity; [BackgroundDependencyLoader] private void load(OsuConfigManager osuConfig, FrameworkConfigManager config) @@ -38,7 +37,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input LabelText = "Raw input", Bindable = rawInputToggle }, - sensitivity = new SensitivitySetting + new SensitivitySetting { LabelText = "Cursor sensitivity", Bindable = sensitivityBindable From b1243d6a8787a71701898de661a91fd457a827b7 Mon Sep 17 00:00:00 2001 From: Shivam Date: Sat, 16 May 2020 04:05:01 +0200 Subject: [PATCH 0759/1117] Add padding to so the back button is not in the way --- .../Screens/Editors/TournamentEditorScreen.cs | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tournament/Screens/Editors/TournamentEditorScreen.cs b/osu.Game.Tournament/Screens/Editors/TournamentEditorScreen.cs index 11cb072c6b..bca0814d3a 100644 --- a/osu.Game.Tournament/Screens/Editors/TournamentEditorScreen.cs +++ b/osu.Game.Tournament/Screens/Editors/TournamentEditorScreen.cs @@ -44,6 +44,18 @@ namespace osu.Game.Tournament.Screens.Editors [BackgroundDependencyLoader] private void load() { + BackButton.Receptor receptor = new BackButton.Receptor(); + backButton = new BackButton(receptor) + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Action = () => + { + if (IsSubScreen) + backAction.Invoke(); + } + }; + AddRangeInternal(new Drawable[] { new Box @@ -56,6 +68,7 @@ namespace osu.Game.Tournament.Screens.Editors RelativeSizeAxes = Axes.Both, Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, + Padding = new MarginPadding { Bottom = backButton.Height }, Child = flow = new FillFlowContainer { Direction = FillDirection.Vertical, @@ -64,6 +77,7 @@ namespace osu.Game.Tournament.Screens.Editors Spacing = new Vector2(20) }, }, + backButton, ControlPanel = new ControlPanel { Children = new Drawable[] @@ -85,17 +99,7 @@ namespace osu.Game.Tournament.Screens.Editors }); if (IsSubScreen) - { - BackButton.Receptor receptor = new BackButton.Receptor(); - backButton = new BackButton(receptor) - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Action = () => { backAction.Invoke(); } - }; - AddInternal(backButton); backButton.Show(); - } Storage.CollectionChanged += (_, args) => { From 648999a2de9e9cfc0bc25e804337c8d6f2d6cf30 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 16 May 2020 18:17:12 +0900 Subject: [PATCH 0760/1117] Remove all RequiredTypes usages --- .../CatchSkinnableTestScene.cs | 9 ------- .../TestSceneBananaShower.cs | 15 ----------- .../TestSceneCatcher.cs | 9 ------- .../TestSceneDrawableHitObjects.cs | 10 ------- .../TestSceneDrawableHitObjectsHidden.cs | 5 ---- .../TestSceneFruitObjects.cs | 19 -------------- .../TestSceneHyperDash.cs | 6 ----- .../Skinning/ManiaSkinnableTestScene.cs | 10 ------- .../Skinning/TestSceneDrawableJudgement.cs | 7 ----- .../Skinning/TestSceneHitExplosion.cs | 9 ------- .../Skinning/TestSceneKeyArea.cs | 9 ------- .../Skinning/TestSceneStageBackground.cs | 10 ------- .../Skinning/TestSceneStageForeground.cs | 9 ------- .../TestSceneColumn.cs | 10 ------- .../TestSceneManiaHitObjectComposer.cs | 7 ----- .../TestSceneNotes.cs | 8 ------ .../OsuSkinnableTestScene.cs | 9 ------- .../TestSceneDrawableJudgement.cs | 7 ----- .../TestSceneGameplayCursor.cs | 14 ---------- .../TestSceneHitCircle.cs | 7 ----- .../TestSceneHitCircleHidden.cs | 5 ---- .../TestSceneOsuDistanceSnapGrid.cs | 7 ----- .../TestScenePathControlPointVisualiser.cs | 10 ------- .../TestSceneResumeOverlay.cs | 7 ----- .../TestSceneSlider.cs | 18 ------------- .../TestSceneSliderHidden.cs | 5 ---- .../TestSceneSliderInput.cs | 14 ---------- .../TestSceneSliderSelectionBlueprint.cs | 12 --------- .../TestSceneSpinner.cs | 10 ------- .../TestSceneSpinnerHidden.cs | 5 ---- .../TestSceneSpinnerSelectionBlueprint.cs | 9 ------- .../TestSceneSpinnerSpunOut.cs | 11 -------- .../Skinning/TaikoSkinnableTestScene.cs | 9 ------- .../Skinning/TestSceneDrawableBarLine.cs | 11 -------- .../Skinning/TestSceneDrawableDrumRoll.cs | 11 -------- .../Skinning/TestSceneDrawableHit.cs | 11 -------- .../Skinning/TestSceneDrawableTaikoMascot.cs | 7 ----- .../Skinning/TestSceneHitExplosion.cs | 11 -------- .../Skinning/TestSceneInputDrum.cs | 10 ------- .../Skinning/TestSceneTaikoPlayfield.cs | 11 -------- .../Background/TestSceneUserDimBackgrounds.cs | 11 -------- .../Editing/TestSceneBeatDivisorControl.cs | 2 -- .../TestSceneEditorComposeRadioButtons.cs | 4 --- .../Visual/Editing/TestSceneEditorMenuBar.cs | 4 --- .../Editing/TestSceneEditorSummaryTimeline.cs | 4 --- .../Editing/TestSceneHitObjectComposer.cs | 18 ------------- .../TestSceneTimelineBlueprintContainer.cs | 7 ----- .../Visual/Editing/TestSceneTimingScreen.cs | 14 ---------- .../Visual/Editing/TimelineTestScene.cs | 10 ------- .../Visual/Gameplay/TestSceneBreakTracker.cs | 6 ----- .../Visual/Gameplay/TestSceneFailAnimation.cs | 8 ------ .../Gameplay/TestSceneGameplayMenuOverlay.cs | 3 --- .../Visual/Gameplay/TestSceneHitErrorMeter.cs | 9 ------- .../Visual/Gameplay/TestSceneKeyCounter.cs | 9 ------- .../Visual/Gameplay/TestSceneMedalOverlay.cs | 9 ------- .../TestSceneNightcoreBeatContainer.cs | 7 ----- .../Gameplay/TestSceneReplayDownloadButton.cs | 7 ----- .../Gameplay/TestSceneScrollingHitObjects.cs | 3 --- .../Visual/Gameplay/TestSceneSongProgress.cs | 6 ----- osu.Game.Tests/Visual/Menus/IntroTestScene.cs | 9 ------- .../Visual/Menus/TestSceneToolbar.cs | 10 ------- .../TestSceneDrawableRoomPlaylist.cs | 7 ----- .../Multiplayer/TestSceneLoungeRoomInfo.cs | 6 ----- .../TestSceneLoungeRoomsContainer.cs | 7 ----- .../Multiplayer/TestSceneMatchHeader.cs | 7 ----- .../TestSceneMatchLeaderboardChatDisplay.cs | 7 ----- .../TestSceneMatchSettingsOverlay.cs | 6 ----- .../Multiplayer/TestSceneMatchSongSelect.cs | 6 ----- .../Multiplayer/TestSceneMatchSubScreen.cs | 10 ------- .../Multiplayer/TestSceneMultiScreen.cs | 11 -------- .../TestSceneOverlinedParticipants.cs | 9 ------- .../Visual/Multiplayer/TestSceneRoomStatus.cs | 9 ------- .../Online/TestSceneAccountCreationOverlay.cs | 13 ---------- .../Online/TestSceneBeatmapListingOverlay.cs | 9 ------- .../Online/TestSceneBeatmapRulesetSelector.cs | 7 ----- .../Online/TestSceneBeatmapSetOverlay.cs | 26 ------------------- .../TestSceneBeatmapSetOverlayDetails.cs | 6 ----- .../TestSceneBeatmapSetOverlaySuccessRate.cs | 7 ----- .../Online/TestSceneChangelogOverlay.cs | 12 --------- .../Online/TestSceneChannelTabControl.cs | 6 ----- .../Online/TestSceneChatLineTruncation.cs | 10 ------- .../Visual/Online/TestSceneChatLink.cs | 12 --------- .../Visual/Online/TestSceneChatOverlay.cs | 13 ---------- .../Online/TestSceneCommentsContainer.cs | 15 ----------- .../Visual/Online/TestSceneCommentsHeader.cs | 9 ------- .../Visual/Online/TestSceneCommentsPage.cs | 6 ----- .../Online/TestSceneDashboardOverlay.cs | 11 -------- .../Online/TestSceneDirectDownloadButton.cs | 7 ----- .../Visual/Online/TestSceneDirectPanel.cs | 8 ------ .../Online/TestSceneExternalLinkButton.cs | 4 --- .../Visual/Online/TestSceneFriendDisplay.cs | 7 ----- .../Online/TestSceneHistoricalSection.cs | 10 ------- .../Visual/Online/TestSceneKudosuHistory.cs | 5 ---- .../Online/TestSceneLeaderboardModSelector.cs | 7 ----- .../TestSceneLeaderboardScopeSelector.cs | 7 ----- .../Online/TestSceneProfileCounterPill.cs | 7 ----- .../Online/TestSceneProfileRulesetSelector.cs | 8 ------ .../Visual/Online/TestSceneRankGraph.cs | 9 ------- .../Online/TestSceneRankingsCountryFilter.cs | 8 ------ .../Visual/Online/TestSceneRankingsHeader.cs | 9 ------- .../Visual/Online/TestSceneRankingsOverlay.cs | 15 ----------- .../TestSceneRankingsSpotlightSelector.cs | 5 ---- .../Visual/Online/TestSceneRankingsTables.cs | 12 --------- .../Visual/Online/TestSceneScoresContainer.cs | 10 ------- .../Visual/Online/TestSceneShowMoreButton.cs | 7 ----- .../Visual/Online/TestSceneSocialOverlay.cs | 11 -------- .../Online/TestSceneSpotlightsLayout.cs | 8 ------ .../Online/TestSceneTotalCommentsCounter.cs | 7 ----- .../Visual/Online/TestSceneUserPanel.cs | 8 ------ .../Online/TestSceneUserProfileHeader.cs | 16 ------------ .../Online/TestSceneUserProfileOverlay.cs | 13 ---------- .../TestSceneUserProfilePreviousUsernames.cs | 6 ----- .../TestSceneUserProfileRecentSection.cs | 10 ------- .../Online/TestSceneUserProfileScores.cs | 9 ------- .../Visual/Online/TestSceneUserRanks.cs | 10 ------- .../Visual/Online/TestSceneVotePill.cs | 7 ----- .../Visual/Ranking/TestSceneAccuracyCircle.cs | 10 ------- .../TestSceneExpandedPanelMiddleContent.cs | 15 ----------- .../Visual/Ranking/TestSceneResultsScreen.cs | 8 ------ .../Visual/Ranking/TestSceneScorePanel.cs | 8 ------ .../Settings/TestSceneKeyBindingPanel.cs | 13 ---------- .../Visual/Settings/TestSceneSettingsPanel.cs | 9 ------- .../SongSelect/TestSceneBeatmapCarousel.cs | 15 ----------- .../SongSelect/TestSceneBeatmapLeaderboard.cs | 11 -------- .../SongSelect/TestScenePlaySongSelect.cs | 18 ------------- osu.Game.Tests/Visual/TestSceneOsuGame.cs | 5 ---- .../UserInterface/TestSceneBackButton.cs | 7 ----- .../TestSceneBeatSyncedContainer.cs | 5 ---- .../TestSceneBeatmapListingSearchControl.cs | 7 ----- .../TestSceneBeatmapListingSortTabControl.cs | 7 ----- .../TestSceneBeatmapSearchFilter.cs | 8 ------ .../UserInterface/TestSceneButtonSystem.cs | 8 ------ .../UserInterface/TestSceneCommentEditor.cs | 8 ------ .../TestSceneDeleteLocalScore.cs | 12 --------- .../TestSceneFooterButtonMods.cs | 6 ----- .../TestSceneFriendsOnlineStatusControl.cs | 10 ------- .../TestSceneHoldToConfirmOverlay.cs | 9 ------- .../TestSceneLabelledSwitchButton.cs | 8 ------ .../UserInterface/TestSceneLabelledTextBox.cs | 7 ----- .../UserInterface/TestSceneLoadingLayer.cs | 4 --- .../TestSceneLogoTrackingContainer.cs | 14 ---------- .../TestSceneModSelectOverlay.cs | 16 ------------ .../TestSceneNotificationOverlay.cs | 11 -------- .../UserInterface/TestSceneNumberBox.cs | 7 ----- .../Visual/UserInterface/TestSceneOsuMenu.cs | 8 ------ .../UserInterface/TestSceneOverlayHeader.cs | 14 ---------- .../TestSceneOverlayHeaderBackground.cs | 7 ----- .../TestSceneOverlayRulesetSelector.cs | 8 ------ .../TestSceneOverlayScrollContainer.cs | 7 ----- .../UserInterface/TestScenePlaylistOverlay.cs | 7 ----- .../UserInterface/TestScenePopupDialog.cs | 11 -------- .../TestSceneStatefulMenuItem.cs | 9 ------- .../UserInterface/TestSceneToggleMenuItem.cs | 9 ------- .../TestSceneToolbarRulesetSelector.cs | 8 ------ .../UserInterface/TestSceneUserListToolbar.cs | 10 ------- .../UserInterface/TestSceneVolumePieces.cs | 4 --- .../TestSceneDrawableTournamentMatch.cs | 9 ------- .../TestSceneDrawableTournamentTeam.cs | 13 ---------- .../Components/TestSceneMatchHeader.cs | 9 ------- .../Components/TestSceneRoundDisplay.cs | 8 ------ .../Screens/TestSceneGameplayScreen.cs | 15 ----------- osu.Game/Tests/Visual/EditorTestScene.cs | 4 --- osu.Game/Tests/Visual/ModTestScene.cs | 5 ---- 163 files changed, 1471 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/CatchSkinnableTestScene.cs b/osu.Game.Rulesets.Catch.Tests/CatchSkinnableTestScene.cs index 0c46b078b5..378772fea3 100644 --- a/osu.Game.Rulesets.Catch.Tests/CatchSkinnableTestScene.cs +++ b/osu.Game.Rulesets.Catch.Tests/CatchSkinnableTestScene.cs @@ -1,21 +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 System.Collections.Generic; -using osu.Game.Rulesets.Catch.Skinning; using osu.Game.Tests.Visual; namespace osu.Game.Rulesets.Catch.Tests { public abstract class CatchSkinnableTestScene : SkinnableTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(CatchRuleset), - typeof(CatchLegacySkinTransformer), - }; - protected override Ruleset CreateRulesetForSkinProvider() => new CatchRuleset(); } } diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneBananaShower.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneBananaShower.cs index 024c4cefb0..27a2d5bd0a 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneBananaShower.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneBananaShower.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. -using System; -using System.Collections.Generic; using NUnit.Framework; 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.Tests.Visual; namespace osu.Game.Rulesets.Catch.Tests @@ -15,17 +11,6 @@ namespace osu.Game.Rulesets.Catch.Tests [TestFixture] public class TestSceneBananaShower : PlayerTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(BananaShower), - typeof(Banana), - typeof(DrawableBananaShower), - typeof(DrawableBanana), - - typeof(CatchRuleset), - typeof(DrawableCatchRuleset), - }; - public TestSceneBananaShower() : base(new CatchRuleset()) { diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs index 3a3e664690..6eeda2c731 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs @@ -4,9 +4,6 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Game.Rulesets.Catch.UI; -using System; -using System.Collections.Generic; -using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -15,12 +12,6 @@ namespace osu.Game.Rulesets.Catch.Tests [TestFixture] public class TestSceneCatcher : CatchSkinnableTestScene { - public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] - { - typeof(CatcherArea), - typeof(CatcherSprite) - }).ToList(); - [BackgroundDependencyLoader] private void load() { diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs index df5494aab0..a7094c00be 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.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.Collections.Generic; using System.Linq; using NUnit.Framework; @@ -22,15 +21,6 @@ namespace osu.Game.Rulesets.Catch.Tests { public class TestSceneDrawableHitObjects : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(Catcher), - typeof(DrawableCatchRuleset), - typeof(DrawableFruit), - typeof(DrawableJuiceStream), - typeof(DrawableBanana) - }; - private DrawableCatchRuleset drawableRuleset; private double playfieldTime => drawableRuleset.Playfield.Time.Current; diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjectsHidden.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjectsHidden.cs index 8c3dfef39c..62fe5dca2c 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjectsHidden.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjectsHidden.cs @@ -1,9 +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.Collections.Generic; -using System.Linq; using NUnit.Framework; using osu.Game.Rulesets.Catch.Mods; @@ -11,8 +8,6 @@ namespace osu.Game.Rulesets.Catch.Tests { public class TestSceneDrawableHitObjectsHidden : TestSceneDrawableHitObjects { - public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(CatchModHidden) }).ToList(); - [SetUp] public void SetUp() => Schedule(() => { diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneFruitObjects.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneFruitObjects.cs index cd674bb754..c07e4fdad3 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneFruitObjects.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneFruitObjects.cs @@ -2,13 +2,10 @@ // 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.Graphics; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects.Drawables; -using osu.Game.Rulesets.Catch.Objects.Drawables.Pieces; using osuTK; namespace osu.Game.Rulesets.Catch.Tests @@ -16,22 +13,6 @@ namespace osu.Game.Rulesets.Catch.Tests [TestFixture] public class TestSceneFruitObjects : CatchSkinnableTestScene { - public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] - { - typeof(CatchHitObject), - typeof(Fruit), - typeof(FruitPiece), - typeof(Droplet), - typeof(Banana), - typeof(BananaShower), - typeof(DrawableCatchHitObject), - typeof(DrawableFruit), - typeof(DrawableDroplet), - typeof(DrawableBanana), - typeof(DrawableBananaShower), - typeof(Pulp), - }).ToList(); - protected override void LoadComplete() { base.LoadComplete(); diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs index 49ff9df4d7..0a142a52f8 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.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 NUnit.Framework; using osu.Framework.Testing; @@ -18,11 +17,6 @@ namespace osu.Game.Rulesets.Catch.Tests [TestFixture] public class TestSceneHyperDash : PlayerTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(CatcherArea), - }; - public TestSceneHyperDash() : base(new CatchRuleset()) { diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/ManiaSkinnableTestScene.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/ManiaSkinnableTestScene.cs index a3c1d518c5..1d84a2dfcb 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/ManiaSkinnableTestScene.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/ManiaSkinnableTestScene.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 System; -using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; -using osu.Game.Rulesets.Mania.Skinning; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Rulesets.UI.Scrolling.Algorithms; using osu.Game.Tests.Visual; @@ -27,13 +24,6 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning [Cached(Type = typeof(IScrollingInfo))] private readonly TestScrollingInfo scrollingInfo = new TestScrollingInfo(); - public override IReadOnlyList RequiredTypes => new[] - { - typeof(ManiaRuleset), - typeof(ManiaLegacySkinTransformer), - typeof(ManiaSettingsSubsection) - }; - protected override Ruleset CreateRulesetForSkinProvider() => new ManiaRuleset(); protected ManiaSkinnableTestScene() diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneDrawableJudgement.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneDrawableJudgement.cs index 6ab8a68176..497b80950a 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneDrawableJudgement.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneDrawableJudgement.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.Framework.Extensions; using osu.Framework.Graphics; @@ -15,12 +14,6 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning { public class TestSceneDrawableJudgement : ManiaSkinnableTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(DrawableJudgement), - typeof(DrawableManiaJudgement) - }; - public TestSceneDrawableJudgement() { foreach (HitResult result in Enum.GetValues(typeof(HitResult)).OfType().Skip(1)) diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneHitExplosion.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneHitExplosion.cs index 5f046574ba..a692c0b697 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneHitExplosion.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneHitExplosion.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 System; -using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; using osu.Game.Rulesets.Mania.UI; using osu.Game.Skinning; @@ -21,12 +18,6 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning [TestFixture] public class TestSceneHitExplosion : ManiaSkinnableTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(DrawableNote), - typeof(DrawableManiaHitObject), - }; - public TestSceneHitExplosion() { int runcount = 0; diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneKeyArea.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneKeyArea.cs index c8f901285a..7e80419944 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneKeyArea.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneKeyArea.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. -using System; -using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Rulesets.Mania.Skinning; using osu.Game.Rulesets.Mania.UI.Components; using osu.Game.Skinning; using osuTK; @@ -15,12 +12,6 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning { public class TestSceneKeyArea : ManiaSkinnableTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(DefaultKeyArea), - typeof(LegacyKeyArea) - }; - [BackgroundDependencyLoader] private void load() { diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStageBackground.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStageBackground.cs index a8fc68188a..87c84cf89c 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStageBackground.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStageBackground.cs @@ -1,12 +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 System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Game.Rulesets.Mania.Skinning; using osu.Game.Rulesets.Mania.UI.Components; using osu.Game.Skinning; @@ -14,12 +10,6 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning { public class TestSceneStageBackground : ManiaSkinnableTestScene { - public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] - { - typeof(DefaultStageBackground), - typeof(LegacyStageBackground), - }).ToList(); - [BackgroundDependencyLoader] private void load() { diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStageForeground.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStageForeground.cs index d436445b59..4e99068ed5 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStageForeground.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStageForeground.cs @@ -1,23 +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; -using System.Collections.Generic; -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Game.Rulesets.Mania.Skinning; using osu.Game.Skinning; namespace osu.Game.Rulesets.Mania.Tests.Skinning { public class TestSceneStageForeground : ManiaSkinnableTestScene { - public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] - { - typeof(LegacyStageForeground), - }).ToList(); - [BackgroundDependencyLoader] private void load() { diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneColumn.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneColumn.cs index 5e06002f41..d9b1ad22fa 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneColumn.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneColumn.cs @@ -12,7 +12,6 @@ using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.UI; -using osu.Game.Rulesets.Mania.UI.Components; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Tests.Visual; @@ -24,15 +23,6 @@ namespace osu.Game.Rulesets.Mania.Tests [TestFixture] public class TestSceneColumn : ManiaInputTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(Column), - typeof(ColumnBackground), - typeof(ColumnHitObjectArea), - typeof(DefaultKeyArea), - typeof(DefaultHitTarget) - }; - [Cached(typeof(IReadOnlyList))] private IReadOnlyList mods { get; set; } = Array.Empty(); diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs index 48159c817d..6274bb1005 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.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. -using System; -using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; @@ -29,11 +27,6 @@ namespace osu.Game.Rulesets.Mania.Tests { public class TestSceneManiaHitObjectComposer : EditorClockTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(ManiaBlueprintContainer) - }; - private TestComposer composer; [SetUp] diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneNotes.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneNotes.cs index 8dae5e6d84..ea6a1e2e6a 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneNotes.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneNotes.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. -using System; -using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; @@ -30,12 +28,6 @@ namespace osu.Game.Rulesets.Mania.Tests [TestFixture] public class TestSceneNotes : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(DrawableNote), - typeof(DrawableHoldNote) - }; - [BackgroundDependencyLoader] private void load() { diff --git a/osu.Game.Rulesets.Osu.Tests/OsuSkinnableTestScene.cs b/osu.Game.Rulesets.Osu.Tests/OsuSkinnableTestScene.cs index 90ebbd9f04..a0a38fc47b 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuSkinnableTestScene.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuSkinnableTestScene.cs @@ -1,21 +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 System.Collections.Generic; -using osu.Game.Rulesets.Osu.Skinning; using osu.Game.Tests.Visual; namespace osu.Game.Rulesets.Osu.Tests { public abstract class OsuSkinnableTestScene : SkinnableTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(OsuRuleset), - typeof(OsuLegacySkinTransformer), - }; - protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); } } diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs index f867630df6..c81edf4e07 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.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.Framework.Extensions; using osu.Framework.Graphics; @@ -15,12 +14,6 @@ namespace osu.Game.Rulesets.Osu.Tests { public class TestSceneDrawableJudgement : OsuSkinnableTestScene { - public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] - { - typeof(DrawableJudgement), - typeof(DrawableOsuJudgement) - }).ToList(); - public TestSceneDrawableJudgement() { foreach (HitResult result in Enum.GetValues(typeof(HitResult)).OfType().Skip(1)) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs index 22dacc6f5e..38c2bb9b95 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs @@ -2,16 +2,12 @@ // 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.Allocation; using osu.Framework.Graphics; using osu.Framework.Testing.Input; using osu.Game.Configuration; -using osu.Game.Rulesets.Osu.Skinning; using osu.Game.Rulesets.Osu.UI.Cursor; -using osu.Game.Rulesets.UI; using osu.Game.Screens.Play; using osuTK; @@ -20,16 +16,6 @@ namespace osu.Game.Rulesets.Osu.Tests [TestFixture] public class TestSceneGameplayCursor : OsuSkinnableTestScene { - public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] - { - typeof(GameplayCursorContainer), - typeof(OsuCursorContainer), - typeof(OsuCursor), - typeof(LegacyCursor), - typeof(LegacyCursorTrail), - typeof(CursorTrail) - }).ToList(); - [Cached] private GameplayBeatmap gameplayBeatmap; diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs index e117729f01..37df0d6e37 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs @@ -8,8 +8,6 @@ using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; using osuTK; -using System.Collections.Generic; -using System; using osu.Game.Rulesets.Mods; using System.Linq; using NUnit.Framework; @@ -20,11 +18,6 @@ namespace osu.Game.Rulesets.Osu.Tests [TestFixture] public class TestSceneHitCircle : OsuSkinnableTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(DrawableHitCircle) - }; - private int depthIndex; public TestSceneHitCircle() diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleHidden.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleHidden.cs index 21ebce8c23..45125204b6 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleHidden.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleHidden.cs @@ -1,9 +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.Collections.Generic; -using System.Linq; using NUnit.Framework; using osu.Game.Rulesets.Osu.Mods; @@ -12,8 +9,6 @@ namespace osu.Game.Rulesets.Osu.Tests [TestFixture] public class TestSceneHitCircleHidden : TestSceneHitCircle { - public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList(); - [SetUp] public void SetUp() => Schedule(() => { diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuDistanceSnapGrid.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuDistanceSnapGrid.cs index 0ae49790cd..c182aa5d63 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuDistanceSnapGrid.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuDistanceSnapGrid.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -16,7 +15,6 @@ using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Edit; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Screens.Edit; -using osu.Game.Screens.Edit.Compose.Components; using osu.Game.Tests.Visual; using osuTK; using osuTK.Graphics; @@ -28,11 +26,6 @@ namespace osu.Game.Rulesets.Osu.Tests private const double beat_length = 100; private static readonly Vector2 grid_position = new Vector2(512, 384); - public override IReadOnlyList RequiredTypes => new[] - { - typeof(CircularDistanceSnapGrid) - }; - [Cached(typeof(EditorBeatmap))] private readonly EditorBeatmap editorBeatmap; diff --git a/osu.Game.Rulesets.Osu.Tests/TestScenePathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu.Tests/TestScenePathControlPointVisualiser.cs index cbe14ff4d2..21fa283b6d 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestScenePathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestScenePathControlPointVisualiser.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; -using System.Collections.Generic; using System.Linq; -using Humanizer; using NUnit.Framework; using osu.Framework.Graphics; using osu.Game.Beatmaps; @@ -19,13 +16,6 @@ namespace osu.Game.Rulesets.Osu.Tests { public class TestScenePathControlPointVisualiser : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(StringHumanizeExtensions), - typeof(PathControlPointPiece), - typeof(PathControlPointConnectionPiece) - }; - private Slider slider; private PathControlPointVisualiser visualiser; diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneResumeOverlay.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneResumeOverlay.cs index f4809b0c9b..a7967c407a 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneResumeOverlay.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneResumeOverlay.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. -using System; -using System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; @@ -14,11 +12,6 @@ namespace osu.Game.Rulesets.Osu.Tests { public class TestSceneResumeOverlay : OsuManualInputManagerTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(OsuResumeOverlay), - }; - public TestSceneResumeOverlay() { ManualOsuInputManager osuInputManager; diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs index eb6130c8a6..a9404f665a 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.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.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -21,29 +20,12 @@ 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.Objects.Drawables.Pieces; namespace osu.Game.Rulesets.Osu.Tests { [TestFixture] public class TestSceneSlider : OsuSkinnableTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(Slider), - typeof(SliderTick), - typeof(SliderTailCircle), - typeof(SliderBall), - typeof(SliderBody), - typeof(SnakingSliderBody), - typeof(DrawableSlider), - typeof(DrawableSliderTick), - typeof(DrawableSliderTail), - typeof(DrawableSliderHead), - typeof(DrawableSliderRepeat), - typeof(DrawableOsuHitObject) - }; - private Container content; protected override Container Content diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderHidden.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderHidden.cs index d0ee1bddb5..b2bd727c6a 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderHidden.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderHidden.cs @@ -1,9 +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.Collections.Generic; -using System.Linq; using NUnit.Framework; using osu.Game.Rulesets.Osu.Mods; @@ -12,8 +9,6 @@ namespace osu.Game.Rulesets.Osu.Tests [TestFixture] public class TestSceneSliderHidden : TestSceneSlider { - public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList(); - [SetUp] public void SetUp() => Schedule(() => { diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs index b0c2e56c3e..b543b6fa94 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.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.Collections.Generic; using System.Linq; using NUnit.Framework; @@ -13,8 +12,6 @@ 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.Objects.Drawables; -using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using osu.Game.Rulesets.Osu.Replays; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Scoring; @@ -27,17 +24,6 @@ namespace osu.Game.Rulesets.Osu.Tests { public class TestSceneSliderInput : RateAdjustedBeatmapTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(SliderBall), - typeof(DrawableSlider), - typeof(DrawableSliderTick), - typeof(DrawableSliderRepeat), - typeof(DrawableOsuHitObject), - typeof(DrawableSliderHead), - typeof(DrawableSliderTail), - }; - private const double time_before_slider = 250; private const double time_slider_start = 1500; private const double time_during_slide_1 = 2500; diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSelectionBlueprint.cs index 5dd2bd18a8..d5be538d94 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSelectionBlueprint.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. -using System; -using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Utils; using osu.Game.Beatmaps; @@ -22,16 +20,6 @@ namespace osu.Game.Rulesets.Osu.Tests { public class TestSceneSliderSelectionBlueprint : SelectionBlueprintTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(SliderSelectionBlueprint), - typeof(SliderCircleSelectionBlueprint), - typeof(SliderBodyPiece), - typeof(SliderCircle), - typeof(PathControlPointVisualiser), - typeof(PathControlPointPiece) - }; - private Slider slider; private DrawableSlider drawableObject; private TestSliderBlueprint blueprint; diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs index f53b64c729..65bed071cd 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.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. -using System; -using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; @@ -12,7 +10,6 @@ using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; -using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using osu.Game.Tests.Visual; namespace osu.Game.Rulesets.Osu.Tests @@ -20,13 +17,6 @@ namespace osu.Game.Rulesets.Osu.Tests [TestFixture] public class TestSceneSpinner : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(SpinnerDisc), - typeof(DrawableSpinner), - typeof(DrawableOsuHitObject) - }; - private readonly Container content; protected override Container Content => content; diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerHidden.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerHidden.cs index dd863deed2..91b6a05fe3 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerHidden.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerHidden.cs @@ -1,9 +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.Collections.Generic; -using System.Linq; using NUnit.Framework; using osu.Game.Rulesets.Osu.Mods; @@ -12,8 +9,6 @@ namespace osu.Game.Rulesets.Osu.Tests [TestFixture] public class TestSceneSpinnerHidden : TestSceneSpinner { - public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList(); - [SetUp] public void SetUp() => Schedule(() => { diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerSelectionBlueprint.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerSelectionBlueprint.cs index d777ca3610..011463ab14 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerSelectionBlueprint.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.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners; -using osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners.Components; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Tests.Visual; @@ -18,12 +15,6 @@ namespace osu.Game.Rulesets.Osu.Tests { public class TestSceneSpinnerSelectionBlueprint : SelectionBlueprintTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(SpinnerSelectionBlueprint), - typeof(SpinnerPiece) - }; - public TestSceneSpinnerSelectionBlueprint() { var spinner = new Spinner diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerSpunOut.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerSpunOut.cs index e406f9ddff..d1210db6b1 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerSpunOut.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerSpunOut.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. -using System; -using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; @@ -12,7 +10,6 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; -using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using osu.Game.Tests.Visual; namespace osu.Game.Rulesets.Osu.Tests @@ -20,14 +17,6 @@ namespace osu.Game.Rulesets.Osu.Tests [TestFixture] public class TestSceneSpinnerSpunOut : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(SpinnerDisc), - typeof(DrawableSpinner), - typeof(DrawableOsuHitObject), - typeof(OsuModSpunOut) - }; - [SetUp] public void SetUp() => Schedule(() => { diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TaikoSkinnableTestScene.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TaikoSkinnableTestScene.cs index 161154b1a7..69250a14e1 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TaikoSkinnableTestScene.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TaikoSkinnableTestScene.cs @@ -1,21 +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 System.Collections.Generic; -using osu.Game.Rulesets.Taiko.Skinning; using osu.Game.Tests.Visual; namespace osu.Game.Rulesets.Taiko.Tests.Skinning { public abstract class TaikoSkinnableTestScene : SkinnableTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(TaikoRuleset), - typeof(TaikoLegacySkinTransformer), - }; - protected override Ruleset CreateRulesetForSkinProvider() => new TaikoRuleset(); } } diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableBarLine.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableBarLine.cs index 70493aa69a..f6aec20d53 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableBarLine.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableBarLine.cs @@ -1,9 +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.Collections.Generic; -using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -12,7 +9,6 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Objects.Drawables; -using osu.Game.Rulesets.Taiko.Skinning; using osu.Game.Rulesets.Taiko.UI; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Tests.Visual; @@ -22,13 +18,6 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning [TestFixture] public class TestSceneDrawableBarLine : TaikoSkinnableTestScene { - public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] - { - typeof(DrawableBarLine), - typeof(LegacyBarLine), - typeof(BarLine), - }).ToList(); - [Cached(typeof(IScrollingInfo))] private ScrollingTestContainer.TestScrollingInfo info = new ScrollingTestContainer.TestScrollingInfo { diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableDrumRoll.cs index 554894bf68..44646e5fc9 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableDrumRoll.cs @@ -1,9 +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.Collections.Generic; -using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -11,7 +8,6 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Objects.Drawables; -using osu.Game.Rulesets.Taiko.Skinning; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Tests.Visual; @@ -20,13 +16,6 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning [TestFixture] public class TestSceneDrawableDrumRoll : TaikoSkinnableTestScene { - public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] - { - typeof(DrawableDrumRoll), - typeof(DrawableDrumRollTick), - typeof(LegacyDrumRoll), - }).ToList(); - [Cached(typeof(IScrollingInfo))] private ScrollingTestContainer.TestScrollingInfo info = new ScrollingTestContainer.TestScrollingInfo { diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableHit.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableHit.cs index 6a3c98a514..9930d97d31 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableHit.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableHit.cs @@ -1,9 +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.Collections.Generic; -using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -11,20 +8,12 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Objects.Drawables; -using osu.Game.Rulesets.Taiko.Skinning; namespace osu.Game.Rulesets.Taiko.Tests.Skinning { [TestFixture] public class TestSceneDrawableHit : TaikoSkinnableTestScene { - public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] - { - typeof(DrawableHit), - typeof(LegacyHit), - typeof(LegacyCirclePiece), - }).ToList(); - [BackgroundDependencyLoader] private void load() { diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs index bd3b360577..d200c44a02 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.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.Collections.Generic; using System.Linq; using Humanizer; @@ -27,12 +26,6 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning [TestFixture] public class TestSceneDrawableTaikoMascot : TaikoSkinnableTestScene { - public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] - { - typeof(DrawableTaikoMascot), - typeof(TaikoMascotAnimation) - }).ToList(); - [Cached(typeof(IScrollingInfo))] private ScrollingTestContainer.TestScrollingInfo info = new ScrollingTestContainer.TestScrollingInfo { diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneHitExplosion.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneHitExplosion.cs index 791c438c94..2b5efec7f9 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneHitExplosion.cs @@ -1,9 +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.Collections.Generic; -using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -11,7 +8,6 @@ using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Objects.Drawables; -using osu.Game.Rulesets.Taiko.Skinning; using osu.Game.Rulesets.Taiko.UI; namespace osu.Game.Rulesets.Taiko.Tests.Skinning @@ -19,13 +15,6 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning [TestFixture] public class TestSceneHitExplosion : TaikoSkinnableTestScene { - public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] - { - typeof(HitExplosion), - typeof(LegacyHitExplosion), - typeof(DefaultHitExplosion), - }).ToList(); - [BackgroundDependencyLoader] private void load() { diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneInputDrum.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneInputDrum.cs index 412027ca61..fa6c9da174 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneInputDrum.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneInputDrum.cs @@ -1,15 +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.Collections.Generic; -using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Rulesets.Taiko.Skinning; using osu.Game.Rulesets.Taiko.UI; using osuTK; @@ -18,12 +14,6 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning [TestFixture] public class TestSceneInputDrum : TaikoSkinnableTestScene { - public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] - { - typeof(InputDrum), - typeof(LegacyInputDrum), - }).ToList(); - [BackgroundDependencyLoader] private void load() { diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs index e02ad53ed8..7b7e2c43d1 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs @@ -2,15 +2,12 @@ // 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; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Testing; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Taiko.Beatmaps; -using osu.Game.Rulesets.Taiko.Skinning; using osu.Game.Rulesets.Taiko.UI; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Tests.Visual; @@ -19,14 +16,6 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning { public class TestSceneTaikoPlayfield : TaikoSkinnableTestScene { - public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] - { - typeof(TaikoHitTarget), - typeof(TaikoLegacyHitTarget), - typeof(PlayfieldBackgroundRight), - typeof(LegacyTaikoScroller), - }).ToList(); - [Cached(typeof(IScrollingInfo))] private ScrollingTestContainer.TestScrollingInfo info = new ScrollingTestContainer.TestScrollingInfo { diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs index f97aa48f11..76d0c7a50f 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.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. -using System; -using System.Collections.Generic; using System.Linq; using System.Threading; using NUnit.Framework; @@ -39,15 +37,6 @@ namespace osu.Game.Tests.Visual.Background [TestFixture] public class TestSceneUserDimBackgrounds : OsuManualInputManagerTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(ScreenWithBeatmapBackground), - typeof(PlayerLoader), - typeof(Player), - typeof(UserDimContainer), - typeof(OsuScreen) - }; - private DummySongSelect songSelect; private TestPlayerLoader playerLoader; private LoadBlockingTestPlayer player; diff --git a/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs b/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs index f6e69fd8bf..6cf5e6a987 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.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 NUnit.Framework; using osu.Framework.Graphics; @@ -18,7 +17,6 @@ namespace osu.Game.Tests.Visual.Editing { public class TestSceneBeatDivisorControl : OsuManualInputManagerTestScene { - public override IReadOnlyList RequiredTypes => new[] { typeof(BindableBeatDivisor) }; private BeatDivisorControl beatDivisorControl; private BindableBeatDivisor bindableBeatDivisor; diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorComposeRadioButtons.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorComposeRadioButtons.cs index 2deeaef1f6..e4d7e025a8 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorComposeRadioButtons.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorComposeRadioButtons.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. -using System; -using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Graphics; using osu.Game.Screens.Edit.Components.RadioButtons; @@ -12,8 +10,6 @@ namespace osu.Game.Tests.Visual.Editing [TestFixture] public class TestSceneEditorComposeRadioButtons : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] { typeof(DrawableRadioButton) }; - public TestSceneEditorComposeRadioButtons() { RadioButtonCollection collection; diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorMenuBar.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorMenuBar.cs index 2cbdacb61c..3cb44d9ae8 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorMenuBar.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorMenuBar.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. -using System; -using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -15,8 +13,6 @@ namespace osu.Game.Tests.Visual.Editing [TestFixture] public class TestSceneEditorMenuBar : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] { typeof(EditorMenuBar), typeof(ScreenSelectionTabControl) }; - public TestSceneEditorMenuBar() { Add(new Container diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorSummaryTimeline.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorSummaryTimeline.cs index c92423545d..3adc1bd425 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorSummaryTimeline.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorSummaryTimeline.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. -using System; -using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -15,8 +13,6 @@ namespace osu.Game.Tests.Visual.Editing [TestFixture] public class TestSceneEditorSummaryTimeline : EditorClockTestScene { - public override IReadOnlyList RequiredTypes => new[] { typeof(SummaryTimeline) }; - [BackgroundDependencyLoader] private void load() { diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs index ddaca26220..7ca24346aa 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs @@ -1,9 +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 JetBrains.Annotations; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Timing; @@ -13,11 +11,8 @@ using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Edit; -using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles; -using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Screens.Edit; -using osu.Game.Screens.Edit.Compose.Components; using osuTK; namespace osu.Game.Tests.Visual.Editing @@ -25,19 +20,6 @@ namespace osu.Game.Tests.Visual.Editing [TestFixture] public class TestSceneHitObjectComposer : EditorClockTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(SelectionHandler), - typeof(DragBox), - typeof(HitObjectComposer), - typeof(OsuHitObjectComposer), - typeof(BlueprintContainer), - typeof(NotNullAttribute), - typeof(HitCirclePiece), - typeof(HitCircleSelectionBlueprint), - typeof(HitCirclePlacementBlueprint), - }; - [BackgroundDependencyLoader] private void load() { diff --git a/osu.Game.Tests/Visual/Editing/TestSceneTimelineBlueprintContainer.cs b/osu.Game.Tests/Visual/Editing/TestSceneTimelineBlueprintContainer.cs index 5ab2f49b4a..e931be044c 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneTimelineBlueprintContainer.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneTimelineBlueprintContainer.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. -using System; -using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Graphics; using osu.Game.Screens.Edit.Compose.Components.Timeline; @@ -12,11 +10,6 @@ namespace osu.Game.Tests.Visual.Editing [TestFixture] public class TestSceneTimelineBlueprintContainer : TimelineTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(TimelineHitObjectBlueprint), - }; - public override Drawable CreateTestComponent() => new TimelineBlueprintContainer(); protected override void LoadComplete() diff --git a/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs b/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs index a6dbe9571e..2a7f9389d1 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.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. -using System; -using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Allocation; using osu.Game.Rulesets.Osu.Beatmaps; @@ -14,18 +12,6 @@ namespace osu.Game.Tests.Visual.Editing [TestFixture] public class TestSceneTimingScreen : EditorClockTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(ControlPointTable), - typeof(ControlPointSettings), - typeof(Section<>), - typeof(TimingSection), - typeof(EffectSection), - typeof(SampleSection), - typeof(DifficultySection), - typeof(RowAttribute) - }; - [Cached(typeof(EditorBeatmap))] private readonly EditorBeatmap editorBeatmap; diff --git a/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs b/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs index 56b2860e96..01ef7e6170 100644 --- a/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs +++ b/osu.Game.Tests/Visual/Editing/TimelineTestScene.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. -using System; -using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Bindables; @@ -22,14 +20,6 @@ namespace osu.Game.Tests.Visual.Editing { public abstract class TimelineTestScene : EditorClockTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(TimelineArea), - typeof(Timeline), - typeof(TimelineButton), - typeof(CentreMarker) - }; - protected TimelineArea TimelineArea { get; private set; } [BackgroundDependencyLoader] diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBreakTracker.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBreakTracker.cs index a6f996c30d..be17721b88 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBreakTracker.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBreakTracker.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.Collections.Generic; using System.Linq; using NUnit.Framework; @@ -15,11 +14,6 @@ namespace osu.Game.Tests.Visual.Gameplay [TestFixture] public class TestSceneBreakTracker : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(BreakOverlay), - }; - private readonly BreakOverlay breakOverlay; private readonly TestBreakTracker breakTracker; diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneFailAnimation.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneFailAnimation.cs index de257c9e53..85aaf20a19 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneFailAnimation.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneFailAnimation.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; @@ -18,13 +17,6 @@ namespace osu.Game.Tests.Visual.Gameplay return new FailPlayer(); } - public override IReadOnlyList RequiredTypes => new[] - { - typeof(TestSceneAllRulesetPlayers), - typeof(TestPlayer), - typeof(Player), - }; - protected override void AddCheckSteps() { AddUntilStep("wait for fail", () => Player.HasFailed); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs index ea3e0c2293..e8b8c7c8e9 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.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 NUnit.Framework; using osu.Framework.Allocation; @@ -20,8 +19,6 @@ namespace osu.Game.Tests.Visual.Gameplay [Description("player pause/fail screens")] public class TestSceneGameplayMenuOverlay : OsuManualInputManagerTestScene { - public override IReadOnlyList RequiredTypes => new[] { typeof(FailOverlay), typeof(PauseOverlay) }; - private FailOverlay failOverlay; private PauseOverlay pauseOverlay; diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs index 1527cba6fc..253b8d9c55 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs @@ -2,8 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using NUnit.Framework; -using System; -using System.Collections.Generic; using osu.Game.Rulesets.Judgements; using osu.Framework.Utils; using osu.Framework.Graphics; @@ -22,13 +20,6 @@ namespace osu.Game.Tests.Visual.Gameplay { public class TestSceneHitErrorMeter : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(HitErrorMeter), - typeof(BarHitErrorMeter), - typeof(ColourHitErrorMeter) - }; - private BarHitErrorMeter barMeter; private BarHitErrorMeter barMeter2; private ColourHitErrorMeter colourMeter; diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs index 593dcd245c..d7a3f80256 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.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. -using System; -using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; @@ -15,13 +13,6 @@ namespace osu.Game.Tests.Visual.Gameplay [TestFixture] public class TestSceneKeyCounter : OsuManualInputManagerTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(KeyCounterKeyboard), - typeof(KeyCounterMouse), - typeof(KeyCounterDisplay) - }; - public TestSceneKeyCounter() { KeyCounterKeyboard testCounter; diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneMedalOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneMedalOverlay.cs index 41722b430e..0ada3cf05f 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneMedalOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneMedalOverlay.cs @@ -1,11 +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.Game.Overlays; -using osu.Game.Overlays.MedalSplash; using osu.Game.Users; namespace osu.Game.Tests.Visual.Gameplay @@ -13,12 +10,6 @@ namespace osu.Game.Tests.Visual.Gameplay [TestFixture] public class TestSceneMedalOverlay : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(MedalOverlay), - typeof(DrawableMedal), - }; - public TestSceneMedalOverlay() { AddStep(@"display", () => diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneNightcoreBeatContainer.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneNightcoreBeatContainer.cs index 3473b03eaf..951ee1489d 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneNightcoreBeatContainer.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneNightcoreBeatContainer.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. -using System; -using System.Collections.Generic; using System.Linq; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Game.Beatmaps.Timing; @@ -15,11 +13,6 @@ namespace osu.Game.Tests.Visual.Gameplay { public class TestSceneNightcoreBeatContainer : TestSceneBeatSyncedContainer { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(ModNightcore<>) - }; - protected override void LoadComplete() { base.LoadComplete(); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs index c9561a70fa..a35437a286 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs @@ -7,8 +7,6 @@ using osu.Game.Online; using osu.Game.Online.API.Requests.Responses; using osu.Game.Scoring; using osu.Game.Users; -using System; -using System.Collections.Generic; using osu.Framework.Allocation; using osu.Game.Rulesets; using osu.Game.Screens.Ranking; @@ -21,11 +19,6 @@ namespace osu.Game.Tests.Visual.Gameplay [Resolved] private RulesetStore rulesets { get; set; } - public override IReadOnlyList RequiredTypes => new[] - { - typeof(ReplayDownloadButton) - }; - private TestReplayDownloadButton downloadButton; public TestSceneReplayDownloadButton() diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneScrollingHitObjects.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneScrollingHitObjects.cs index d03716db2e..0d15e495e3 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneScrollingHitObjects.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneScrollingHitObjects.cs @@ -17,7 +17,6 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Timing; -using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; using osuTK; using osuTK.Graphics; @@ -27,8 +26,6 @@ namespace osu.Game.Tests.Visual.Gameplay [TestFixture] public class TestSceneScrollingHitObjects : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] { typeof(Playfield) }; - [Cached(typeof(IReadOnlyList))] private IReadOnlyList mods { get; set; } = Array.Empty(); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs index b9b13d7bd8..733e8f4290 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.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.Collections.Generic; using NUnit.Framework; using osu.Framework.Allocation; @@ -20,11 +19,6 @@ namespace osu.Game.Tests.Visual.Gameplay [TestFixture] public class TestSceneSongProgress : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(SongProgressBar), - }; - private SongProgress progress; private TestSongProgressGraph graph; private readonly Container progressContainer; diff --git a/osu.Game.Tests/Visual/Menus/IntroTestScene.cs b/osu.Game.Tests/Visual/Menus/IntroTestScene.cs index 33811f9529..2d2f1a1618 100644 --- a/osu.Game.Tests/Visual/Menus/IntroTestScene.cs +++ b/osu.Game.Tests/Visual/Menus/IntroTestScene.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. -using System; -using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -18,13 +16,6 @@ namespace osu.Game.Tests.Visual.Menus [TestFixture] public abstract class IntroTestScene : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(StartupScreen), - typeof(IntroScreen), - typeof(IntroTestScene), - }; - [Cached] private OsuLogo logo; diff --git a/osu.Game.Tests/Visual/Menus/TestSceneToolbar.cs b/osu.Game.Tests/Visual/Menus/TestSceneToolbar.cs index 8fbbc8ebd8..b4985cad9f 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneToolbar.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneToolbar.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. -using System; -using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; @@ -17,14 +15,6 @@ namespace osu.Game.Tests.Visual.Menus [TestFixture] public class TestSceneToolbar : OsuManualInputManagerTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(ToolbarButton), - typeof(ToolbarRulesetSelector), - typeof(ToolbarRulesetTabButton), - typeof(ToolbarNotificationButton), - }; - private Toolbar toolbar; [Resolved] diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs index 713ba13439..5ef4dd6773 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.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.Collections.Generic; using System.Linq; using NUnit.Framework; @@ -22,12 +21,6 @@ namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneDrawableRoomPlaylist : OsuManualInputManagerTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(DrawableRoomPlaylist), - typeof(DrawableRoomPlaylistItem) - }; - private TestPlaylist playlist; [Test] diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomInfo.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomInfo.cs index 1e1bc9725c..8b74eb5f27 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomInfo.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomInfo.cs @@ -2,7 +2,6 @@ // 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.Online.Multiplayer; @@ -14,11 +13,6 @@ namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneLoungeRoomInfo : MultiplayerTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(RoomInfo) - }; - [SetUp] public void Setup() => Schedule(() => { diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs index b5d946d049..77b41c89b0 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.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 NUnit.Framework; using osu.Framework.Allocation; @@ -23,12 +22,6 @@ namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneLoungeRoomsContainer : MultiplayerTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(RoomsContainer), - typeof(DrawableRoom) - }; - [Cached(Type = typeof(IRoomManager))] private TestRoomManager roomManager = new TestRoomManager(); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchHeader.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchHeader.cs index cf40995fc0..38eb3181bf 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchHeader.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchHeader.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. -using System; -using System.Collections.Generic; using osu.Game.Beatmaps; using osu.Game.Online.Multiplayer; using osu.Game.Rulesets.Osu; @@ -14,11 +12,6 @@ namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneMatchHeader : MultiplayerTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(Header) - }; - public TestSceneMatchHeader() { Room.Playlist.Add(new PlaylistItem diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboardChatDisplay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboardChatDisplay.cs index e46386b263..72bbc11cd0 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboardChatDisplay.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboardChatDisplay.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. -using System; -using System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Screens.Multi.Match.Components; @@ -12,11 +10,6 @@ namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneMatchLeaderboardChatDisplay : MultiplayerTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(LeaderboardChatDisplay) - }; - protected override bool UseOnlineAPI => true; public TestSceneMatchLeaderboardChatDisplay() diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs index 047e9d860d..d2e8c22c39 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -18,11 +17,6 @@ namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneMatchSettingsOverlay : MultiplayerTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(MatchSettingsOverlay) - }; - [Cached(Type = typeof(IRoomManager))] private TestRoomManager roomManager = new TestRoomManager(); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSongSelect.cs index 2c6f34d8a6..5cff2d7d05 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSongSelect.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSongSelect.cs @@ -23,12 +23,6 @@ namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneMatchSongSelect : MultiplayerTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(MatchSongSelect), - typeof(MatchBeatmapDetailArea), - }; - [Resolved] private BeatmapManager beatmapManager { get; set; } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSubScreen.cs index 7f79e306ad..d678d5a814 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSubScreen.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 NUnit.Framework; using osu.Framework.Allocation; @@ -20,7 +19,6 @@ using osu.Game.Screens.Multi.Match.Components; using osu.Game.Tests.Beatmaps; using osu.Game.Users; using osuTK.Input; -using Header = osu.Game.Screens.Multi.Match.Components.Header; namespace osu.Game.Tests.Visual.Multiplayer { @@ -28,14 +26,6 @@ namespace osu.Game.Tests.Visual.Multiplayer { protected override bool UseOnlineAPI => true; - public override IReadOnlyList RequiredTypes => new[] - { - typeof(Screens.Multi.Multiplayer), - typeof(MatchSubScreen), - typeof(Header), - typeof(Footer) - }; - [Cached(typeof(IRoomManager))] private readonly TestRoomManager roomManager = new TestRoomManager(); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiScreen.cs index dfe61a4dda..61859c9da3 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiScreen.cs @@ -1,11 +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 NUnit.Framework; -using osu.Game.Screens.Multi.Lounge; -using osu.Game.Screens.Multi.Lounge.Components; namespace osu.Game.Tests.Visual.Multiplayer { @@ -14,13 +10,6 @@ namespace osu.Game.Tests.Visual.Multiplayer { protected override bool UseOnlineAPI => true; - public override IReadOnlyList RequiredTypes => new[] - { - typeof(Screens.Multi.Multiplayer), - typeof(LoungeSubScreen), - typeof(FilterControl) - }; - public TestSceneMultiScreen() { Screens.Multi.Multiplayer multi = new Screens.Multi.Multiplayer(); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneOverlinedParticipants.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneOverlinedParticipants.cs index 1fc258a225..7ea3bba23f 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneOverlinedParticipants.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneOverlinedParticipants.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. -using System; -using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Graphics; using osu.Game.Screens.Multi.Components; @@ -12,13 +10,6 @@ namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneOverlinedParticipants : MultiplayerTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(OverlinedParticipants), - typeof(OverlinedDisplay), - typeof(ParticipantsList) - }; - protected override bool UseOnlineAPI => true; public TestSceneOverlinedParticipants() diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomStatus.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomStatus.cs index 74d1645f6d..1925e0ef4f 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomStatus.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomStatus.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. -using System; -using System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Online.Multiplayer; @@ -13,13 +11,6 @@ namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneRoomStatus : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(RoomStatusEnded), - typeof(RoomStatusOpen), - typeof(RoomStatusPlaying) - }; - public TestSceneRoomStatus() { Child = new FillFlowContainer diff --git a/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs index a53a818065..6c8ec917ba 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs @@ -1,30 +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 System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Overlays; -using osu.Game.Overlays.AccountCreation; using osu.Game.Users; namespace osu.Game.Tests.Visual.Online { public class TestSceneAccountCreationOverlay : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(ErrorTextFlowContainer), - typeof(AccountCreationBackground), - typeof(ScreenEntry), - typeof(ScreenWarning), - typeof(ScreenWelcome), - typeof(AccountCreationScreen), - }; - private readonly Container userPanelArea; private Bindable localUser; diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs index 64d1a9ddcd..6cb1687d1f 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs @@ -1,22 +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.Collections.Generic; using osu.Game.Overlays; using NUnit.Framework; -using osu.Game.Overlays.BeatmapListing; namespace osu.Game.Tests.Visual.Online { public class TestSceneBeatmapListingOverlay : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(BeatmapListingOverlay), - typeof(BeatmapListingFilterControl) - }; - protected override bool UseOnlineAPI => true; private readonly BeatmapListingOverlay overlay; diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapRulesetSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapRulesetSelector.cs index 8b077c8de3..eb34187cd6 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapRulesetSelector.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapRulesetSelector.cs @@ -8,7 +8,6 @@ using osu.Game.Beatmaps; using osu.Game.Overlays; using osu.Game.Overlays.BeatmapSet; using osu.Game.Rulesets; -using System; using System.Collections.Generic; using System.Linq; @@ -16,12 +15,6 @@ namespace osu.Game.Tests.Visual.Online { public class TestSceneBeatmapRulesetSelector : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(BeatmapRulesetSelector), - typeof(BeatmapRulesetTabItem), - }; - [Cached] private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs index 5ca2c9868f..c5d1fd6887 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs @@ -6,8 +6,6 @@ using osu.Framework.Allocation; using osu.Game.Beatmaps; using osu.Game.Overlays; using osu.Game.Overlays.BeatmapSet; -using osu.Game.Overlays.BeatmapSet.Buttons; -using osu.Game.Overlays.BeatmapSet.Scores; using osu.Game.Rulesets; using osu.Game.Users; using System; @@ -21,30 +19,6 @@ namespace osu.Game.Tests.Visual.Online { private readonly TestBeatmapSetOverlay overlay; - public override IReadOnlyList RequiredTypes => new[] - { - typeof(Header), - typeof(ScoreTable), - typeof(ScoreTableRowBackground), - typeof(DrawableTopScore), - typeof(ScoresContainer), - typeof(AuthorInfo), - typeof(BasicStats), - typeof(BeatmapPicker), - typeof(Details), - typeof(HeaderDownloadButton), - typeof(FavouriteButton), - typeof(Header), - typeof(HeaderButton), - typeof(Info), - typeof(PreviewButton), - typeof(SuccessRate), - typeof(BeatmapAvailability), - typeof(BeatmapRulesetSelector), - typeof(BeatmapRulesetTabItem), - typeof(NotSupporterPlaceholder) - }; - protected override bool UseOnlineAPI => true; public TestSceneBeatmapSetOverlay() diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs index dea1e710b5..f7099b0615 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.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.Collections.Generic; using System.Linq; using NUnit.Framework; @@ -17,11 +16,6 @@ namespace osu.Game.Tests.Visual.Online { public class TestSceneBeatmapSetOverlayDetails : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(Details) - }; - private RatingsExposingDetails details; [Cached] diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs index 03003daf81..4cb22bf1fe 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.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. -using System; -using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; @@ -21,11 +19,6 @@ namespace osu.Game.Tests.Visual.Online { public class TestSceneBeatmapSetOverlaySuccessRate : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(Details) - }; - private GraphExposingSuccessRate successRate; [Cached] diff --git a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs index 22d20f7098..02f6de2269 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.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.Collections.Generic; using NUnit.Framework; using osu.Game.Online.API.Requests.Responses; @@ -15,17 +14,6 @@ namespace osu.Game.Tests.Visual.Online { private TestChangelogOverlay changelog; - public override IReadOnlyList RequiredTypes => new[] - { - typeof(ChangelogUpdateStreamControl), - typeof(ChangelogUpdateStreamItem), - typeof(ChangelogHeader), - typeof(ChangelogContent), - typeof(ChangelogListing), - typeof(ChangelogSingleBuild), - typeof(ChangelogBuild), - }; - protected override bool UseOnlineAPI => true; [SetUp] diff --git a/osu.Game.Tests/Visual/Online/TestSceneChannelTabControl.cs b/osu.Game.Tests/Visual/Online/TestSceneChannelTabControl.cs index 1fb3f4ba45..73e1fc9b35 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChannelTabControl.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChannelTabControl.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.Collections.Generic; using System.Linq; using osu.Framework.Extensions.Color4Extensions; @@ -21,11 +20,6 @@ namespace osu.Game.Tests.Visual.Online { public class TestSceneChannelTabControl : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(ChannelTabControl), - }; - private readonly TestTabControl channelTabControl; public TestSceneChannelTabControl() diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatLineTruncation.cs b/osu.Game.Tests/Visual/Online/TestSceneChatLineTruncation.cs index 4773e84a5e..8408b7dd60 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatLineTruncation.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatLineTruncation.cs @@ -2,12 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Graphics.Containers; using osu.Game.Online.Chat; using osu.Game.Overlays.Chat; using osu.Game.Users; @@ -19,14 +17,6 @@ namespace osu.Game.Tests.Visual.Online { private readonly TestChatLineContainer textContainer; - public override IReadOnlyList RequiredTypes => new[] - { - typeof(ChatLine), - typeof(Message), - typeof(LinkFlowContainer), - typeof(MessageFormatter) - }; - public TestSceneChatLineTruncation() { Add(textContainer = new TestChatLineContainer diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs index 7a257a1603..9e69530a77 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatLink.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 NUnit.Framework; using osu.Framework.Allocation; @@ -11,7 +10,6 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; -using osu.Game.Graphics.Containers; using osu.Game.Online.Chat; using osu.Game.Overlays; using osu.Game.Overlays.Chat; @@ -27,16 +25,6 @@ namespace osu.Game.Tests.Visual.Online private readonly DialogOverlay dialogOverlay; private Color4 linkColour; - public override IReadOnlyList RequiredTypes => new[] - { - typeof(ChatLine), - typeof(Message), - typeof(LinkFlowContainer), - typeof(DummyEchoMessage), - typeof(LocalEchoMessage), - typeof(MessageFormatter) - }; - public TestSceneChatLink() { Add(dialogOverlay = new DialogOverlay { Depth = float.MinValue }); diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index 14924dda21..05b33e4386 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.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.Collections.Generic; using System.Linq; using NUnit.Framework; @@ -14,7 +13,6 @@ using osu.Framework.Testing; using osu.Game.Graphics.UserInterface; using osu.Game.Online.Chat; using osu.Game.Overlays; -using osu.Game.Overlays.Chat; using osu.Game.Overlays.Chat.Selection; using osu.Game.Overlays.Chat.Tabs; using osu.Game.Users; @@ -24,17 +22,6 @@ namespace osu.Game.Tests.Visual.Online { public class TestSceneChatOverlay : OsuManualInputManagerTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(ChatLine), - typeof(DrawableChannel), - typeof(ChannelSelectorTabItem), - typeof(ChannelTabControl), - typeof(ChannelTabItem), - typeof(PrivateChannelTabItem), - typeof(TabCloseButton) - }; - private TestChatOverlay chatOverlay; private ChannelManager channelManager; diff --git a/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs index ece280659c..42e6b9087c 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.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. -using System; -using System.Collections.Generic; using NUnit.Framework; using osu.Game.Online.API.Requests; using osu.Framework.Graphics.Containers; @@ -18,19 +16,6 @@ namespace osu.Game.Tests.Visual.Online [TestFixture] public class TestSceneCommentsContainer : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(CommentsContainer), - typeof(CommentsHeader), - typeof(DrawableComment), - typeof(HeaderButton), - typeof(OverlaySortTabControl<>), - typeof(ShowChildrenButton), - typeof(DeletedCommentsCounter), - typeof(VotePill), - typeof(CommentsPage), - }; - protected override bool UseOnlineAPI => true; [Cached] diff --git a/osu.Game.Tests/Visual/Online/TestSceneCommentsHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneCommentsHeader.cs index c688d600a3..03eac5d85b 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneCommentsHeader.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneCommentsHeader.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. -using System; -using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -14,13 +12,6 @@ namespace osu.Game.Tests.Visual.Online [TestFixture] public class TestSceneCommentsHeader : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(CommentsHeader), - typeof(HeaderButton), - typeof(OverlaySortTabControl<>), - }; - [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); diff --git a/osu.Game.Tests/Visual/Online/TestSceneCommentsPage.cs b/osu.Game.Tests/Visual/Online/TestSceneCommentsPage.cs index a28a0107a1..7fdf0708e0 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneCommentsPage.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneCommentsPage.cs @@ -20,12 +20,6 @@ namespace osu.Game.Tests.Visual.Online { public class TestSceneCommentsPage : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(DrawableComment), - typeof(CommentsPage), - }; - [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); diff --git a/osu.Game.Tests/Visual/Online/TestSceneDashboardOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneDashboardOverlay.cs index df95f24686..960d3fa248 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneDashboardOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneDashboardOverlay.cs @@ -1,24 +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.Collections.Generic; using NUnit.Framework; using osu.Game.Overlays; -using osu.Game.Overlays.Dashboard; -using osu.Game.Overlays.Dashboard.Friends; namespace osu.Game.Tests.Visual.Online { public class TestSceneDashboardOverlay : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(DashboardOverlay), - typeof(DashboardOverlayHeader), - typeof(FriendDisplay) - }; - protected override bool UseOnlineAPI => true; private readonly DashboardOverlay overlay; diff --git a/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs b/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs index 9fe873cb6a..684ce10820 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.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. -using System; -using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; @@ -18,11 +16,6 @@ namespace osu.Game.Tests.Visual.Online { public class TestSceneDirectDownloadButton : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(BeatmapPanelDownloadButton) - }; - private TestDownloadButton downloadButton; [Resolved] diff --git a/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs index d6ed654bac..74ece5da05 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.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.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -18,13 +17,6 @@ namespace osu.Game.Tests.Visual.Online [Cached(typeof(IPreviewTrackOwner))] public class TestSceneDirectPanel : OsuTestScene, IPreviewTrackOwner { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(GridBeatmapPanel), - typeof(ListBeatmapPanel), - typeof(IconPill) - }; - private BeatmapSetInfo getUndownloadableBeatmapSet() => new BeatmapSetInfo { OnlineBeatmapSetID = 123, diff --git a/osu.Game.Tests/Visual/Online/TestSceneExternalLinkButton.cs b/osu.Game.Tests/Visual/Online/TestSceneExternalLinkButton.cs index 637b577021..31bb276cd4 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneExternalLinkButton.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneExternalLinkButton.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. -using System; -using System.Collections.Generic; using osu.Game.Graphics.UserInterface; using osuTK; @@ -10,8 +8,6 @@ namespace osu.Game.Tests.Visual.Online { public class TestSceneExternalLinkButton : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] { typeof(ExternalLinkButton) }; - public TestSceneExternalLinkButton() { Child = new ExternalLinkButton("https://osu.ppy.sh/home") diff --git a/osu.Game.Tests/Visual/Online/TestSceneFriendDisplay.cs b/osu.Game.Tests/Visual/Online/TestSceneFriendDisplay.cs index 0b5ff1c960..72033fc121 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneFriendDisplay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneFriendDisplay.cs @@ -16,13 +16,6 @@ namespace osu.Game.Tests.Visual.Online { public class TestSceneFriendDisplay : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(FriendDisplay), - typeof(FriendOnlineStreamControl), - typeof(UserListToolbar) - }; - protected override bool UseOnlineAPI => true; [Cached] diff --git a/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs b/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs index d098ea8b16..3ecca85ef1 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.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. -using System; -using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -11,7 +9,6 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Overlays; using osu.Game.Overlays.Profile.Sections; -using osu.Game.Overlays.Profile.Sections.Historical; using osu.Game.Users; namespace osu.Game.Tests.Visual.Online @@ -21,13 +18,6 @@ namespace osu.Game.Tests.Visual.Online { protected override bool UseOnlineAPI => true; - public override IReadOnlyList RequiredTypes => new[] - { - typeof(HistoricalSection), - typeof(PaginatedMostPlayedBeatmapContainer), - typeof(DrawableMostPlayedBeatmap), - }; - [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Pink); diff --git a/osu.Game.Tests/Visual/Online/TestSceneKudosuHistory.cs b/osu.Game.Tests/Visual/Online/TestSceneKudosuHistory.cs index 325d657f0e..2231139856 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneKudosuHistory.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneKudosuHistory.cs @@ -16,11 +16,6 @@ namespace osu.Game.Tests.Visual.Online { public class TestSceneKudosuHistory : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(DrawableKudosuHistoryItem), - }; - private readonly Box background; public TestSceneKudosuHistory() diff --git a/osu.Game.Tests/Visual/Online/TestSceneLeaderboardModSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneLeaderboardModSelector.cs index 7327e80d06..54e655d4ec 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneLeaderboardModSelector.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneLeaderboardModSelector.cs @@ -2,8 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Overlays.BeatmapSet; -using System; -using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; using osu.Framework.Graphics; @@ -23,11 +21,6 @@ namespace osu.Game.Tests.Visual.Online { public class TestSceneLeaderboardModSelector : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(LeaderboardModSelector), - }; - public TestSceneLeaderboardModSelector() { LeaderboardModSelector modSelector; diff --git a/osu.Game.Tests/Visual/Online/TestSceneLeaderboardScopeSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneLeaderboardScopeSelector.cs index f9a7bc99c3..afa559280c 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneLeaderboardScopeSelector.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneLeaderboardScopeSelector.cs @@ -2,8 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Overlays.BeatmapSet; -using System; -using System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Bindables; using osu.Game.Screens.Select.Leaderboards; @@ -17,11 +15,6 @@ namespace osu.Game.Tests.Visual.Online [Cached] private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); - public override IReadOnlyList RequiredTypes => new[] - { - typeof(LeaderboardScopeSelector), - }; - public TestSceneLeaderboardScopeSelector() { Bindable scope = new Bindable(); diff --git a/osu.Game.Tests/Visual/Online/TestSceneProfileCounterPill.cs b/osu.Game.Tests/Visual/Online/TestSceneProfileCounterPill.cs index 5e2b125521..eaa989f0de 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneProfileCounterPill.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneProfileCounterPill.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. -using System; -using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -14,11 +12,6 @@ namespace osu.Game.Tests.Visual.Online { public class TestSceneProfileCounterPill : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(CounterPill) - }; - [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Red); diff --git a/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs index 826624f686..6a847e4269 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs @@ -3,8 +3,6 @@ using osu.Framework.Graphics; using osu.Game.Overlays.Profile.Header.Components; -using System; -using System.Collections.Generic; using osu.Game.Rulesets.Catch; using osu.Game.Rulesets.Mania; using osu.Game.Rulesets.Osu; @@ -18,12 +16,6 @@ namespace osu.Game.Tests.Visual.Online { public class TestSceneProfileRulesetSelector : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(ProfileRulesetSelector), - typeof(ProfileRulesetTabItem), - }; - [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green); diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankGraph.cs b/osu.Game.Tests/Visual/Online/TestSceneRankGraph.cs index 8f7e7498a9..3b31192259 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneRankGraph.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneRankGraph.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 System; -using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; -using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; using osu.Game.Overlays.Profile.Header.Components; using osu.Game.Users; @@ -20,12 +17,6 @@ namespace osu.Game.Tests.Visual.Online [TestFixture] public class TestSceneRankGraph : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(RankGraph), - typeof(LineGraph) - }; - [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Pink); diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsCountryFilter.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsCountryFilter.cs index 79862deb16..458ba80712 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneRankingsCountryFilter.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsCountryFilter.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. -using System; -using System.Collections.Generic; using osu.Framework.Bindables; using osu.Framework.Graphics.Containers; using osu.Game.Overlays.Rankings; @@ -18,12 +16,6 @@ namespace osu.Game.Tests.Visual.Online { public class TestSceneRankingsCountryFilter : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(CountryFilter), - typeof(CountryPill) - }; - [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green); diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs index 1e711b3cd7..677952681c 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.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. -using System; -using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Overlays; @@ -14,13 +12,6 @@ namespace osu.Game.Tests.Visual.Online { public class TestSceneRankingsHeader : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(RankingsOverlayHeader), - typeof(CountryFilter), - typeof(CountryPill) - }; - [Cached] private readonly OverlayColourProvider overlayColour = new OverlayColourProvider(OverlayColourScheme.Green); diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs index 83e5cd0fe7..626f545b91 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs @@ -1,9 +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.Collections.Generic; -using osu.Game.Overlays.Rankings.Tables; using osu.Framework.Allocation; using osu.Game.Overlays; using NUnit.Framework; @@ -17,18 +14,6 @@ namespace osu.Game.Tests.Visual.Online { protected override bool UseOnlineAPI => true; - public override IReadOnlyList RequiredTypes => new[] - { - typeof(PerformanceTable), - typeof(ScoresTable), - typeof(CountriesTable), - typeof(TableRowBackground), - typeof(UserBasedTable), - typeof(RankingsTable<>), - typeof(RankingsOverlay), - typeof(RankingsOverlayHeader) - }; - [Cached(typeof(RankingsOverlay))] private readonly RankingsOverlay rankingsOverlay; diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsSpotlightSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsSpotlightSelector.cs index f27ab1e775..997db827f3 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneRankingsSpotlightSelector.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsSpotlightSelector.cs @@ -15,11 +15,6 @@ namespace osu.Game.Tests.Visual.Online { public class TestSceneRankingsSpotlightSelector : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(SpotlightSelector), - }; - protected override bool UseOnlineAPI => true; [Cached] diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsTables.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsTables.cs index 8542a5e46e..a3b102dc76 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneRankingsTables.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsTables.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. -using System; -using System.Collections.Generic; using osu.Framework.Graphics.Containers; using osu.Game.Overlays.Rankings.Tables; using osu.Framework.Graphics; @@ -24,16 +22,6 @@ namespace osu.Game.Tests.Visual.Online { protected override bool UseOnlineAPI => true; - public override IReadOnlyList RequiredTypes => new[] - { - typeof(PerformanceTable), - typeof(ScoresTable), - typeof(CountriesTable), - typeof(TableRowBackground), - typeof(UserBasedTable), - typeof(RankingsTable<>) - }; - [Resolved] private IAPIProvider api { get; set; } diff --git a/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs index 51f4089058..0cb8cc22ec 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.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.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -20,15 +19,6 @@ namespace osu.Game.Tests.Visual.Online { public class TestSceneScoresContainer : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(DrawableTopScore), - typeof(TopScoreUserSection), - typeof(TopScoreStatisticsSection), - typeof(ScoreTable), - typeof(ScoreTableRowBackground), - }; - [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); diff --git a/osu.Game.Tests/Visual/Online/TestSceneShowMoreButton.cs b/osu.Game.Tests/Visual/Online/TestSceneShowMoreButton.cs index b9fbbfef6b..273f593c32 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneShowMoreButton.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneShowMoreButton.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. -using System; -using System.Collections.Generic; using osu.Framework.Graphics; using osu.Game.Graphics.UserInterface; using osu.Framework.Allocation; @@ -12,11 +10,6 @@ namespace osu.Game.Tests.Visual.Online { public class TestSceneShowMoreButton : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(ShowMoreButton), - }; - public TestSceneShowMoreButton() { TestButton button = null; diff --git a/osu.Game.Tests/Visual/Online/TestSceneSocialOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneSocialOverlay.cs index 24341cbd05..77e77d90c1 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneSocialOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneSocialOverlay.cs @@ -1,11 +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.Game.Overlays; -using osu.Game.Overlays.Social; using osu.Game.Users; namespace osu.Game.Tests.Visual.Online @@ -15,14 +12,6 @@ namespace osu.Game.Tests.Visual.Online { protected override bool UseOnlineAPI => true; - public override IReadOnlyList RequiredTypes => new[] - { - typeof(UserPanel), - typeof(FilterControl), - typeof(UserGridPanel), - typeof(UserListPanel) - }; - public TestSceneSocialOverlay() { SocialOverlay s = new SocialOverlay diff --git a/osu.Game.Tests/Visual/Online/TestSceneSpotlightsLayout.cs b/osu.Game.Tests/Visual/Online/TestSceneSpotlightsLayout.cs index d025a8d7c2..266dcb013b 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneSpotlightsLayout.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneSpotlightsLayout.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. -using System; -using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -19,12 +17,6 @@ namespace osu.Game.Tests.Visual.Online { public class TestSceneSpotlightsLayout : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(SpotlightsLayout), - typeof(SpotlightSelector), - }; - protected override bool UseOnlineAPI => true; [Cached] diff --git a/osu.Game.Tests/Visual/Online/TestSceneTotalCommentsCounter.cs b/osu.Game.Tests/Visual/Online/TestSceneTotalCommentsCounter.cs index 8ecbf0891b..f168ae5035 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneTotalCommentsCounter.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneTotalCommentsCounter.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. -using System; -using System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Bindables; using osu.Game.Overlays.Comments; @@ -14,11 +12,6 @@ namespace osu.Game.Tests.Visual.Online { public class TestSceneTotalCommentsCounter : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(TotalCommentsCounter), - }; - [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs index a38f045e7f..f763e50067 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -17,13 +16,6 @@ namespace osu.Game.Tests.Visual.Online [TestFixture] public class TestSceneUserPanel : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(UserPanel), - typeof(UserListPanel), - typeof(UserGridPanel), - }; - private readonly Bindable activity = new Bindable(); private readonly Bindable status = new Bindable(); diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs index 523de4e38f..04b741b2bb 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs @@ -2,15 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using osu.Framework.Allocation; -using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Overlays; using osu.Game.Overlays.Profile; -using osu.Game.Overlays.Profile.Header; -using osu.Game.Overlays.Profile.Header.Components; using osu.Game.Users; namespace osu.Game.Tests.Visual.Online @@ -19,18 +15,6 @@ namespace osu.Game.Tests.Visual.Online { protected override bool UseOnlineAPI => true; - public override IReadOnlyList RequiredTypes => new[] - { - typeof(ProfileHeader), - typeof(RankGraph), - typeof(LineGraph), - typeof(TabControlOverlayHeader<>.OverlayHeaderTabControl), - typeof(CentreHeaderContainer), - typeof(BottomHeaderContainer), - typeof(DetailHeaderContainer), - typeof(ProfileHeaderButton) - }; - [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green); diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs index 15f9c9a013..7ade24f4de 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs @@ -2,16 +2,12 @@ // 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.Allocation; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Overlays; using osu.Game.Overlays.Profile; -using osu.Game.Overlays.Profile.Header.Components; using osu.Game.Users; namespace osu.Game.Tests.Visual.Online @@ -26,15 +22,6 @@ namespace osu.Game.Tests.Visual.Online [Resolved] private IAPIProvider api { get; set; } - public override IReadOnlyList RequiredTypes => new[] - { - typeof(ProfileHeader), - typeof(RankGraph), - typeof(LineGraph), - typeof(SectionsContainer<>), - typeof(SupporterIcon) - }; - public static readonly User TEST_USER = new User { Username = @"Somebody", diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfilePreviousUsernames.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfilePreviousUsernames.cs index 048a1950fd..1e9d62f379 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserProfilePreviousUsernames.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfilePreviousUsernames.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -17,11 +16,6 @@ namespace osu.Game.Tests.Visual.Online [TestFixture] public class TestSceneUserProfilePreviousUsernames : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(PreviousUsernames) - }; - [Resolved] private IAPIProvider api { get; set; } diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfileRecentSection.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfileRecentSection.cs index 06091f3c81..0973076c40 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserProfileRecentSection.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfileRecentSection.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.Collections.Generic; using System.Linq; using NUnit.Framework; @@ -14,7 +13,6 @@ using osu.Game.Graphics.Containers; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; -using osu.Game.Overlays.Profile.Sections; using osu.Game.Overlays.Profile.Sections.Recent; namespace osu.Game.Tests.Visual.Online @@ -22,14 +20,6 @@ namespace osu.Game.Tests.Visual.Online [TestFixture] public class TestSceneUserProfileRecentSection : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(RecentSection), - typeof(DrawableRecentActivity), - typeof(PaginatedRecentActivityContainer), - typeof(MedalIcon) - }; - [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green); diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfileScores.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfileScores.cs index f1e745bd14..5dca218531 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserProfileScores.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfileScores.cs @@ -2,8 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; -using osu.Game.Overlays.Profile.Sections; using osu.Game.Overlays.Profile.Sections.Ranks; using osu.Framework.Graphics; using osu.Game.Scoring; @@ -19,13 +17,6 @@ namespace osu.Game.Tests.Visual.Online { public class TestSceneUserProfileScores : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(DrawableProfileScore), - typeof(DrawableProfileWeightedScore), - typeof(ProfileItemContainer), - }; - public TestSceneUserProfileScores() { var firstScore = new ScoreInfo diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserRanks.cs b/osu.Game.Tests/Visual/Online/TestSceneUserRanks.cs index c8e94b2915..c22cff4af6 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserRanks.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserRanks.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. -using System; -using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -12,7 +10,6 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Overlays; using osu.Game.Overlays.Profile.Sections; -using osu.Game.Overlays.Profile.Sections.Ranks; using osu.Game.Users; namespace osu.Game.Tests.Visual.Online @@ -22,13 +19,6 @@ namespace osu.Game.Tests.Visual.Online { protected override bool UseOnlineAPI => true; - public override IReadOnlyList RequiredTypes => new[] - { - typeof(DrawableProfileScore), - typeof(DrawableProfileWeightedScore), - typeof(RanksSection) - }; - [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green); diff --git a/osu.Game.Tests/Visual/Online/TestSceneVotePill.cs b/osu.Game.Tests/Visual/Online/TestSceneVotePill.cs index 770cef8f1b..9bb29541ec 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneVotePill.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneVotePill.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. -using System; -using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Graphics; using osu.Game.Overlays.Comments; @@ -15,11 +13,6 @@ namespace osu.Game.Tests.Visual.Online [TestFixture] public class TestSceneVotePill : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(VotePill) - }; - [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs b/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs index 0781cba924..1e87893f39 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -23,15 +22,6 @@ namespace osu.Game.Tests.Visual.Ranking { public class TestSceneAccuracyCircle : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(AccuracyCircle), - typeof(RankBadge), - typeof(RankNotch), - typeof(RankText), - typeof(SmoothCircularProgress) - }; - [Test] public void TestLowDRank() { diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs index 328a0e0c27..106b4187ee 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.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 NUnit.Framework; using osu.Framework.Allocation; @@ -22,8 +21,6 @@ using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens.Ranking; using osu.Game.Screens.Ranking.Expanded; -using osu.Game.Screens.Ranking.Expanded.Accuracy; -using osu.Game.Screens.Ranking.Expanded.Statistics; using osu.Game.Tests.Beatmaps; using osu.Game.Users; using osuTK; @@ -35,18 +32,6 @@ namespace osu.Game.Tests.Visual.Ranking [Resolved] private RulesetStore rulesetStore { get; set; } - public override IReadOnlyList RequiredTypes => new[] - { - typeof(ExpandedPanelMiddleContent), - typeof(AccuracyCircle), - typeof(AccuracyStatistic), - typeof(ComboStatistic), - typeof(CounterStatistic), - typeof(StarRatingDisplay), - typeof(StatisticDisplay), - typeof(TotalScoreCounter) - }; - [Test] public void TestMapWithKnownMapper() { diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs b/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs index bd5b039bc1..aa0ce89d93 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs @@ -26,14 +26,6 @@ namespace osu.Game.Tests.Visual.Ranking { private BeatmapManager beatmaps; - public override IReadOnlyList RequiredTypes => new[] - { - typeof(ResultsScreen), - typeof(RetryButton), - typeof(ReplayDownloadButton), - typeof(TestPlayer) - }; - [BackgroundDependencyLoader] private void load(BeatmapManager beatmaps) { diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneScorePanel.cs b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanel.cs index 1e55885385..78511b1be1 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneScorePanel.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanel.cs @@ -2,7 +2,6 @@ // 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.Mods; @@ -19,13 +18,6 @@ namespace osu.Game.Tests.Visual.Ranking { public class TestSceneScorePanel : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(ScorePanel), - typeof(PanelState), - typeof(ExpandedPanelMiddleContent), - typeof(ExpandedPanelTopContent), - }; [Test] public void TestDRank() diff --git a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs index 426ff988c4..745820696a 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs @@ -1,11 +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.Game.Overlays; -using osu.Game.Overlays.KeyBinding; namespace osu.Game.Tests.Visual.Settings { @@ -14,16 +11,6 @@ namespace osu.Game.Tests.Visual.Settings { private readonly KeyBindingPanel panel; - public override IReadOnlyList RequiredTypes => new[] - { - typeof(KeyBindingRow), - typeof(GlobalKeyBindingsSection), - typeof(KeyBindingRow), - typeof(KeyBindingsSubsection), - typeof(RulesetBindingsSection), - typeof(VariantBindingsSubsection), - }; - public TestSceneKeyBindingPanel() { Child = panel = new KeyBindingPanel(); diff --git a/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs index 668fdf2c20..115d2fec7d 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.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. -using System; -using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics.Containers; using osu.Game.Overlays; -using osu.Game.Overlays.Settings; namespace osu.Game.Tests.Visual.Settings { @@ -17,12 +14,6 @@ namespace osu.Game.Tests.Visual.Settings private readonly SettingsPanel settings; private readonly DialogOverlay dialogOverlay; - public override IReadOnlyList RequiredTypes => new[] - { - typeof(SettingsFooter), - typeof(SettingsOverlay), - }; - public TestSceneSettingsPanel() { settings = new SettingsOverlay diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs index f68ed4154b..2f12194ede 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs @@ -26,21 +26,6 @@ namespace osu.Game.Tests.Visual.SongSelect private TestBeatmapCarousel carousel; private RulesetStore rulesets; - public override IReadOnlyList RequiredTypes => new[] - { - typeof(CarouselItem), - typeof(CarouselGroup), - typeof(CarouselGroupEagerSelect), - typeof(CarouselBeatmap), - typeof(CarouselBeatmapSet), - - typeof(DrawableCarouselItem), - typeof(CarouselItemState), - - typeof(DrawableCarouselBeatmap), - typeof(DrawableCarouselBeatmapSet), - }; - private readonly Stack selectedSets = new Stack(); private readonly HashSet eagerSelectedIDs = new HashSet(); diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs index 1198488bda..48b718c04d 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs @@ -2,14 +2,12 @@ // 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; using osu.Game.Beatmaps; using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Leaderboards; using osu.Game.Overlays; -using osu.Game.Online.Placeholders; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Scoring; using osu.Game.Screens.Select.Leaderboards; @@ -20,15 +18,6 @@ namespace osu.Game.Tests.Visual.SongSelect { public class TestSceneBeatmapLeaderboard : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(Placeholder), - typeof(MessagePlaceholder), - typeof(RetrievalFailurePlaceholder), - typeof(UserTopScoreContainer), - typeof(Leaderboard), - }; - private readonly FailableLeaderboard leaderboard; [Cached] diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 81fd1b66e5..a7e2dbeccb 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -45,24 +45,6 @@ namespace osu.Game.Tests.Visual.SongSelect private WorkingBeatmap defaultBeatmap; - public override IReadOnlyList RequiredTypes => new[] - { - typeof(Screens.Select.SongSelect), - typeof(BeatmapCarousel), - - typeof(CarouselItem), - typeof(CarouselGroup), - typeof(CarouselGroupEagerSelect), - typeof(CarouselBeatmap), - typeof(CarouselBeatmapSet), - - typeof(DrawableCarouselItem), - typeof(CarouselItemState), - - typeof(DrawableCarouselBeatmap), - typeof(DrawableCarouselBeatmapSet), - }; - private TestSongSelect songSelect; [BackgroundDependencyLoader] diff --git a/osu.Game.Tests/Visual/TestSceneOsuGame.cs b/osu.Game.Tests/Visual/TestSceneOsuGame.cs index 2eaac2a45f..22ae5257e7 100644 --- a/osu.Game.Tests/Visual/TestSceneOsuGame.cs +++ b/osu.Game.Tests/Visual/TestSceneOsuGame.cs @@ -34,11 +34,6 @@ namespace osu.Game.Tests.Visual [TestFixture] public class TestSceneOsuGame : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(OsuLogo), - }; - private IReadOnlyList requiredGameDependencies => new[] { typeof(OsuGame), diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBackButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBackButton.cs index b7d7053dcd..2440911c11 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBackButton.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBackButton.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. -using System; -using System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -14,11 +12,6 @@ namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneBackButton : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(TwoLayerButton) - }; - public TestSceneBackButton() { BackButton button; diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs index b0b673d6a4..4c32e995e8 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs @@ -26,11 +26,6 @@ namespace osu.Game.Tests.Visual.UserInterface { private readonly NowPlayingOverlay np; - public override IReadOnlyList RequiredTypes => new[] - { - typeof(BeatSyncedContainer) - }; - [Cached] private MusicController musicController = new MusicController(); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchControl.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchControl.cs index d6ede950df..a4698a9a32 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchControl.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchControl.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. -using System; -using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -17,11 +15,6 @@ namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneBeatmapListingSearchControl : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(BeatmapListingSearchControl), - }; - [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSortTabControl.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSortTabControl.cs index f643d4e3fe..5364f0bef5 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSortTabControl.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSortTabControl.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. -using System; -using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -15,11 +13,6 @@ namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneBeatmapListingSortTabControl : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(OverlaySortTabControl<>), - }; - [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapSearchFilter.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapSearchFilter.cs index 283fe03af3..37b7b64615 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapSearchFilter.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapSearchFilter.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. -using System; -using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -16,12 +14,6 @@ namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneBeatmapSearchFilter : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(BeatmapSearchFilterRow<>), - typeof(BeatmapSearchRulesetFilterRow) - }; - [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs index f0e1c38525..1bb5cadc6a 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.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 NUnit.Framework; using osu.Framework.Graphics; @@ -17,13 +16,6 @@ namespace osu.Game.Tests.Visual.UserInterface [TestFixture] public class TestSceneButtonSystem : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(ButtonSystem), - typeof(ButtonArea), - typeof(Button) - }; - private OsuLogo logo; private ButtonSystem buttons; diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneCommentEditor.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneCommentEditor.cs index cef04a4c18..d0a2ca83e3 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneCommentEditor.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneCommentEditor.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. -using System; -using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -17,12 +15,6 @@ namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneCommentEditor : OsuManualInputManagerTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(CommentEditor), - typeof(CancellableCommentEditor), - }; - [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs index a812b4dc79..eb4750a597 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.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.Collections.Generic; using System.Linq; using NUnit.Framework; @@ -15,7 +14,6 @@ using osu.Game.Beatmaps; using osu.Game.Graphics.Cursor; using osu.Game.Graphics.UserInterface; using osu.Game.Online.Leaderboards; -using osu.Game.Online.Placeholders; using osu.Game.Overlays; using osu.Game.Rulesets; using osu.Game.Scoring; @@ -29,16 +27,6 @@ namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneDeleteLocalScore : OsuManualInputManagerTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(Placeholder), - typeof(MessagePlaceholder), - typeof(RetrievalFailurePlaceholder), - typeof(UserTopScoreContainer), - typeof(Leaderboard), - typeof(LeaderboardScore), - }; - private readonly ContextMenuContainer contextMenuContainer; private readonly BeatmapLeaderboard leaderboard; diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFooterButtonMods.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFooterButtonMods.cs index 63197ed26a..1e3b1c2ffd 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneFooterButtonMods.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFooterButtonMods.cs @@ -14,12 +14,6 @@ namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneFooterButtonMods : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(FooterButtonMods), - typeof(FooterButton) - }; - private readonly TestFooterButtonMods footerButtonMods; public TestSceneFooterButtonMods() diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFriendsOnlineStatusControl.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFriendsOnlineStatusControl.cs index f6dcf78d55..9fa5c83dba 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneFriendsOnlineStatusControl.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFriendsOnlineStatusControl.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.Collections.Generic; using System.Linq; using NUnit.Framework; @@ -15,15 +14,6 @@ namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneFriendsOnlineStatusControl : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(FriendOnlineStreamControl), - typeof(FriendsOnlineStatusItem), - typeof(OverlayStreamControl<>), - typeof(OverlayStreamItem<>), - typeof(FriendStream) - }; - [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneHoldToConfirmOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneHoldToConfirmOverlay.cs index feef1dae6b..cea91d422e 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneHoldToConfirmOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneHoldToConfirmOverlay.cs @@ -1,11 +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 osu.Framework.Graphics; using osu.Game.Graphics; -using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Screens.Menu; @@ -15,12 +12,6 @@ namespace osu.Game.Tests.Visual.UserInterface { protected override double TimePerAction => 100; // required for the early exit test, since hold-to-confirm delay is 200ms - public override IReadOnlyList RequiredTypes => new[] - { - typeof(ExitConfirmOverlay), - typeof(HoldToConfirmContainer), - }; - public TestSceneHoldToConfirmOverlay() { bool fired = false; diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledSwitchButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledSwitchButton.cs index 6ca4d9fa4c..903f1242b4 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledSwitchButton.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledSwitchButton.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. -using System; -using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -12,12 +10,6 @@ namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneLabelledSwitchButton : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(LabelledSwitchButton), - typeof(SwitchButton) - }; - [TestCase(false)] [TestCase(true)] public void TestSwitchButton(bool hasDescription) => createSwitchButton(hasDescription); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledTextBox.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledTextBox.cs index 8208b55952..c11ba0aa59 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledTextBox.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledTextBox.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. -using System; -using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -14,11 +12,6 @@ namespace osu.Game.Tests.Visual.UserInterface [TestFixture] public class TestSceneLabelledTextBox : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(LabelledTextBox), - }; - [TestCase(false)] [TestCase(true)] public void TestTextBox(bool hasDescription) => createTextBox(hasDescription); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneLoadingLayer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneLoadingLayer.cs index 7e9654715b..1be191fc29 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneLoadingLayer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneLoadingLayer.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. -using System; -using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -19,8 +17,6 @@ namespace osu.Game.Tests.Visual.UserInterface private Drawable dimContent; private LoadingLayer overlay; - public override IReadOnlyList RequiredTypes => new[] { typeof(LoadingSpinner) }; - private Container content; [SetUp] diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneLogoTrackingContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneLogoTrackingContainer.cs index 4e394b5ed8..010e4330d7 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneLogoTrackingContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneLogoTrackingContainer.cs @@ -2,17 +2,14 @@ // 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.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.UserInterface; using osu.Framework.Utils; using osu.Framework.Testing; using osu.Game.Graphics.Containers; using osu.Game.Screens.Menu; -using osu.Game.Screens.Play; using osuTK; using osuTK.Graphics; @@ -20,17 +17,6 @@ namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneLogoTrackingContainer : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(PlayerLoader), - typeof(Player), - typeof(LogoTrackingContainer), - typeof(ButtonSystem), - typeof(ButtonSystemState), - typeof(Menu), - typeof(MainMenu) - }; - private OsuLogo logo; private TestLogoTrackingContainer trackingContainer; private Container transferContainer; diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index ec6ee6bc83..ce691bff70 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -12,12 +12,10 @@ using osu.Framework.Testing; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Mods; -using osu.Game.Overlays.Mods.Sections; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; -using osu.Game.Rulesets.UI; using osu.Game.Screens.Play.HUD; using osuTK; using osuTK.Graphics; @@ -27,20 +25,6 @@ namespace osu.Game.Tests.Visual.UserInterface [Description("mod select and icon display")] public class TestSceneModSelectOverlay : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(ModDisplay), - typeof(ModSection), - typeof(ModIcon), - typeof(ModButton), - typeof(ModButtonEmpty), - typeof(DifficultyReductionSection), - typeof(DifficultyIncreaseSection), - typeof(AutomationSection), - typeof(ConversionSection), - typeof(FunSection), - }; - private RulesetStore rulesets; private ModDisplay modDisplay; private TestModSelectOverlay modSelect; diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs index f8ace73168..43ba23e6c6 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.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.Collections.Generic; using System.Linq; using NUnit.Framework; @@ -18,16 +17,6 @@ namespace osu.Game.Tests.Visual.UserInterface [TestFixture] public class TestSceneNotificationOverlay : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(NotificationSection), - typeof(SimpleNotification), - typeof(ProgressNotification), - typeof(ProgressCompletionNotification), - typeof(IHasCompletionTarget), - typeof(Notification) - }; - private NotificationOverlay notificationOverlay; private readonly List progressingNotifications = new List(); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneNumberBox.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneNumberBox.cs index f73450db60..97a3f62b2d 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneNumberBox.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneNumberBox.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. -using System; -using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -14,11 +12,6 @@ namespace osu.Game.Tests.Visual.UserInterface [TestFixture] public class TestSceneNumberBox : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(OsuNumberBox), - }; - private OsuNumberBox numberBox; [BackgroundDependencyLoader] diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuMenu.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuMenu.cs index 9ea76c2c7b..387deea76c 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuMenu.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuMenu.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. -using System; -using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; @@ -14,12 +12,6 @@ namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneOsuMenu : OsuManualInputManagerTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(OsuMenu), - typeof(DrawableOsuMenuItem) - }; - private OsuMenu menu; private bool actionPerformed; diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayHeader.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayHeader.cs index c81ec9f663..60af5b37ef 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayHeader.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayHeader.cs @@ -3,8 +3,6 @@ using osu.Framework.Graphics.Containers; using osu.Game.Overlays; -using System; -using System.Collections.Generic; using osu.Framework.Graphics; using osu.Game.Graphics.Sprites; using osu.Framework.Allocation; @@ -15,18 +13,6 @@ namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneOverlayHeader : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(OverlayHeader), - typeof(TabControlOverlayHeader<>), - typeof(BreadcrumbControlOverlayHeader), - typeof(TestNoControlHeader), - typeof(TestStringTabControlHeader), - typeof(TestEnumTabControlHeader), - typeof(TestBreadcrumbControlHeader), - typeof(OverlayHeaderBackground) - }; - private readonly FillFlowContainer flow; public TestSceneOverlayHeader() diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayHeaderBackground.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayHeaderBackground.cs index 5a0b28e24a..db414d23a0 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayHeaderBackground.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayHeaderBackground.cs @@ -3,8 +3,6 @@ using osu.Framework.Graphics.Containers; using osu.Game.Overlays; -using System; -using System.Collections.Generic; using osu.Framework.Graphics; using osuTK; @@ -12,11 +10,6 @@ namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneOverlayHeaderBackground : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(OverlayHeaderBackground) - }; - public TestSceneOverlayHeaderBackground() { Add(new BasicScrollContainer diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayRulesetSelector.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayRulesetSelector.cs index 8a98127793..f4fa41a3b7 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayRulesetSelector.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayRulesetSelector.cs @@ -2,8 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; -using System; -using System.Collections.Generic; using osu.Game.Rulesets.Catch; using osu.Game.Rulesets.Mania; using osu.Game.Rulesets.Osu; @@ -20,12 +18,6 @@ namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneOverlayRulesetSelector : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(OverlayRulesetSelector), - typeof(OverlayRulesetTabItem), - }; - private readonly OverlayRulesetSelector selector; private readonly Bindable ruleset = new Bindable(); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayScrollContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayScrollContainer.cs index e9e63613c0..7fa730e02b 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayScrollContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayScrollContainer.cs @@ -3,8 +3,6 @@ using osu.Framework.Graphics.Containers; using osu.Game.Overlays; -using System; -using System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Allocation; using osu.Framework.Graphics.Shapes; @@ -17,11 +15,6 @@ namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneOverlayScrollContainer : OsuManualInputManagerTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(OverlayScrollContainer) - }; - [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); diff --git a/osu.Game.Tests/Visual/UserInterface/TestScenePlaylistOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestScenePlaylistOverlay.cs index 7476b52b49..a470244f53 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestScenePlaylistOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestScenePlaylistOverlay.cs @@ -2,7 +2,6 @@ // 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.Framework.Graphics; @@ -16,12 +15,6 @@ namespace osu.Game.Tests.Visual.UserInterface { public class TestScenePlaylistOverlay : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(PlaylistOverlay), - typeof(Playlist) - }; - private readonly BindableList beatmapSets = new BindableList(); [SetUp] diff --git a/osu.Game.Tests/Visual/UserInterface/TestScenePopupDialog.cs b/osu.Game.Tests/Visual/UserInterface/TestScenePopupDialog.cs index 7207506ccd..8e53c7c402 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestScenePopupDialog.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestScenePopupDialog.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. -using System; -using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Dialog; namespace osu.Game.Tests.Visual.UserInterface @@ -14,14 +11,6 @@ namespace osu.Game.Tests.Visual.UserInterface [TestFixture] public class TestScenePopupDialog : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(PopupDialogOkButton), - typeof(PopupDialogCancelButton), - typeof(PopupDialogButton), - typeof(DialogButton), - }; - public TestScenePopupDialog() { AddStep("new popup", () => diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneStatefulMenuItem.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneStatefulMenuItem.cs index 85fea73bf5..29aeb6a4b2 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneStatefulMenuItem.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneStatefulMenuItem.cs @@ -2,7 +2,6 @@ // 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.Framework.Graphics; @@ -14,14 +13,6 @@ namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneStatefulMenuItem : OsuManualInputManagerTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(OsuMenu), - typeof(StatefulMenuItem), - typeof(TernaryStateMenuItem), - typeof(DrawableStatefulMenuItem), - }; - [Test] public void TestTernaryMenuItem() { diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneToggleMenuItem.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneToggleMenuItem.cs index 2abda56a28..9fb8e747f3 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneToggleMenuItem.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneToggleMenuItem.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. -using System; -using System.Collections.Generic; using osu.Framework.Graphics; using osu.Game.Graphics.UserInterface; @@ -10,13 +8,6 @@ namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneToggleMenuItem : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(OsuMenu), - typeof(ToggleMenuItem), - typeof(DrawableStatefulMenuItem) - }; - public TestSceneToggleMenuItem() { Add(new OsuMenu(Direction.Vertical, true) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneToolbarRulesetSelector.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneToolbarRulesetSelector.cs index 9738f73548..cdfbb14cba 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneToolbarRulesetSelector.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneToolbarRulesetSelector.cs @@ -3,8 +3,6 @@ using osu.Framework.Graphics.Containers; using osu.Game.Overlays.Toolbar; -using System; -using System.Collections.Generic; using osu.Framework.Graphics; using System.Linq; using NUnit.Framework; @@ -16,12 +14,6 @@ namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneToolbarRulesetSelector : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(ToolbarRulesetSelector), - typeof(ToolbarRulesetTabButton), - }; - [Resolved] private RulesetStore rulesets { get; set; } diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneUserListToolbar.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneUserListToolbar.cs index 1546972580..8f7140ed7c 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneUserListToolbar.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneUserListToolbar.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. -using System; -using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -15,14 +13,6 @@ namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneUserListToolbar : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(UserSortTabControl), - typeof(OverlaySortTabControl<>), - typeof(OverlayPanelDisplayStyleControl), - typeof(UserListToolbar), - }; - [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneVolumePieces.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneVolumePieces.cs index 2fe6240b22..c8478c8eca 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneVolumePieces.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneVolumePieces.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. -using System; -using System.Collections.Generic; using osu.Framework.Graphics; using osu.Game.Overlays.Volume; using osuTK; @@ -12,8 +10,6 @@ namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneVolumePieces : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] { typeof(VolumeMeter), typeof(MuteButton) }; - protected override void LoadComplete() { VolumeMeter meter; diff --git a/osu.Game.Tournament.Tests/Components/TestSceneDrawableTournamentMatch.cs b/osu.Game.Tournament.Tests/Components/TestSceneDrawableTournamentMatch.cs index e65b708fea..f98f55dfbc 100644 --- a/osu.Game.Tournament.Tests/Components/TestSceneDrawableTournamentMatch.cs +++ b/osu.Game.Tournament.Tests/Components/TestSceneDrawableTournamentMatch.cs @@ -1,11 +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 osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Tournament.Components; using osu.Game.Tournament.Models; using osu.Game.Tournament.Screens.Ladder.Components; @@ -13,12 +10,6 @@ namespace osu.Game.Tournament.Tests.Components { public class TestSceneDrawableTournamentMatch : TournamentTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(TournamentMatch), - typeof(DrawableTournamentTeam), - }; - public TestSceneDrawableTournamentMatch() { Container level1; diff --git a/osu.Game.Tournament.Tests/Components/TestSceneDrawableTournamentTeam.cs b/osu.Game.Tournament.Tests/Components/TestSceneDrawableTournamentTeam.cs index 01edcb66e4..376c59ec2d 100644 --- a/osu.Game.Tournament.Tests/Components/TestSceneDrawableTournamentTeam.cs +++ b/osu.Game.Tournament.Tests/Components/TestSceneDrawableTournamentTeam.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. -using System; -using System.Collections.Generic; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Tests.Visual; @@ -17,17 +15,6 @@ namespace osu.Game.Tournament.Tests.Components { public class TestSceneDrawableTournamentTeam : OsuGridTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(DrawableTeamFlag), - typeof(DrawableTeamTitle), - typeof(DrawableTeamTitleWithHeader), - typeof(DrawableMatchTeam), - typeof(DrawableTeamWithPlayers), - typeof(GroupTeam), - typeof(TeamDisplay), - }; - public TestSceneDrawableTournamentTeam() : base(4, 3) { diff --git a/osu.Game.Tournament.Tests/Components/TestSceneMatchHeader.cs b/osu.Game.Tournament.Tests/Components/TestSceneMatchHeader.cs index 9f885ed827..b29e4964b6 100644 --- a/osu.Game.Tournament.Tests/Components/TestSceneMatchHeader.cs +++ b/osu.Game.Tournament.Tests/Components/TestSceneMatchHeader.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. -using System; -using System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; -using osu.Game.Tournament.Components; using osu.Game.Tournament.Screens.Gameplay.Components; using osuTK; @@ -14,12 +11,6 @@ namespace osu.Game.Tournament.Tests.Components { public class TestSceneMatchHeader : TournamentTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(DrawableTournamentHeaderText), - typeof(DrawableTournamentHeaderLogo), - }; - public TestSceneMatchHeader() { Child = new FillFlowContainer diff --git a/osu.Game.Tournament.Tests/Components/TestSceneRoundDisplay.cs b/osu.Game.Tournament.Tests/Components/TestSceneRoundDisplay.cs index 6f71627ce4..13bca7bea1 100644 --- a/osu.Game.Tournament.Tests/Components/TestSceneRoundDisplay.cs +++ b/osu.Game.Tournament.Tests/Components/TestSceneRoundDisplay.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. -using System; -using System.Collections.Generic; using osu.Framework.Graphics; using osu.Game.Tournament.Components; using osu.Game.Tournament.Models; @@ -11,12 +9,6 @@ namespace osu.Game.Tournament.Tests.Components { public class TestSceneRoundDisplay : TournamentTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(DrawableTournamentHeaderText), - typeof(DrawableTournamentHeaderLogo), - }; - public TestSceneRoundDisplay() { Children = new Drawable[] diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneGameplayScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneGameplayScreen.cs index 34fa7a4997..c1159dc000 100644 --- a/osu.Game.Tournament.Tests/Screens/TestSceneGameplayScreen.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneGameplayScreen.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. -using System; -using System.Collections.Generic; using osu.Framework.Allocation; using osu.Game.Tournament.Components; -using osu.Game.Tournament.Screens; using osu.Game.Tournament.Screens.Gameplay; -using osu.Game.Tournament.Screens.Gameplay.Components; namespace osu.Game.Tournament.Tests.Screens { @@ -16,17 +12,6 @@ namespace osu.Game.Tournament.Tests.Screens [Cached] private TournamentMatchChatDisplay chat = new TournamentMatchChatDisplay { Width = 0.5f }; - public override IReadOnlyList RequiredTypes => new[] - { - typeof(TeamScore), - typeof(TeamScoreDisplay), - typeof(TeamDisplay), - typeof(MatchHeader), - typeof(MatchScoreDisplay), - typeof(BeatmapInfoScreen), - typeof(SongBar), - }; - [BackgroundDependencyLoader] private void load() { diff --git a/osu.Game/Tests/Visual/EditorTestScene.cs b/osu.Game/Tests/Visual/EditorTestScene.cs index caf2bc0ff1..2f6e6fb599 100644 --- a/osu.Game/Tests/Visual/EditorTestScene.cs +++ b/osu.Game/Tests/Visual/EditorTestScene.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. -using System; -using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Testing; @@ -15,8 +13,6 @@ namespace osu.Game.Tests.Visual { public abstract class EditorTestScene : ScreenTestScene { - public override IReadOnlyList RequiredTypes => new[] { typeof(Editor), typeof(EditorScreen) }; - protected Editor Editor { get; private set; } private readonly Ruleset ruleset; diff --git a/osu.Game/Tests/Visual/ModTestScene.cs b/osu.Game/Tests/Visual/ModTestScene.cs index 8b41fb5075..1fa638b3d8 100644 --- a/osu.Game/Tests/Visual/ModTestScene.cs +++ b/osu.Game/Tests/Visual/ModTestScene.cs @@ -14,11 +14,6 @@ namespace osu.Game.Tests.Visual { protected sealed override bool HasCustomSteps => true; - public override IReadOnlyList RequiredTypes => new[] - { - typeof(ModTestScene) - }; - protected ModTestScene(Ruleset ruleset) : base(ruleset) { From 2bde4fc3eed85600b421a2cb30e83e310a5f68de Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 16 May 2020 18:17:32 +0900 Subject: [PATCH 0761/1117] Initial implementation of contracted score panel --- .../Scoring/OsuScoreProcessor.cs | 2 +- .../TestSceneContractedPanelMiddleContent.cs | 118 ++++++++ .../ContractedPanelMiddleContent.cs | 255 ++++++++++++++++++ osu.Game/Screens/Ranking/ScorePanel.cs | 11 +- 4 files changed, 381 insertions(+), 5 deletions(-) create mode 100644 osu.Game.Tests/Visual/Ranking/TestSceneContractedPanelMiddleContent.cs create mode 100644 osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs index 1de7d488f3..79a6ea7e92 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs @@ -8,7 +8,7 @@ using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Scoring { - internal class OsuScoreProcessor : ScoreProcessor + public class OsuScoreProcessor : ScoreProcessor { protected override JudgementResult CreateResult(HitObject hitObject, Judgement judgement) => new OsuJudgementResult(hitObject, judgement); diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneContractedPanelMiddleContent.cs b/osu.Game.Tests/Visual/Ranking/TestSceneContractedPanelMiddleContent.cs new file mode 100644 index 0000000000..af3d13777c --- /dev/null +++ b/osu.Game.Tests/Visual/Ranking/TestSceneContractedPanelMiddleContent.cs @@ -0,0 +1,118 @@ +// 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; +using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Testing; +using osu.Game.Beatmaps; +using osu.Game.Graphics.Sprites; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Rulesets.Scoring; +using osu.Game.Scoring; +using osu.Game.Screens.Ranking; +using osu.Game.Screens.Ranking.Contracted; +using osu.Game.Tests.Beatmaps; +using osu.Game.Users; +using osuTK; + +namespace osu.Game.Tests.Visual.Ranking +{ + public class TestSceneContractedPanelMiddleContent : OsuTestScene + { + [Resolved] + private RulesetStore rulesetStore { get; set; } + + [Test] + public void TestMapWithKnownMapper() + { + var author = new User { Username = "mapper_name" }; + + AddStep("show example score", () => showPanel(createTestBeatmap(author), createTestScore())); + + AddAssert("mapper name present", () => this.ChildrenOfType().Any(spriteText => spriteText.Text == "mapper_name")); + } + + [Test] + public void TestMapWithUnknownMapper() + { + AddStep("show example score", () => showPanel(createTestBeatmap(null), createTestScore())); + + AddAssert("mapped by text not present", () => + this.ChildrenOfType().All(spriteText => !containsAny(spriteText.Text, "mapped", "by"))); + } + + private void showPanel(WorkingBeatmap workingBeatmap, ScoreInfo score) + { + Child = new ContractedPanelMiddleContentContainer(workingBeatmap, score); + } + + private WorkingBeatmap createTestBeatmap(User author) + { + var beatmap = new TestBeatmap(rulesetStore.GetRuleset(0)); + beatmap.Metadata.Author = author; + beatmap.Metadata.Title = "Verrrrrrrrrrrrrrrrrrry looooooooooooooooooooooooong beatmap title"; + beatmap.Metadata.Artist = "Verrrrrrrrrrrrrrrrrrry looooooooooooooooooooooooong beatmap artist"; + + return new TestWorkingBeatmap(beatmap); + } + + private ScoreInfo createTestScore() => new ScoreInfo + { + User = new User + { + Id = 2, + Username = "peppy", + }, + Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo, + Mods = new Mod[] { new OsuModHardRock(), new OsuModDoubleTime() }, + TotalScore = 999999, + Accuracy = 0.95, + MaxCombo = 999, + Rank = ScoreRank.S, + Date = DateTimeOffset.Now, + Statistics = + { + { HitResult.Miss, 1 }, + { HitResult.Meh, 50 }, + { HitResult.Good, 100 }, + { HitResult.Great, 300 }, + } + }; + + private bool containsAny(string text, params string[] stringsToMatch) => stringsToMatch.Any(text.Contains); + + private class ContractedPanelMiddleContentContainer : Container + { + [Cached] + private Bindable workingBeatmap { get; set; } + + public ContractedPanelMiddleContentContainer(WorkingBeatmap beatmap, ScoreInfo score) + { + workingBeatmap = new Bindable(beatmap); + + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + Size = new Vector2(ScorePanel.CONTRACTED_WIDTH, 700); + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4Extensions.FromHex("#353535"), + }, + new ContractedPanelMiddleContent(score), + }; + } + } + } +} diff --git a/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs new file mode 100644 index 0000000000..5ecb3fbd0b --- /dev/null +++ b/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs @@ -0,0 +1,255 @@ +// 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.Extensions; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Online.Leaderboards; +using osu.Game.Scoring; +using osu.Game.Screens.Play.HUD; +using osu.Game.Users; +using osu.Game.Users.Drawables; +using osu.Game.Utils; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Screens.Ranking.Contracted +{ + /// + /// The content that appears in the middle of a contracted . + /// + public class ContractedPanelMiddleContent : CompositeDrawable + { + private readonly ScoreInfo score; + + /// + /// Creates a new . + /// + /// The to display. + public ContractedPanelMiddleContent(ScoreInfo score) + { + this.score = score; + RelativeSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader] + private void load() + { + InternalChild = new GridContainer + { + RelativeSizeAxes = Axes.Both, + Content = new[] + { + new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Container + { + Name = "Background", + RelativeSizeAxes = Axes.Both, + Masking = true, + CornerExponent = 2.5f, + CornerRadius = 20, + EdgeEffect = new EdgeEffectParameters + { + Colour = Color4.Black.Opacity(0.25f), + Type = EdgeEffectType.Shadow, + Radius = 1, + Offset = new Vector2(0, 4) + }, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4Extensions.FromHex("444") + }, + new UserCoverBackground + { + RelativeSizeAxes = Axes.Both, + User = score.User, + }, + } + }, + new Container + { + Name = "Background overlay", + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Bottom = -1 }, + Child = new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true, + CornerExponent = 2.5f, + CornerRadius = 20, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.5f), Color4Extensions.FromHex("#444")) + } + } + }, + new Container + { + Name = "Foreground", + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding(10), + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 10), + Children = new Drawable[] + { + new UpdateableAvatar(score.User) + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Size = new Vector2(140), + Masking = true, + CornerExponent = 2.5f, + CornerRadius = 20, + EdgeEffect = new EdgeEffectParameters + { + Colour = Color4.Black.Opacity(0.25f), + Type = EdgeEffectType.Shadow, + Radius = 8, + Offset = new Vector2(0, 4), + } + }, + new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = score.UserString, + Font = OsuFont.GetFont(size: 16, weight: FontWeight.SemiBold) + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 5), + ChildrenEnumerable = score.SortedStatistics.Select(s => createStatistic(s.Key.GetDescription(), s.Value.ToString())) + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Margin = new MarginPadding { Top = 10 }, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 5), + Children = new[] + { + createStatistic("Max Combo", $"x{score.MaxCombo}"), + createStatistic("Accuracy", $"{score.Accuracy.FormatAccuracy()}"), + } + }, + new ModDisplay + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + AutoSizeAxes = Axes.Both, + ExpansionMode = ExpansionMode.AlwaysExpanded, + DisplayUnrankedText = false, + Current = { Value = score.Mods }, + Scale = new Vector2(0.5f), + } + } + } + } + } + } + }, + }, + new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Vertical = 5 }, + Child = new GridContainer + { + RelativeSizeAxes = Axes.Both, + Content = new[] + { + new Drawable[] + { + new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = score.TotalScore.ToString(), + Font = OsuFont.GetFont(size: 20, weight: FontWeight.Medium, fixedWidth: true), + Spacing = new Vector2(-1, 0) + }, + }, + new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Top = 2 }, + Child = new DrawableRank(score.Rank) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } + } + }, + }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + } + } + } + }, + }, + RowDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.Absolute, 45), + } + }; + } + + private Drawable createStatistic(string key, string value) => new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Text = key, + Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold) + }, + new OsuSpriteText + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Text = value, + Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold), + Colour = Color4Extensions.FromHex("#FFDD55") + } + } + }; + } +} diff --git a/osu.Game/Screens/Ranking/ScorePanel.cs b/osu.Game/Screens/Ranking/ScorePanel.cs index c055df7ccc..bf57cb4dd9 100644 --- a/osu.Game/Screens/Ranking/ScorePanel.cs +++ b/osu.Game/Screens/Ranking/ScorePanel.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Scoring; +using osu.Game.Screens.Ranking.Contracted; using osu.Game.Screens.Ranking.Expanded; using osuTK; using osuTK.Graphics; @@ -21,12 +22,12 @@ namespace osu.Game.Screens.Ranking /// /// Width of the panel when contracted. /// - private const float contracted_width = 160; + public const float CONTRACTED_WIDTH = 160; /// /// Height of the panel when contracted. /// - private const float contracted_height = 320; + private const float contracted_height = 385; /// /// Width of the panel when expanded. @@ -71,7 +72,7 @@ namespace osu.Game.Screens.Ranking private static readonly ColourInfo expanded_top_layer_colour = ColourInfo.GradientVertical(Color4Extensions.FromHex("#444"), Color4Extensions.FromHex("#333")); private static readonly ColourInfo expanded_middle_layer_colour = ColourInfo.GradientVertical(Color4Extensions.FromHex("#555"), Color4Extensions.FromHex("#333")); private static readonly Color4 contracted_top_layer_colour = Color4Extensions.FromHex("#353535"); - private static readonly Color4 contracted_middle_layer_colour = Color4Extensions.FromHex("#444"); + private static readonly Color4 contracted_middle_layer_colour = Color4Extensions.FromHex("#353535"); public event Action StateChanged; @@ -193,10 +194,12 @@ namespace osu.Game.Screens.Ranking break; case PanelState.Contracted: - this.ResizeTo(new Vector2(contracted_width, contracted_height), resize_duration, Easing.OutQuint); + this.ResizeTo(new Vector2(CONTRACTED_WIDTH, contracted_height), resize_duration, Easing.OutQuint); topLayerBackground.FadeColour(contracted_top_layer_colour, resize_duration, Easing.OutQuint); middleLayerBackground.FadeColour(contracted_middle_layer_colour, resize_duration, Easing.OutQuint); + + middleLayerContentContainer.Add(topLayerContent = new ContractedPanelMiddleContent(score).With(d => d.Alpha = 0)); break; } From 3df92925ee704dce60d127c04557d3aaac15c9c3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 16 May 2020 18:22:07 +0900 Subject: [PATCH 0762/1117] Add score panel test --- .../Visual/Ranking/TestSceneScorePanel.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneScorePanel.cs b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanel.cs index 1e55885385..7431002c02 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneScorePanel.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanel.cs @@ -107,13 +107,23 @@ namespace osu.Game.Tests.Visual.Ranking addPanelStep(score); } - private void addPanelStep(ScoreInfo score) => AddStep("add panel", () => + [Test] + public void TestContractedPanel() + { + var score = createScore(); + score.Accuracy = 0.925; + score.Rank = ScoreRank.A; + + addPanelStep(score, PanelState.Contracted); + } + + private void addPanelStep(ScoreInfo score, PanelState state = PanelState.Expanded) => AddStep("add panel", () => { Child = new ScorePanel(score) { Anchor = Anchor.Centre, Origin = Anchor.Centre, - State = PanelState.Expanded + State = state }; }); From 9b7b1ef605aa3fc7dbe22682dae4bc496974d28c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 16 May 2020 18:23:18 +0900 Subject: [PATCH 0763/1117] Add cover urls --- .../Visual/Ranking/TestSceneContractedPanelMiddleContent.cs | 1 + osu.Game.Tests/Visual/Ranking/TestSceneScorePanel.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneContractedPanelMiddleContent.cs b/osu.Game.Tests/Visual/Ranking/TestSceneContractedPanelMiddleContent.cs index af3d13777c..f7694c10ec 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneContractedPanelMiddleContent.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneContractedPanelMiddleContent.cs @@ -72,6 +72,7 @@ namespace osu.Game.Tests.Visual.Ranking { Id = 2, Username = "peppy", + CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", }, Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo, Mods = new Mod[] { new OsuModHardRock(), new OsuModDoubleTime() }, diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneScorePanel.cs b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanel.cs index 7431002c02..27905f95fd 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneScorePanel.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanel.cs @@ -133,6 +133,7 @@ namespace osu.Game.Tests.Visual.Ranking { Id = 2, Username = "peppy", + CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", }, Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo, Mods = new Mod[] { new OsuModHardRock(), new OsuModDoubleTime() }, From 0279bcf3c8cc2ee3bc53f5b7aaa9925870dae71c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 16 May 2020 18:28:22 +0900 Subject: [PATCH 0764/1117] Fix missed issues --- osu.Game.Tests/Visual/Ranking/TestSceneScorePanel.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneScorePanel.cs b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanel.cs index 78511b1be1..880e331b92 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneScorePanel.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanel.cs @@ -10,7 +10,6 @@ using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens.Ranking; -using osu.Game.Screens.Ranking.Expanded; using osu.Game.Tests.Beatmaps; using osu.Game.Users; @@ -18,7 +17,6 @@ namespace osu.Game.Tests.Visual.Ranking { public class TestSceneScorePanel : OsuTestScene { - [Test] public void TestDRank() { From 8c5ccf574b3e6fbf6e26a85491713f3acf71ab7a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 16 May 2020 18:28:15 +0900 Subject: [PATCH 0765/1117] Add better fix for 1px bleed --- .../ContractedPanelMiddleContent.cs | 153 ++++++++---------- 1 file changed, 66 insertions(+), 87 deletions(-) diff --git a/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs index 5ecb3fbd0b..1d7d5c4130 100644 --- a/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs @@ -53,22 +53,22 @@ namespace osu.Game.Screens.Ranking.Contracted new Container { RelativeSizeAxes = Axes.Both, + Masking = true, + CornerExponent = 2.5f, + CornerRadius = 20, + EdgeEffect = new EdgeEffectParameters + { + Colour = Color4.Black.Opacity(0.25f), + Type = EdgeEffectType.Shadow, + Radius = 1, + Offset = new Vector2(0, 4) + }, Children = new Drawable[] { - new Container + // Buffered container is used to prevent 1px bleed outside the masking region + new BufferedContainer { - Name = "Background", RelativeSizeAxes = Axes.Both, - Masking = true, - CornerExponent = 2.5f, - CornerRadius = 20, - EdgeEffect = new EdgeEffectParameters - { - Colour = Color4.Black.Opacity(0.25f), - Type = EdgeEffectType.Shadow, - Radius = 1, - Offset = new Vector2(0, 4) - }, Children = new Drawable[] { new Box @@ -81,95 +81,74 @@ namespace osu.Game.Screens.Ranking.Contracted RelativeSizeAxes = Axes.Both, User = score.User, }, - } - }, - new Container - { - Name = "Background overlay", - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Bottom = -1 }, - Child = new Container - { - RelativeSizeAxes = Axes.Both, - Masking = true, - CornerExponent = 2.5f, - CornerRadius = 20, - Child = new Box + new Box { RelativeSizeAxes = Axes.Both, Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.5f), Color4Extensions.FromHex("#444")) - } + }, } }, - new Container + new FillFlowContainer { - Name = "Foreground", RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding(10), + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 10), Children = new Drawable[] { + new UpdateableAvatar(score.User) + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Size = new Vector2(140), + Masking = true, + CornerExponent = 2.5f, + CornerRadius = 20, + EdgeEffect = new EdgeEffectParameters + { + Colour = Color4.Black.Opacity(0.25f), + Type = EdgeEffectType.Shadow, + Radius = 8, + Offset = new Vector2(0, 4), + } + }, + new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = score.UserString, + Font = OsuFont.GetFont(size: 16, weight: FontWeight.SemiBold) + }, new FillFlowContainer { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding(10), + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 10), - Children = new Drawable[] + Spacing = new Vector2(0, 5), + ChildrenEnumerable = score.SortedStatistics.Select(s => createStatistic(s.Key.GetDescription(), s.Value.ToString())) + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Margin = new MarginPadding { Top = 10 }, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 5), + Children = new[] { - new UpdateableAvatar(score.User) - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Size = new Vector2(140), - Masking = true, - CornerExponent = 2.5f, - CornerRadius = 20, - EdgeEffect = new EdgeEffectParameters - { - Colour = Color4.Black.Opacity(0.25f), - Type = EdgeEffectType.Shadow, - Radius = 8, - Offset = new Vector2(0, 4), - } - }, - new OsuSpriteText - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Text = score.UserString, - Font = OsuFont.GetFont(size: 16, weight: FontWeight.SemiBold) - }, - new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 5), - ChildrenEnumerable = score.SortedStatistics.Select(s => createStatistic(s.Key.GetDescription(), s.Value.ToString())) - }, - new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Margin = new MarginPadding { Top = 10 }, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 5), - Children = new[] - { - createStatistic("Max Combo", $"x{score.MaxCombo}"), - createStatistic("Accuracy", $"{score.Accuracy.FormatAccuracy()}"), - } - }, - new ModDisplay - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - AutoSizeAxes = Axes.Both, - ExpansionMode = ExpansionMode.AlwaysExpanded, - DisplayUnrankedText = false, - Current = { Value = score.Mods }, - Scale = new Vector2(0.5f), - } + createStatistic("Max Combo", $"x{score.MaxCombo}"), + createStatistic("Accuracy", $"{score.Accuracy.FormatAccuracy()}"), } + }, + new ModDisplay + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + AutoSizeAxes = Axes.Both, + ExpansionMode = ExpansionMode.AlwaysExpanded, + DisplayUnrankedText = false, + Current = { Value = score.Mods }, + Scale = new Vector2(0.5f), } } } From cfa5a81e7844eb136f7e1fed6a04b3374788709e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 16 May 2020 18:28:25 +0900 Subject: [PATCH 0766/1117] Cleanup testscene --- osu.Game.Tests/Visual/Ranking/TestSceneScorePanel.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneScorePanel.cs b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanel.cs index 3b8ce7d837..0dbafb18bc 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneScorePanel.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanel.cs @@ -10,7 +10,6 @@ using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens.Ranking; -using osu.Game.Screens.Ranking.Expanded; using osu.Game.Tests.Beatmaps; using osu.Game.Users; @@ -18,7 +17,6 @@ namespace osu.Game.Tests.Visual.Ranking { public class TestSceneScorePanel : OsuTestScene { - [Test] public void TestDRank() { From e3c1112b5ab10e981ce59c1c2a851ecdcf7a0dbb Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 16 May 2020 19:00:20 +0900 Subject: [PATCH 0767/1117] Initial integration into results screen --- osu.Game/Screens/Ranking/ResultsScreen.cs | 57 +++++++++++++++++++++-- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index cfba1e6e3e..f2458d9f1f 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.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.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -10,6 +11,9 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Screens; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Rulesets; using osu.Game.Scoring; using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Play; @@ -31,11 +35,18 @@ namespace osu.Game.Screens.Ranking [Resolved(CanBeNull = true)] private Player player { get; set; } + [Resolved] + private IAPIProvider api { get; set; } + + [Resolved] + private RulesetStore rulesets { get; set; } + public readonly ScoreInfo Score; private readonly bool allowRetry; private Drawable bottomPanel; + private Container contractedPanels; public ResultsScreen(ScoreInfo score, bool allowRetry = true) { @@ -52,12 +63,29 @@ namespace osu.Game.Screens.Ranking { new ResultsScrollContainer { - Child = new ScorePanel(Score) + Children = new Drawable[] { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - State = PanelState.Expanded - }, + new ScorePanel(Score) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + State = PanelState.Expanded + }, + new OsuScrollContainer(Direction.Horizontal) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + ScrollbarVisible = false, + Child = contractedPanels = new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Direction = FillDirection.Horizontal, + AutoSizeAxes = Axes.Both + } + } + } }, bottomPanel = new Container { @@ -105,6 +133,25 @@ namespace osu.Game.Screens.Ranking } } + protected override void LoadComplete() + { + base.LoadComplete(); + + var req = new GetScoresRequest(Score.Beatmap, Score.Ruleset); + + req.Success += r => + { + contractedPanels.ChildrenEnumerable = r.Scores.Select(s => s.CreateScoreInfo(rulesets)).Select(s => new ScorePanel(s) + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + State = PanelState.Contracted + }); + }; + + api.Queue(req); + } + public override void OnEntering(IScreen last) { base.OnEntering(last); From 358345cee72cf0e0c6ae57936ee8df27de4d72ac Mon Sep 17 00:00:00 2001 From: Shivam Date: Sat, 16 May 2020 12:50:56 +0200 Subject: [PATCH 0768/1117] Change logic for parentscreen/subscreen relation --- .../Screens/TestSceneSeedingEditorScreen.cs | 2 +- .../Screens/Editors/SeedingEditorScreen.cs | 7 ++----- .../Screens/Editors/TeamEditorScreen.cs | 6 +++--- .../Screens/Editors/TournamentEditorScreen.cs | 16 +++++++++------- 4 files changed, 15 insertions(+), 16 deletions(-) diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneSeedingEditorScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneSeedingEditorScreen.cs index 17cccd34b6..8d12d5393d 100644 --- a/osu.Game.Tournament.Tests/Screens/TestSceneSeedingEditorScreen.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneSeedingEditorScreen.cs @@ -16,7 +16,7 @@ namespace osu.Game.Tournament.Tests.Screens { var match = CreateSampleMatch(); - Add(new SeedingEditorScreen(match.Team1.Value) + Add(new SeedingEditorScreen(match.Team1.Value, new TeamEditorScreen()) { Width = 0.85f // create room for control panel }); diff --git a/osu.Game.Tournament/Screens/Editors/SeedingEditorScreen.cs b/osu.Game.Tournament/Screens/Editors/SeedingEditorScreen.cs index 0f980ec9a3..0973a7dc75 100644 --- a/osu.Game.Tournament/Screens/Editors/SeedingEditorScreen.cs +++ b/osu.Game.Tournament/Screens/Editors/SeedingEditorScreen.cs @@ -28,11 +28,8 @@ namespace osu.Game.Tournament.Screens.Editors [Resolved(canBeNull: true)] private TournamentSceneManager sceneManager { get; set; } - protected override bool IsSubScreen => true; - - protected override System.Type ParentScreen => typeof(TeamEditorScreen); - - public SeedingEditorScreen(TournamentTeam team) + public SeedingEditorScreen(TournamentTeam team, TournamentScreen parentScreen) + : base(parentScreen) { this.team = team; } diff --git a/osu.Game.Tournament/Screens/Editors/TeamEditorScreen.cs b/osu.Game.Tournament/Screens/Editors/TeamEditorScreen.cs index 81487f1bcf..dbfcfe4225 100644 --- a/osu.Game.Tournament/Screens/Editors/TeamEditorScreen.cs +++ b/osu.Game.Tournament/Screens/Editors/TeamEditorScreen.cs @@ -38,7 +38,7 @@ namespace osu.Game.Tournament.Screens.Editors }); } - protected override TeamRow CreateDrawable(TournamentTeam model) => new TeamRow(model); + protected override TeamRow CreateDrawable(TournamentTeam model) => new TeamRow(model, this); private void addAllCountries() { @@ -63,7 +63,7 @@ namespace osu.Game.Tournament.Screens.Editors [Resolved] private LadderInfo ladderInfo { get; set; } - public TeamRow(TournamentTeam team) + public TeamRow(TournamentTeam team, TournamentScreen parent) { Model = team; @@ -154,7 +154,7 @@ namespace osu.Game.Tournament.Screens.Editors Text = "Edit seeding results", Action = () => { - sceneManager?.SetScreen(new SeedingEditorScreen(team)); + sceneManager?.SetScreen(new SeedingEditorScreen(team, parent)); } }, } diff --git a/osu.Game.Tournament/Screens/Editors/TournamentEditorScreen.cs b/osu.Game.Tournament/Screens/Editors/TournamentEditorScreen.cs index bca0814d3a..c0b56f1e68 100644 --- a/osu.Game.Tournament/Screens/Editors/TournamentEditorScreen.cs +++ b/osu.Game.Tournament/Screens/Editors/TournamentEditorScreen.cs @@ -33,13 +33,15 @@ namespace osu.Game.Tournament.Screens.Editors protected ControlPanel ControlPanel; - protected virtual bool IsSubScreen => false; - - protected virtual System.Type ParentScreen { get; set; } - + private readonly TournamentScreen parentScreen; private BackButton backButton; - private System.Action backAction => () => sceneManager?.SetScreen(ParentScreen); + private System.Action backAction => () => sceneManager?.SetScreen(parentScreen.GetType()); + + protected TournamentEditorScreen(TournamentScreen parentScreen = null) + { + this.parentScreen = parentScreen; + } [BackgroundDependencyLoader] private void load() @@ -51,7 +53,7 @@ namespace osu.Game.Tournament.Screens.Editors Origin = Anchor.BottomLeft, Action = () => { - if (IsSubScreen) + if (parentScreen != null) backAction.Invoke(); } }; @@ -98,7 +100,7 @@ namespace osu.Game.Tournament.Screens.Editors } }); - if (IsSubScreen) + if (parentScreen != null) backButton.Show(); Storage.CollectionChanged += (_, args) => From 2c0ac8cc36dd55c859c05b9deb14efdb90479ffb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 17 May 2020 17:25:26 +0900 Subject: [PATCH 0769/1117] Move padding to fill, not scroll container --- osu.Game.Tournament/Screens/Editors/TournamentEditorScreen.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tournament/Screens/Editors/TournamentEditorScreen.cs b/osu.Game.Tournament/Screens/Editors/TournamentEditorScreen.cs index c0b56f1e68..7043328aa7 100644 --- a/osu.Game.Tournament/Screens/Editors/TournamentEditorScreen.cs +++ b/osu.Game.Tournament/Screens/Editors/TournamentEditorScreen.cs @@ -70,13 +70,13 @@ namespace osu.Game.Tournament.Screens.Editors RelativeSizeAxes = Axes.Both, Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Padding = new MarginPadding { Bottom = backButton.Height }, Child = flow = new FillFlowContainer { Direction = FillDirection.Vertical, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Spacing = new Vector2(20) + Spacing = new Vector2(20), + Padding = new MarginPadding { Bottom = backButton.Height * 2 }, }, }, backButton, From 864c1a73ae0ac1a1217ea85d8977004d171ff8bf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 17 May 2020 17:27:52 +0900 Subject: [PATCH 0770/1117] Only add back button if required --- .../Screens/Editors/TournamentEditorScreen.cs | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/osu.Game.Tournament/Screens/Editors/TournamentEditorScreen.cs b/osu.Game.Tournament/Screens/Editors/TournamentEditorScreen.cs index 7043328aa7..b92818b84a 100644 --- a/osu.Game.Tournament/Screens/Editors/TournamentEditorScreen.cs +++ b/osu.Game.Tournament/Screens/Editors/TournamentEditorScreen.cs @@ -46,18 +46,6 @@ namespace osu.Game.Tournament.Screens.Editors [BackgroundDependencyLoader] private void load() { - BackButton.Receptor receptor = new BackButton.Receptor(); - backButton = new BackButton(receptor) - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Action = () => - { - if (parentScreen != null) - backAction.Invoke(); - } - }; - AddRangeInternal(new Drawable[] { new Box @@ -76,10 +64,8 @@ namespace osu.Game.Tournament.Screens.Editors RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Spacing = new Vector2(20), - Padding = new MarginPadding { Bottom = backButton.Height * 2 }, }, }, - backButton, ControlPanel = new ControlPanel { Children = new Drawable[] @@ -101,7 +87,21 @@ namespace osu.Game.Tournament.Screens.Editors }); if (parentScreen != null) - backButton.Show(); + { + AddInternal(backButton = new BackButton(new BackButton.Receptor()) + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + State = { Value = Visibility.Visible }, + Action = () => + { + if (parentScreen != null) + backAction.Invoke(); + } + }); + + flow.Padding = new MarginPadding { Bottom = backButton.Height * 2 }; + } Storage.CollectionChanged += (_, args) => { @@ -126,7 +126,7 @@ namespace osu.Game.Tournament.Screens.Editors switch (action) { case GlobalAction.Back: - backAction.Invoke(); + backAction?.Invoke(); return true; } From 13d4997c9165401c5123fd8a995cb9c8d9cb52e8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 17 May 2020 17:35:10 +0900 Subject: [PATCH 0771/1117] Remove custom back action logic (use receptor as intended) --- .../Screens/Editors/TournamentEditorScreen.cs | 30 ++----------------- osu.Game/Graphics/UserInterface/BackButton.cs | 8 +++-- 2 files changed, 8 insertions(+), 30 deletions(-) diff --git a/osu.Game.Tournament/Screens/Editors/TournamentEditorScreen.cs b/osu.Game.Tournament/Screens/Editors/TournamentEditorScreen.cs index b92818b84a..a5a2c5c15f 100644 --- a/osu.Game.Tournament/Screens/Editors/TournamentEditorScreen.cs +++ b/osu.Game.Tournament/Screens/Editors/TournamentEditorScreen.cs @@ -10,8 +10,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics.UserInterface; -using osu.Game.Input.Bindings; -using osu.Framework.Input.Bindings; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Overlays.Settings; @@ -20,7 +18,7 @@ using osuTK; namespace osu.Game.Tournament.Screens.Editors { - public abstract class TournamentEditorScreen : TournamentScreen, IProvideVideo, IKeyBindingHandler + public abstract class TournamentEditorScreen : TournamentScreen, IProvideVideo where TDrawable : Drawable, IModelBacked where TModel : class, new() { @@ -36,8 +34,6 @@ namespace osu.Game.Tournament.Screens.Editors private readonly TournamentScreen parentScreen; private BackButton backButton; - private System.Action backAction => () => sceneManager?.SetScreen(parentScreen.GetType()); - protected TournamentEditorScreen(TournamentScreen parentScreen = null) { this.parentScreen = parentScreen; @@ -88,16 +84,12 @@ namespace osu.Game.Tournament.Screens.Editors if (parentScreen != null) { - AddInternal(backButton = new BackButton(new BackButton.Receptor()) + AddInternal(backButton = new BackButton { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, State = { Value = Visibility.Visible }, - Action = () => - { - if (parentScreen != null) - backAction.Invoke(); - } + Action = () => sceneManager?.SetScreen(parentScreen.GetType()) }); flow.Padding = new MarginPadding { Bottom = backButton.Height * 2 }; @@ -121,22 +113,6 @@ namespace osu.Game.Tournament.Screens.Editors flow.Add(CreateDrawable(model)); } - public bool OnPressed(GlobalAction action) - { - switch (action) - { - case GlobalAction.Back: - backAction?.Invoke(); - return true; - } - - return false; - } - - public void OnReleased(GlobalAction action) - { - } - protected abstract TDrawable CreateDrawable(TModel model); } } diff --git a/osu.Game/Graphics/UserInterface/BackButton.cs b/osu.Game/Graphics/UserInterface/BackButton.cs index 88ba7ede6e..37a8f7b1b4 100644 --- a/osu.Game/Graphics/UserInterface/BackButton.cs +++ b/osu.Game/Graphics/UserInterface/BackButton.cs @@ -16,10 +16,8 @@ namespace osu.Game.Graphics.UserInterface private readonly TwoLayerButton button; - public BackButton(Receptor receptor) + public BackButton(Receptor receptor = null) { - receptor.OnBackPressed = () => button.Click(); - Size = TwoLayerButton.SIZE_EXTENDED; Child = button = new TwoLayerButton @@ -30,6 +28,10 @@ namespace osu.Game.Graphics.UserInterface Icon = OsuIcon.LeftCircle, Action = () => Action?.Invoke() }; + + Add(receptor ??= new Receptor()); + + receptor.OnBackPressed = () => button.Click(); } [BackgroundDependencyLoader] From 76c5be7bc1039611957ac5ba2b781255ae36ee23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 17 May 2020 17:16:22 +0200 Subject: [PATCH 0772/1117] Disallow catch-specific judgements in mania --- .../Scoring/ManiaHitWindows.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaHitWindows.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaHitWindows.cs index 549f0f9214..289f8a00ef 100644 --- a/osu.Game.Rulesets.Mania/Scoring/ManiaHitWindows.cs +++ b/osu.Game.Rulesets.Mania/Scoring/ManiaHitWindows.cs @@ -7,5 +7,20 @@ namespace osu.Game.Rulesets.Mania.Scoring { public class ManiaHitWindows : HitWindows { + public override bool IsHitResultAllowed(HitResult result) + { + switch (result) + { + case HitResult.Perfect: + case HitResult.Great: + case HitResult.Good: + case HitResult.Ok: + case HitResult.Meh: + case HitResult.Miss: + return true; + } + + return false; + } } } From bc6b64b1d7a2ffc5310a69a69a08a8b1a01f0be7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 17 May 2020 21:55:01 +0200 Subject: [PATCH 0773/1117] Add failing test --- .../NonVisual/BarLineGeneratorTest.cs | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 osu.Game.Tests/NonVisual/BarLineGeneratorTest.cs diff --git a/osu.Game.Tests/NonVisual/BarLineGeneratorTest.cs b/osu.Game.Tests/NonVisual/BarLineGeneratorTest.cs new file mode 100644 index 0000000000..e663e1128e --- /dev/null +++ b/osu.Game.Tests/NonVisual/BarLineGeneratorTest.cs @@ -0,0 +1,73 @@ +// 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.Framework.Utils; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Beatmaps.Timing; +using osu.Game.Rulesets.Objects; + +namespace osu.Game.Tests.NonVisual +{ + public class BarLineGeneratorTest + { + [Test] + public void TestRoundingErrorCompensation() + { + // The aim of this test is to make sure bar line generation compensates for floating-point errors. + // The premise of the test is that we have a single timing point that should result in bar lines + // that start at a time point that is a whole number every seventh beat. + + // The fact it's every seventh beat is important - it's a number indivisible by 2, which makes + // it susceptible to rounding inaccuracies. In fact this was originally spotted in cases of maps + // that met exactly this criteria. + + const int beat_length_numerator = 2000; + const int beat_length_denominator = 7; + const TimeSignatures signature = TimeSignatures.SimpleQuadruple; + + var beatmap = new Beatmap + { + HitObjects = new List + { + new HitObject { StartTime = 0 }, + new HitObject { StartTime = 120_000 } + }, + ControlPointInfo = new ControlPointInfo() + }; + + beatmap.ControlPointInfo.Add(0, new TimingControlPoint + { + BeatLength = (double)beat_length_numerator / beat_length_denominator, + TimeSignature = signature + }); + + var barLines = new BarLineGenerator(beatmap).BarLines; + + for (int i = 0; i * beat_length_denominator < barLines.Count; i++) + { + var barLine = barLines[i * beat_length_denominator]; + var expectedTime = beat_length_numerator * (int)signature * i; + + // every seventh bar's start time should be at least greater than the whole number we expect. + // It cannot be less, as that can affect overlapping scroll algorithms + // (the previous timing point might be chosen incorrectly if this is not the case) + Assert.GreaterOrEqual(barLine.StartTime, expectedTime); + + // on the other side, make sure we don't stray too far from the expected time either. + Assert.IsTrue(Precision.AlmostEquals(barLine.StartTime, expectedTime)); + + // check major/minor lines for good measure too + Assert.AreEqual(i % (int)signature == 0, barLine.Major); + } + } + + private class BarLine : IBarLine + { + public double StartTime { get; set; } + public bool Major { get; set; } + } + } +} From 17ae392a759771801a8a3d325c4e630f4c60f770 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 17 May 2020 22:08:49 +0200 Subject: [PATCH 0774/1117] Apply rounding to bar line start times --- osu.Game/Rulesets/Objects/BarLineGenerator.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game/Rulesets/Objects/BarLineGenerator.cs b/osu.Game/Rulesets/Objects/BarLineGenerator.cs index 5588e9c0b7..9556b52735 100644 --- a/osu.Game/Rulesets/Objects/BarLineGenerator.cs +++ b/osu.Game/Rulesets/Objects/BarLineGenerator.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.Utils; @@ -46,6 +47,16 @@ namespace osu.Game.Rulesets.Objects for (double t = currentTimingPoint.Time; Precision.DefinitelyBigger(endTime, t); t += barLength, currentBeat++) { + var roundedTime = Math.Round(t, MidpointRounding.AwayFromZero); + + // in the case of some bar lengths, rounding errors can cause t to be slightly less than + // the expected whole number value due to floating point inaccuracies. + // if this is the case, apply rounding. + if (Precision.AlmostEquals(t, roundedTime)) + { + t = roundedTime; + } + BarLines.Add(new TBarLine { StartTime = t, From 59ef6002bd9e3b32adbd00761456687c36ff312c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 May 2020 13:12:30 +0900 Subject: [PATCH 0775/1117] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 650ebde54d..eaad4daf35 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index ee6206e166..9112dfe46e 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -24,7 +24,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index cbf8600c62..3f0630af5f 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -80,7 +80,7 @@ - + From 1865cd0762f6e99bfabba22a76ca2165e0b2c08d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 18 May 2020 15:10:59 +0900 Subject: [PATCH 0776/1117] Fix possible exceptions in performance calculators --- .../Difficulty/CatchPerformanceCalculator.cs | 12 ++++++------ .../Difficulty/ManiaPerformanceCalculator.cs | 13 +++++++------ .../Difficulty/OsuPerformanceCalculator.cs | 9 +++++---- .../Difficulty/TaikoPerformanceCalculator.cs | 9 +++++---- 4 files changed, 23 insertions(+), 20 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs index e7ce680365..2dc28fad35 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs @@ -4,12 +4,12 @@ using System; using System.Collections.Generic; using System.Linq; +using osu.Framework.Extensions; using osu.Game.Beatmaps; using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; -using osu.Game.Scoring.Legacy; namespace osu.Game.Rulesets.Catch.Difficulty { @@ -34,11 +34,11 @@ namespace osu.Game.Rulesets.Catch.Difficulty { mods = Score.Mods; - fruitsHit = Score?.GetCount300() ?? Score.Statistics[HitResult.Perfect]; - ticksHit = Score?.GetCount100() ?? 0; - tinyTicksHit = Score?.GetCount50() ?? 0; - tinyTicksMissed = Score?.GetCountKatu() ?? 0; - misses = Score.Statistics[HitResult.Miss]; + fruitsHit = Score.Statistics.GetOrDefault(HitResult.Perfect); + ticksHit = Score.Statistics.GetOrDefault(HitResult.LargeTickHit); + tinyTicksHit = Score.Statistics.GetOrDefault(HitResult.SmallTickHit); + tinyTicksMissed = Score.Statistics.GetOrDefault(HitResult.SmallTickMiss); + misses = Score.Statistics.GetOrDefault(HitResult.Miss); // Don't count scores made with supposedly unranked mods if (mods.Any(m => !m.Ranked)) diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs index 3f7a2baedd..91383c5548 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using osu.Framework.Extensions; using osu.Game.Beatmaps; using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Mods; @@ -37,12 +38,12 @@ namespace osu.Game.Rulesets.Mania.Difficulty { mods = Score.Mods; scaledScore = Score.TotalScore; - countPerfect = Score.Statistics[HitResult.Perfect]; - countGreat = Score.Statistics[HitResult.Great]; - countGood = Score.Statistics[HitResult.Good]; - countOk = Score.Statistics[HitResult.Ok]; - countMeh = Score.Statistics[HitResult.Meh]; - countMiss = Score.Statistics[HitResult.Miss]; + countPerfect = Score.Statistics.GetOrDefault(HitResult.Perfect); + countGreat = Score.Statistics.GetOrDefault(HitResult.Great); + countGood = Score.Statistics.GetOrDefault(HitResult.Good); + countOk = Score.Statistics.GetOrDefault(HitResult.Ok); + countMeh = Score.Statistics.GetOrDefault(HitResult.Meh); + countMiss = Score.Statistics.GetOrDefault(HitResult.Miss); if (mods.Any(m => !m.Ranked)) return 0; diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index ce8ecf02ac..4022b554f4 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using osu.Framework.Extensions; using osu.Game.Beatmaps; using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Mods; @@ -45,10 +46,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty mods = Score.Mods; accuracy = Score.Accuracy; scoreMaxCombo = Score.MaxCombo; - countGreat = Score.Statistics[HitResult.Great]; - countGood = Score.Statistics[HitResult.Good]; - countMeh = Score.Statistics[HitResult.Meh]; - countMiss = Score.Statistics[HitResult.Miss]; + countGreat = Score.Statistics.GetOrDefault(HitResult.Great); + countGood = Score.Statistics.GetOrDefault(HitResult.Good); + countMeh = Score.Statistics.GetOrDefault(HitResult.Meh); + countMiss = Score.Statistics.GetOrDefault(HitResult.Miss); // Don't count scores made with supposedly unranked mods if (mods.Any(m => !m.Ranked)) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs index 3a0fb64622..bc147b53ac 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using osu.Framework.Extensions; using osu.Game.Beatmaps; using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Mods; @@ -31,10 +32,10 @@ namespace osu.Game.Rulesets.Taiko.Difficulty public override double Calculate(Dictionary categoryDifficulty = null) { mods = Score.Mods; - countGreat = Score.Statistics[HitResult.Great]; - countGood = Score.Statistics[HitResult.Good]; - countMeh = Score.Statistics[HitResult.Meh]; - countMiss = Score.Statistics[HitResult.Miss]; + countGreat = Score.Statistics.GetOrDefault(HitResult.Great); + countGood = Score.Statistics.GetOrDefault(HitResult.Good); + countMeh = Score.Statistics.GetOrDefault(HitResult.Meh); + countMiss = Score.Statistics.GetOrDefault(HitResult.Miss); // Don't count scores made with supposedly unranked mods if (mods.Any(m => !m.Ranked)) From d9bb90078b5f0b9d4c32635417f818fe1c562073 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 May 2020 17:47:47 +0900 Subject: [PATCH 0777/1117] Move grids to inside columns --- .../Edit/ManiaBeatSnapGrid.cs | 48 +++++++++---------- osu.Game.Rulesets.Mania/UI/Column.cs | 2 + .../UI/Components/ColumnHitObjectArea.cs | 8 ++++ 3 files changed, 32 insertions(+), 26 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs index 31ebb7bc1c..9a998366e9 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs @@ -7,12 +7,10 @@ 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; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; -using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI.Scrolling; @@ -22,7 +20,7 @@ using osuTK.Graphics; namespace osu.Game.Rulesets.Mania.Edit { - public class ManiaBeatSnapGrid : CompositeDrawable + public class ManiaBeatSnapGrid : Component { /// /// The brightness of bar lines one beat around the time range from . @@ -54,15 +52,32 @@ namespace osu.Game.Rulesets.Mania.Edit { foreach (var stage in composer.Playfield.Stages) { - var grid = new Grid(stage); - grids.Add(grid); + foreach (var column in stage.Columns) + { + var grid = new Grid(); - AddInternal(grid); + grids.Add(grid); + column.UnderlayElements.Add(grid); + } } beatDivisor.BindValueChanged(_ => createLines(), true); } + public override void Hide() + { + base.Hide(); + foreach (var grid in grids) + grid.Hide(); + } + + public override void Show() + { + base.Show(); + foreach (var grid in grids) + grid.Show(); + } + private void createLines() { foreach (var grid in grids) @@ -145,7 +160,7 @@ namespace osu.Game.Rulesets.Mania.Edit linesDuring.Clear(); linesAfter.Clear(); - foreach (var line in grid.AliveObjects.OfType()) + foreach (var line in grid.Objects.OfType()) { if (line.HitObject.StartTime < minTime) linesBefore.Add(line); @@ -202,30 +217,11 @@ namespace osu.Game.Rulesets.Mania.Edit [Resolved] private IManiaHitObjectComposer composer { get; set; } - private readonly Stage stage; - - public Grid(Stage stage) - { - this.stage = stage; - - RelativeSizeAxes = Axes.None; - } - protected override void LoadComplete() { base.LoadComplete(); - Clock = composer.Playfield.Clock; } - - protected override void Update() - { - base.Update(); - - var parentQuad = Parent.ToLocalSpace(stage.HitObjectContainer.ScreenSpaceDrawQuad); - Position = parentQuad.TopLeft; - Size = parentQuad.Size; - } } private class DrawableGridLine : DrawableHitObject diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index 506a07f26b..511d6c8623 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -37,6 +37,8 @@ namespace osu.Game.Rulesets.Mania.UI internal readonly Container TopLevelContainer; + public Container UnderlayElements => hitObjectArea.UnderlayElements; + public Column(int index) { Index = index; diff --git a/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs b/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs index cb79bf7f43..b365ae45a9 100644 --- a/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs +++ b/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs @@ -12,6 +12,9 @@ namespace osu.Game.Rulesets.Mania.UI.Components public class ColumnHitObjectArea : HitObjectArea { public readonly Container Explosions; + + public readonly Container UnderlayElements; + private readonly Drawable hitTarget; public ColumnHitObjectArea(int columnIndex, HitObjectContainer hitObjectContainer) @@ -19,6 +22,11 @@ namespace osu.Game.Rulesets.Mania.UI.Components { AddRangeInternal(new[] { + UnderlayElements = new Container + { + RelativeSizeAxes = Axes.Both, + Depth = 2, + }, hitTarget = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HitTarget, columnIndex), _ => new DefaultHitTarget()) { RelativeSizeAxes = Axes.X, From 16e85ae0b1c0399b2a3dce77df4758b3c51f6079 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 May 2020 17:52:04 +0900 Subject: [PATCH 0778/1117] Remove Grid class --- .../Edit/ManiaBeatSnapGrid.cs | 20 ++++--------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs index 9a998366e9..5b13b1421c 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs @@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Mania.Edit [Resolved] private BindableBeatDivisor beatDivisor { get; set; } - private readonly List grids = new List(); + private readonly List grids = new List(); [BackgroundDependencyLoader] private void load() @@ -54,10 +54,10 @@ namespace osu.Game.Rulesets.Mania.Edit { foreach (var column in stage.Columns) { - var grid = new Grid(); + var lineContainer = new ScrollingHitObjectContainer(); - grids.Add(grid); - column.UnderlayElements.Add(grid); + grids.Add(lineContainer); + column.UnderlayElements.Add(lineContainer); } } @@ -212,18 +212,6 @@ namespace osu.Game.Rulesets.Mania.Edit } } - private class Grid : ScrollingHitObjectContainer - { - [Resolved] - private IManiaHitObjectComposer composer { get; set; } - - protected override void LoadComplete() - { - base.LoadComplete(); - Clock = composer.Playfield.Clock; - } - } - private class DrawableGridLine : DrawableHitObject { [Resolved] From b43e9781566e45723dfe33ebc24fc8083edd0f96 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Mon, 18 May 2020 17:44:56 +0800 Subject: [PATCH 0779/1117] Unify to use double in CatchPerformanceCalculator. --- .../Difficulty/CatchPerformanceCalculator.cs | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs index 2dc28fad35..2ee7cea645 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs @@ -52,8 +52,8 @@ namespace osu.Game.Rulesets.Catch.Difficulty // Longer maps are worth more double lengthBonus = - 0.95f + 0.3f * Math.Min(1.0f, numTotalHits / 2500.0f) + - (numTotalHits > 2500 ? (float)Math.Log10(numTotalHits / 2500.0f) * 0.475f : 0.0f); + 0.95 + 0.3 * Math.Min(1.0, numTotalHits / 2500.0) + + (numTotalHits > 2500 ? Math.Log10(numTotalHits / 2500.0) * 0.475 : 0.0); // Longer maps are worth more value *= lengthBonus; @@ -65,14 +65,14 @@ namespace osu.Game.Rulesets.Catch.Difficulty if (Attributes.MaxCombo > 0) value *= Math.Min(Math.Pow(Score.MaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0); - float approachRate = (float)Attributes.ApproachRate; - float approachRateFactor = 1.0f; - if (approachRate > 9.0f) - approachRateFactor += 0.1f * (approachRate - 9.0f); // 10% for each AR above 9 - if (approachRate > 10.0f) - approachRateFactor += 0.1f * (approachRate - 10.0f); // Additional 10% at AR 11, 30% total - else if (approachRate < 8.0f) - approachRateFactor += 0.025f * (8.0f - approachRate); // 2.5% for each AR below 8 + double approachRate = Attributes.ApproachRate; + double approachRateFactor = 1.0; + if (approachRate > 9.0) + approachRateFactor += 0.1 * (approachRate - 9.0); // 10% for each AR above 9 + if (approachRate > 10.0) + approachRateFactor += 0.1 * (approachRate - 10.0); // Additional 10% at AR 11, 30% total + else if (approachRate < 8.0) + approachRateFactor += 0.025 * (8.0 - approachRate); // 2.5% for each AR below 8 value *= approachRateFactor; @@ -80,10 +80,10 @@ namespace osu.Game.Rulesets.Catch.Difficulty { value *= 1.05 + 0.075 * (10.0 - Math.Min(10.0, Attributes.ApproachRate)); // 7.5% for each AR below 10 // Hiddens gives almost nothing on max approach rate, and more the lower it is - if (approachRate <= 10.0f) - value *= 1.05f + 0.075f * (10.0f - approachRate); // 7.5% for each AR below 10 - else if (approachRate > 10.0f) - value *= 1.01f + 0.04f * (11.0f - Math.Min(11.0f, approachRate)); // 5% at AR 10, 1% at AR 11 + if (approachRate <= 10.0) + value *= 1.05 + 0.075 * (10.0 - approachRate); // 7.5% for each AR below 10 + else if (approachRate > 10.0) + value *= 1.01 + 0.04 * (11.0 - Math.Min(11.0, approachRate)); // 5% at AR 10, 1% at AR 11 } if (mods.Any(m => m is ModFlashlight)) @@ -100,7 +100,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty return value; } - private float accuracy() => totalHits() == 0 ? 0 : Math.Clamp((float)totalSuccessfulHits() / totalHits(), 0, 1); + private double accuracy() => totalHits() == 0 ? 0 : Math.Clamp((double)totalSuccessfulHits() / totalHits(), 0, 1); private int totalHits() => tinyTicksHit + ticksHit + fruitsHit + misses + tinyTicksMissed; private int totalSuccessfulHits() => tinyTicksHit + ticksHit + fruitsHit; private int totalComboHits() => misses + ticksHit + fruitsHit; From 373aae06109b845c078dcaaacab8deb5406094b3 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Mon, 18 May 2020 17:45:32 +0800 Subject: [PATCH 0780/1117] Use int for total hits in OsuPerformanceCalculator. --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 4022b554f4..c8c6db06d7 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -204,7 +204,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty return accuracyValue; } - private double totalHits => countGreat + countGood + countMeh + countMiss; - private double totalSuccessfulHits => countGreat + countGood + countMeh; + private int totalHits => countGreat + countGood + countMeh + countMiss; + private int totalSuccessfulHits => countGreat + countGood + countMeh; } } From c20902f249883ab6a16ca95d3096463767cc015a Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Mon, 18 May 2020 18:22:03 +0800 Subject: [PATCH 0781/1117] Fix double in accuracy calculation in OsuPerformanceCalculator. --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index c8c6db06d7..6f4c0f9cfa 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -181,7 +181,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty int amountHitObjectsWithAccuracy = countHitCircles; if (amountHitObjectsWithAccuracy > 0) - betterAccuracyPercentage = ((countGreat - (totalHits - amountHitObjectsWithAccuracy)) * 6 + countGood * 2 + countMeh) / (amountHitObjectsWithAccuracy * 6); + betterAccuracyPercentage = ((countGreat - (totalHits - amountHitObjectsWithAccuracy)) * 6 + countGood * 2 + countMeh) / (double)(amountHitObjectsWithAccuracy * 6); else betterAccuracyPercentage = 0; From 49ee05c3c4dbd8b8d9d6f673fb887665b384b592 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 18 May 2020 19:37:49 +0900 Subject: [PATCH 0782/1117] Make into CompositeDrawable --- osu.Game/Screens/Play/SkipOverlay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/SkipOverlay.cs b/osu.Game/Screens/Play/SkipOverlay.cs index fec35df4e3..b123757ded 100644 --- a/osu.Game/Screens/Play/SkipOverlay.cs +++ b/osu.Game/Screens/Play/SkipOverlay.cs @@ -24,7 +24,7 @@ using osu.Game.Input.Bindings; namespace osu.Game.Screens.Play { - public class SkipOverlay : Container, IKeyBindingHandler + public class SkipOverlay : CompositeDrawable, IKeyBindingHandler { private readonly double startTime; @@ -62,7 +62,7 @@ namespace osu.Game.Screens.Play [BackgroundDependencyLoader(true)] private void load(OsuColour colours) { - Child = buttonContainer = new ButtonContainer + InternalChild = buttonContainer = new ButtonContainer { RelativeSizeAxes = Axes.Both, Child = fadeContainer = new FadeContainer From b35b150f3883609e62e31e8bb6216e82b888cc6c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 May 2020 19:32:14 +0900 Subject: [PATCH 0783/1117] Simplify colouring logic --- .../Edit/ManiaBeatSnapGrid.cs | 66 +++---------------- 1 file changed, 9 insertions(+), 57 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs index 5b13b1421c..e771a9753f 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs @@ -22,10 +22,7 @@ namespace osu.Game.Rulesets.Mania.Edit { public class ManiaBeatSnapGrid : Component { - /// - /// The brightness of bar lines one beat around the time range from . - /// - private const float first_beat_brightness = 0.5f; + private const double visible_range = 1500; [Resolved] private IManiaHitObjectComposer composer { get; set; } @@ -150,64 +147,19 @@ namespace osu.Game.Rulesets.Mania.Edit private void setRange(double minTime, double maxTime) { - var linesBefore = new List(); - var linesDuring = new List(); - var linesAfter = new List(); - foreach (var grid in grids) { - linesBefore.Clear(); - linesDuring.Clear(); - linesAfter.Clear(); - foreach (var line in grid.Objects.OfType()) { - if (line.HitObject.StartTime < minTime) - linesBefore.Add(line); - else if (line.HitObject.StartTime <= maxTime) - linesDuring.Add(line); + double lineTime = line.HitObject.StartTime; + + if (lineTime >= minTime && lineTime <= maxTime) + line.Colour = Color4.White; else - linesAfter.Add(line); - } - - // Snapping will always happen on one of the two lines around minTime (the "target" line). - // One of those lines may exist in linesBefore and the other may exist in linesAfter, depending on whether such a line exists, and the target changes when the mid-point is crossed. - // For display purposes, one complete beat is shown at the maximum brightness such that the target line should always be bright. - bool targetLineIsLastLineBefore = false; - - if (linesBefore.Count > 0 && linesAfter.Count > 0) - targetLineIsLastLineBefore = Math.Abs(linesBefore[^1].HitObject.StartTime - minTime) <= Math.Abs(linesAfter[0].HitObject.StartTime - minTime); - else if (linesBefore.Count > 0) - targetLineIsLastLineBefore = true; - - if (targetLineIsLastLineBefore) - { - // Move the last line before to linesDuring - linesDuring.Insert(0, linesBefore[^1]); - linesBefore.RemoveAt(linesBefore.Count - 1); - } - else if (linesAfter.Count > 0) // = false does not guarantee that a line after exists (maybe at the bottom of the screen) - { - // Move the first line after to linesDuring - linesDuring.Insert(0, linesAfter[0]); - linesAfter.RemoveAt(0); - } - - // Grays are used rather than transparency since the lines appear on a coloured mania playfield. - - foreach (var l in linesDuring) - l.Colour = OsuColour.Gray(first_beat_brightness); - - for (int i = 0; i < linesBefore.Count; i++) - { - int offset = (linesBefore.Count - i - 1) / beatDivisor.Value; - linesBefore[i].Colour = OsuColour.Gray(first_beat_brightness / (offset + 1)); - } - - for (int i = 0; i < linesAfter.Count; i++) - { - int offset = i / beatDivisor.Value; - linesAfter[i].Colour = OsuColour.Gray(first_beat_brightness / (offset + 1)); + { + double timeSeparation = lineTime < minTime ? minTime - lineTime : lineTime - maxTime; + line.Colour = OsuColour.Gray((float)Math.Max(0, 1 - timeSeparation / visible_range)); + } } } } From 2fd25f5ee6776fd9c17f3ae25a1e00a6d435ba42 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 18 May 2020 19:54:26 +0900 Subject: [PATCH 0784/1117] Fix tests --- osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs index e093542d1e..12ada088a1 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs @@ -105,7 +105,7 @@ namespace osu.Game.Tests.Visual.Gameplay { AddStep("move mouse", () => InputManager.MoveMouseTo(skip.ScreenSpaceDrawQuad.Centre)); AddStep("button down", () => InputManager.PressButton(MouseButton.Left)); - AddUntilStep("wait for overlay disappear", () => !skip.Child.IsPresent); + AddUntilStep("wait for overlay disappear", () => !skip.OverlayContents.IsPresent); AddAssert("ensure button didn't disappear", () => skip.OverlayContents.Alpha > 0); AddStep("button up", () => InputManager.ReleaseButton(MouseButton.Left)); checkRequestCount(0); @@ -121,7 +121,7 @@ namespace osu.Game.Tests.Visual.Gameplay { } - public Drawable OverlayContents => (Child as Container)?.Child; + public Drawable OverlayContents => (InternalChild as Container)?.Child; } } } From f98ee2718552e1663318e7466d70cc51e983264d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 18 May 2020 20:01:00 +0900 Subject: [PATCH 0785/1117] Fix referencing wrong child --- .../Visual/Gameplay/TestSceneSkipOverlay.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs index 12ada088a1..7ed7a116b4 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs @@ -56,19 +56,19 @@ namespace osu.Game.Tests.Visual.Gameplay public void TestFadeOnIdle() { AddStep("move mouse", () => InputManager.MoveMouseTo(Vector2.Zero)); - AddUntilStep("fully visible", () => skip.OverlayContents.Alpha == 1); - AddUntilStep("wait for fade", () => skip.OverlayContents.Alpha < 1); + AddUntilStep("fully visible", () => skip.FadingContent.Alpha == 1); + AddUntilStep("wait for fade", () => skip.FadingContent.Alpha < 1); AddStep("move mouse", () => InputManager.MoveMouseTo(skip.ScreenSpaceDrawQuad.Centre)); - AddUntilStep("fully visible", () => skip.OverlayContents.Alpha == 1); - AddUntilStep("wait for fade", () => skip.OverlayContents.Alpha < 1); + AddUntilStep("fully visible", () => skip.FadingContent.Alpha == 1); + AddUntilStep("wait for fade", () => skip.FadingContent.Alpha < 1); } [Test] public void TestClickableAfterFade() { AddStep("move mouse", () => InputManager.MoveMouseTo(skip.ScreenSpaceDrawQuad.Centre)); - AddUntilStep("wait for fade", () => skip.OverlayContents.Alpha == 0); + AddUntilStep("wait for fade", () => skip.FadingContent.Alpha == 0); AddStep("click", () => InputManager.Click(MouseButton.Left)); checkRequestCount(1); } @@ -105,8 +105,8 @@ namespace osu.Game.Tests.Visual.Gameplay { AddStep("move mouse", () => InputManager.MoveMouseTo(skip.ScreenSpaceDrawQuad.Centre)); AddStep("button down", () => InputManager.PressButton(MouseButton.Left)); - AddUntilStep("wait for overlay disappear", () => !skip.OverlayContents.IsPresent); - AddAssert("ensure button didn't disappear", () => skip.OverlayContents.Alpha > 0); + AddUntilStep("wait for overlay disappear", () => !skip.OverlayContent.IsPresent); + AddAssert("ensure button didn't disappear", () => skip.FadingContent.Alpha > 0); AddStep("button up", () => InputManager.ReleaseButton(MouseButton.Left)); checkRequestCount(0); } @@ -121,7 +121,9 @@ namespace osu.Game.Tests.Visual.Gameplay { } - public Drawable OverlayContents => (InternalChild as Container)?.Child; + public Drawable OverlayContent => InternalChild; + + public Drawable FadingContent => (OverlayContent as Container)?.Child; } } } From 406f39e8bfc281fc91c3e96ce12077423974006f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 May 2020 21:27:26 +0900 Subject: [PATCH 0786/1117] Construct online visible lines --- .../TestSceneManiaBeatSnapGrid.cs | 2 +- .../Edit/ManiaBeatSnapGrid.cs | 135 ++++++++++-------- .../Edit/ManiaHitObjectComposer.cs | 9 +- 3 files changed, 81 insertions(+), 65 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaBeatSnapGrid.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaBeatSnapGrid.cs index 84419313e6..941cf4e7c8 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaBeatSnapGrid.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaBeatSnapGrid.cs @@ -61,7 +61,7 @@ namespace osu.Game.Rulesets.Mania.Tests float relativePosition = Playfield.Stages[0].HitObjectContainer.ToLocalSpace(e.ScreenSpaceMousePosition).Y / Playfield.Stages[0].HitObjectContainer.DrawHeight; double timeValue = scrollingInfo.TimeRange.Value * relativePosition; - beatSnapGrid.SetRange(timeValue, timeValue); + beatSnapGrid.SelectionTimeRange = (timeValue, timeValue); return true; } diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs index e771a9753f..a16fb52f01 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.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.Graphics; using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps; @@ -22,7 +23,7 @@ namespace osu.Game.Rulesets.Mania.Edit { public class ManiaBeatSnapGrid : Component { - private const double visible_range = 1500; + private const double visible_range = 750; [Resolved] private IManiaHitObjectComposer composer { get; set; } @@ -44,6 +45,34 @@ namespace osu.Game.Rulesets.Mania.Edit private readonly List grids = new List(); + private readonly Cached lineCache = new Cached(); + + private (double start, double end)? selectionTimeRange; + + public (double start, double end)? SelectionTimeRange + { + get => selectionTimeRange; + set + { + if (value == selectionTimeRange) + return; + + selectionTimeRange = value; + lineCache.Invalidate(); + } + } + + protected override void Update() + { + base.Update(); + + if (!lineCache.IsValid) + { + lineCache.Validate(); + createLines(); + } + } + [BackgroundDependencyLoader] private void load() { @@ -61,49 +90,65 @@ namespace osu.Game.Rulesets.Mania.Edit beatDivisor.BindValueChanged(_ => createLines(), true); } - public override void Hide() - { - base.Hide(); - foreach (var grid in grids) - grid.Hide(); - } - - public override void Show() - { - base.Show(); - foreach (var grid in grids) - grid.Show(); - } - private void createLines() { foreach (var grid in grids) grid.Clear(); - for (int i = 0; i < beatmap.ControlPointInfo.TimingPoints.Count; i++) + if (selectionTimeRange == null) + return; + + var range = selectionTimeRange.Value; + + var timingPoint = beatmap.ControlPointInfo.TimingPointAt(range.start - visible_range); + + double time = timingPoint.Time; + int beat = 0; + + // progress time until in the visible range. + while (time < range.start - visible_range) { - var point = beatmap.ControlPointInfo.TimingPoints[i]; - var until = i + 1 < beatmap.ControlPointInfo.TimingPoints.Count ? beatmap.ControlPointInfo.TimingPoints[i + 1].Time : working.Value.Track.Length; + time += timingPoint.BeatLength / beatDivisor.Value; + beat++; + } - int beat = 0; + while (time < range.end + visible_range) + { + var nextTimingPoint = beatmap.ControlPointInfo.TimingPointAt(time); - for (double t = point.Time; t < until; t += point.BeatLength / beatDivisor.Value) + // switch to the next timing point if we have reached it. + if (nextTimingPoint != timingPoint) { - var indexInBeat = beat % beatDivisor.Value; - Color4 colour; + beat = 0; + timingPoint = nextTimingPoint; + } - if (indexInBeat == 0) - colour = BindableBeatDivisor.GetColourFor(1, colours); + Color4 colour = BindableBeatDivisor.GetColourFor( + BindableBeatDivisor.GetDivisorForBeatIndex(Math.Max(1, beat), beatDivisor.Value), colours); + + foreach (var grid in grids) + grid.Add(new DrawableGridLine(time, colour)); + + beat++; + time += timingPoint.BeatLength / beatDivisor.Value; + } + + foreach (var grid in grids) + { + // required to update ScrollingHitObjectContainer's cache. + grid.UpdateSubTree(); + + foreach (var line in grid.Objects.OfType()) + { + time = line.HitObject.StartTime; + + if (time >= range.start && time <= range.end) + line.Alpha = 1; else { - var divisor = BindableBeatDivisor.GetDivisorForBeatIndex(beat, beatDivisor.Value); - colour = BindableBeatDivisor.GetColourFor(divisor, colours); + double timeSeparation = time < range.start ? range.start - time : time - range.end; + line.Alpha = (float)Math.Max(0, 1 - timeSeparation / visible_range); } - - foreach (var grid in grids) - grid.Add(new DrawableGridLine(t, colour)); - - beat++; } } } @@ -112,6 +157,7 @@ namespace osu.Game.Rulesets.Mania.Edit { float minDist = float.PositiveInfinity; DrawableGridLine minDistLine = null; + Vector2 minDistLinePosition = Vector2.Zero; foreach (var grid in grids) @@ -137,33 +183,6 @@ namespace osu.Game.Rulesets.Mania.Edit return (new Vector2(position.X, minDistLinePosition.Y + noteOffset), minDistLine.HitObject.StartTime); } - public void SetRange(double minTime, double maxTime) - { - if (LoadState >= LoadState.Ready) - setRange(minTime, maxTime); - else - Schedule(() => setRange(minTime, maxTime)); - } - - private void setRange(double minTime, double maxTime) - { - foreach (var grid in grids) - { - foreach (var line in grid.Objects.OfType()) - { - double lineTime = line.HitObject.StartTime; - - if (lineTime >= minTime && lineTime <= maxTime) - line.Colour = Color4.White; - else - { - double timeSeparation = lineTime < minTime ? minTime - lineTime : lineTime - maxTime; - line.Colour = OsuColour.Gray((float)Math.Max(0, 1 - timeSeparation / visible_range)); - } - } - } - } - private class DrawableGridLine : DrawableHitObject { [Resolved] diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index 1266761d12..475320ece3 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -68,18 +68,15 @@ namespace osu.Game.Rulesets.Mania.Edit { if (EditorBeatmap.SelectedHitObjects.Any()) { - beatSnapGrid.SetRange(EditorBeatmap.SelectedHitObjects.Min(h => h.StartTime), EditorBeatmap.SelectedHitObjects.Max(h => h.GetEndTime())); - beatSnapGrid.Show(); + beatSnapGrid.SelectionTimeRange = (EditorBeatmap.SelectedHitObjects.Min(h => h.StartTime), EditorBeatmap.SelectedHitObjects.Max(h => h.GetEndTime())); } else - beatSnapGrid.Hide(); + beatSnapGrid.SelectionTimeRange = null; } else { var placementTime = GetSnappedPosition(ToLocalSpace(inputManager.CurrentState.Mouse.Position), 0).time; - beatSnapGrid.SetRange(placementTime, placementTime); - - beatSnapGrid.Show(); + beatSnapGrid.SelectionTimeRange = (placementTime, placementTime); } } From 013683c23ba1d02bf348ae6288d56b253adc9385 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 May 2020 00:17:13 +0900 Subject: [PATCH 0787/1117] Fix taiko rim markers incorrectly playing as whistle samples --- osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index d332f90cd4..1e1f9ae09b 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -52,7 +52,12 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables protected override IEnumerable GetSamples() { // normal and claps are always handled by the drum (see DrumSampleMapping). - var samples = HitObject.Samples.Where(s => s.Name != HitSampleInfo.HIT_NORMAL && s.Name != HitSampleInfo.HIT_CLAP); + // in addition, whistles are excluded as they are an alternative rim marker. + + var samples = HitObject.Samples.Where(s => + s.Name != HitSampleInfo.HIT_NORMAL + && s.Name != HitSampleInfo.HIT_CLAP + && s.Name != HitSampleInfo.HIT_WHISTLE); if (HitObject.Type == HitType.Rim && HitObject.IsStrong) { From 9415e45aea5afb899edc21bd866accb0923c82eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 18 May 2020 20:11:19 +0200 Subject: [PATCH 0788/1117] Add overlay layer to enumeration type --- osu.Game/Beatmaps/Legacy/LegacyStoryLayer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Legacy/LegacyStoryLayer.cs b/osu.Game/Beatmaps/Legacy/LegacyStoryLayer.cs index 48e8bdbb76..ea23c49c4a 100644 --- a/osu.Game/Beatmaps/Legacy/LegacyStoryLayer.cs +++ b/osu.Game/Beatmaps/Legacy/LegacyStoryLayer.cs @@ -9,6 +9,7 @@ namespace osu.Game.Beatmaps.Legacy Fail = 1, Pass = 2, Foreground = 3, - Video = 4 + Overlay = 4, + Video = 5 } } From e9710b6f836578850646db5e78e06f8985066a35 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 May 2020 09:43:05 +0900 Subject: [PATCH 0789/1117] Add taiko type conversion test coverage --- .../TaikoBeatmapConversionTest.cs | 3 +- ...-type-conversions-expected-conversion.json | 116 ++++++++++++++++++ .../Beatmaps/sample-to-type-conversions.osu | 62 ++++++++++ 3 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/sample-to-type-conversions-expected-conversion.json create mode 100644 osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/sample-to-type-conversions.osu diff --git a/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs index 8c26ca70ac..f7729138ff 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs @@ -19,6 +19,7 @@ namespace osu.Game.Rulesets.Taiko.Tests [NonParallelizable] [TestCase("basic")] [TestCase("slider-generating-drumroll")] + [TestCase("sample-to-type-conversions")] public void Test(string name) => base.Test(name); protected override IEnumerable CreateConvertValue(HitObject hitObject) @@ -41,7 +42,7 @@ namespace osu.Game.Rulesets.Taiko.Tests public struct ConvertValue : IEquatable { /// - /// A sane value to account for osu!stable using ints everwhere. + /// A sane value to account for osu!stable using ints everywhere. /// private const float conversion_lenience = 2; diff --git a/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/sample-to-type-conversions-expected-conversion.json b/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/sample-to-type-conversions-expected-conversion.json new file mode 100644 index 0000000000..47ca6aef68 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/sample-to-type-conversions-expected-conversion.json @@ -0,0 +1,116 @@ +{ + "Mappings": [ + { + "StartTime": 110.0, + "Objects": [ + { + "StartTime": 110.0, + "EndTime": 110.0, + "IsRim": false, + "IsCentre": true, + "IsDrumRoll": false, + "IsSwell": false, + "IsStrong": false + } + ] + }, + { + "StartTime": 538.0, + "Objects": [ + { + "StartTime": 538.0, + "EndTime": 538.0, + "IsRim": true, + "IsCentre": false, + "IsDrumRoll": false, + "IsSwell": false, + "IsStrong": false + } + ] + }, + { + "StartTime": 967.0, + "Objects": [ + { + "StartTime": 967.0, + "EndTime": 967.0, + "IsRim": true, + "IsCentre": false, + "IsDrumRoll": false, + "IsSwell": false, + "IsStrong": false + } + ] + }, + { + "StartTime": 1395.0, + "Objects": [ + { + "StartTime": 1395.0, + "EndTime": 1395.0, + "IsRim": true, + "IsCentre": false, + "IsDrumRoll": false, + "IsSwell": false, + "IsStrong": false + } + ] + }, + { + "StartTime": 1824.0, + "Objects": [ + { + "StartTime": 1824.0, + "EndTime": 1824.0, + "IsRim": false, + "IsCentre": true, + "IsDrumRoll": false, + "IsSwell": false, + "IsStrong": true + } + ] + }, + { + "StartTime": 2252.0, + "Objects": [ + { + "StartTime": 2252.0, + "EndTime": 2252.0, + "IsRim": true, + "IsCentre": false, + "IsDrumRoll": false, + "IsSwell": false, + "IsStrong": true + } + ] + }, + { + "StartTime": 2681.0, + "Objects": [ + { + "StartTime": 2681.0, + "EndTime": 2681.0, + "IsRim": true, + "IsCentre": false, + "IsDrumRoll": false, + "IsSwell": false, + "IsStrong": true + } + ] + }, + { + "StartTime": 3110.0, + "Objects": [ + { + "StartTime": 3110.0, + "EndTime": 3110.0, + "IsRim": true, + "IsCentre": false, + "IsDrumRoll": false, + "IsSwell": false, + "IsStrong": true + } + ] + } + ] +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/sample-to-type-conversions.osu b/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/sample-to-type-conversions.osu new file mode 100644 index 0000000000..a3537e7149 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/sample-to-type-conversions.osu @@ -0,0 +1,62 @@ +osu file format v14 + +[General] +AudioFilename: audio.mp3 +AudioLeadIn: 0 +PreviewTime: -1 +Countdown: 0 +SampleSet: Normal +StackLeniency: 0.5 +Mode: 1 +LetterboxInBreaks: 0 +WidescreenStoryboard: 1 + +[Editor] +Bookmarks: 110,13824,54967,82395,109824 +DistanceSpacing: 0.1 +BeatDivisor: 4 +GridSize: 32 +TimelineZoom: 3.099999 + +[Metadata] +Title:test +TitleUnicode:test +Artist:sample conversion +ArtistUnicode:sample conversion +Creator:banchobot +Version:sample test +Source: +Tags: +BeatmapID:0 +BeatmapSetID:-1 + +[Difficulty] +HPDrainRate:6 +CircleSize:2 +OverallDifficulty:6 +ApproachRate:7 +SliderMultiplier:1.4 +SliderTickRate:4 + +[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] +110,428.571428571429,4,1,0,100,1,0 + +[HitObjects] +256,192,110,5,0,0:0:0:0: +256,192,538,1,8,0:0:0:0: +256,192,967,1,2,0:0:0:0: +256,192,1395,1,10,0:0:0:0: +256,192,1824,1,4,0:0:0:0: +256,192,2252,1,12,0:0:0:0: +256,192,2681,1,6,0:0:0:0: +256,192,3110,1,14,0:0:0:0: From 3ee698cfa02bf3cb7d24f37761a75b211810b702 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 19 May 2020 12:39:09 +0900 Subject: [PATCH 0790/1117] Fix being able to press enter to create matches --- .../Screens/Multi/Match/Components/MatchSettingsOverlay.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs b/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs index 5d68de9ce6..54c4f8f7c7 100644 --- a/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs +++ b/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs @@ -133,7 +133,6 @@ namespace osu.Game.Screens.Multi.Match.Components { RelativeSizeAxes = Axes.X, TabbableContentContainer = this, - OnCommit = (sender, text) => apply(), }, }, new Section("Duration") @@ -196,7 +195,6 @@ namespace osu.Game.Screens.Multi.Match.Components RelativeSizeAxes = Axes.X, TabbableContentContainer = this, ReadOnly = true, - OnCommit = (sender, text) => apply() }, }, new Section("Password (optional)") @@ -207,7 +205,6 @@ namespace osu.Game.Screens.Multi.Match.Components RelativeSizeAxes = Axes.X, TabbableContentContainer = this, ReadOnly = true, - OnCommit = (sender, text) => apply() }, }, }, @@ -331,6 +328,9 @@ namespace osu.Game.Screens.Multi.Match.Components private void apply() { + if (!ApplyButton.Enabled.Value) + return; + hideError(); RoomName.Value = NameField.Text; From 6d3ca4ec43c2963f9fee14be4508fafe10287c73 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 19 May 2020 13:16:46 +0900 Subject: [PATCH 0791/1117] Fix failing tests --- .../Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs index d2e8c22c39..34c6940552 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs @@ -69,6 +69,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { settings.NameField.Current.Value = expected_name; settings.DurationField.Current.Value = expectedDuration; + Room.Playlist.Add(new PlaylistItem { Beatmap = { Value = CreateBeatmap(Ruleset.Value).BeatmapInfo } }); roomManager.CreateRequested = r => { @@ -89,6 +90,9 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("setup", () => { + Room.Name.Value = "Test Room"; + Room.Playlist.Add(new PlaylistItem { Beatmap = { Value = CreateBeatmap(Ruleset.Value).BeatmapInfo } }); + fail = true; roomManager.CreateRequested = _ => !fail; }); From 052ad79fc6e012b3901214bf4005a263a2fa16e3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 May 2020 16:44:22 +0900 Subject: [PATCH 0792/1117] Convert dangerous events to IBindables --- .../Beatmaps/IO/ImportBeatmapTest.cs | 4 +- osu.Game/Beatmaps/BeatmapManager.cs | 13 ++-- osu.Game/Database/ArchiveModelManager.cs | 13 ++-- .../DownloadableArchiveModelManager.cs | 15 +++-- osu.Game/Database/IModelDownloader.cs | 7 +- osu.Game/Database/IModelManager.cs | 7 +- osu.Game/Online/DownloadTrackingComposite.cs | 65 ++++++++++++------- osu.Game/OsuGameBase.cs | 13 +++- osu.Game/Overlays/MusicController.cs | 46 +++++++------ .../Overlays/Settings/Sections/SkinSection.cs | 30 +++++---- .../Multi/Match/Components/ReadyButton.cs | 50 +++++++------- .../Screens/Multi/Match/MatchSubScreen.cs | 26 ++++---- osu.Game/Screens/Select/BeatmapCarousel.cs | 49 +++++++++----- .../Screens/Select/Carousel/TopLocalRank.cs | 32 ++++----- .../Select/Leaderboards/BeatmapLeaderboard.cs | 15 ++--- osu.Game/Skinning/SkinManager.cs | 13 ++-- 16 files changed, 234 insertions(+), 164 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index ba6f5fc85c..43fab186aa 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -156,8 +156,8 @@ namespace osu.Game.Tests.Beatmaps.IO var manager = osu.Dependencies.Get(); // ReSharper disable once AccessToModifiedClosure - manager.ItemAdded += _ => Interlocked.Increment(ref itemAddRemoveFireCount); - manager.ItemRemoved += _ => Interlocked.Increment(ref itemAddRemoveFireCount); + manager.ItemAdded.BindValueChanged(_ => Interlocked.Increment(ref itemAddRemoveFireCount)); + manager.ItemRemoved.BindValueChanged(_ => Interlocked.Increment(ref itemAddRemoveFireCount)); var imported = await LoadOszIntoOsu(osu); diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 34ad1df6bc..7aaf0ca08d 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -12,6 +12,7 @@ using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using osu.Framework.Audio; using osu.Framework.Audio.Track; +using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Graphics.Textures; using osu.Framework.Lists; @@ -38,12 +39,16 @@ namespace osu.Game.Beatmaps /// /// Fired when a single difficulty has been hidden. /// - public event Action BeatmapHidden; + public IBindable> BeatmapHidden => beatmapHidden; + + private readonly Bindable> beatmapHidden = new Bindable>(); /// /// Fired when a single difficulty has been restored. /// - public event Action BeatmapRestored; + public IBindable> BeatmapRestored => beatmapRestored; + + private readonly Bindable> beatmapRestored = new Bindable>(); /// /// A default representation of a WorkingBeatmap to use when no beatmap is available. @@ -74,8 +79,8 @@ namespace osu.Game.Beatmaps DefaultBeatmap = defaultBeatmap; beatmaps = (BeatmapStore)ModelStore; - beatmaps.BeatmapHidden += b => BeatmapHidden?.Invoke(b); - beatmaps.BeatmapRestored += b => BeatmapRestored?.Invoke(b); + beatmaps.BeatmapHidden += b => beatmapHidden.Value = new WeakReference(b); + beatmaps.BeatmapRestored += b => beatmapRestored.Value = new WeakReference(b); onlineLookupQueue = new BeatmapOnlineLookupQueue(api, storage); exportStorage = storage.GetStorageForDirectory("exports"); diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 839f9075e5..33b16cbaaf 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -11,6 +11,7 @@ using Humanizer; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; using osu.Framework; +using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Logging; @@ -56,13 +57,17 @@ namespace osu.Game.Database /// Fired when a new becomes available in the database. /// This is not guaranteed to run on the update thread. /// - public event Action ItemAdded; + public IBindable> ItemAdded => itemAdded; + + private readonly Bindable> itemAdded = new Bindable>(); /// /// Fired when a is removed from the database. /// This is not guaranteed to run on the update thread. /// - public event Action ItemRemoved; + public IBindable> ItemRemoved => itemRemoved; + + private readonly Bindable> itemRemoved = new Bindable>(); public virtual string[] HandledExtensions => new[] { ".zip" }; @@ -82,8 +87,8 @@ namespace osu.Game.Database ContextFactory = contextFactory; ModelStore = modelStore; - ModelStore.ItemAdded += item => handleEvent(() => ItemAdded?.Invoke(item)); - ModelStore.ItemRemoved += s => handleEvent(() => ItemRemoved?.Invoke(s)); + ModelStore.ItemAdded += item => handleEvent(() => itemAdded.Value = new WeakReference(item)); + ModelStore.ItemRemoved += item => handleEvent(() => itemRemoved.Value = new WeakReference(item)); Files = new FileStore(contextFactory, storage); diff --git a/osu.Game/Database/DownloadableArchiveModelManager.cs b/osu.Game/Database/DownloadableArchiveModelManager.cs index 1b90898c8d..8f469ca590 100644 --- a/osu.Game/Database/DownloadableArchiveModelManager.cs +++ b/osu.Game/Database/DownloadableArchiveModelManager.cs @@ -10,6 +10,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using osu.Framework.Bindables; namespace osu.Game.Database { @@ -23,9 +24,13 @@ namespace osu.Game.Database where TModel : class, IHasFiles, IHasPrimaryKey, ISoftDelete, IEquatable where TFileModel : class, INamedFileInfo, new() { - public event Action> DownloadBegan; + public IBindable>> DownloadBegan => downloadBegan; - public event Action> DownloadFailed; + private readonly Bindable>> downloadBegan = new Bindable>>(); + + public IBindable>> DownloadFailed => downloadFailed; + + private readonly Bindable>> downloadFailed = new Bindable>>(); private readonly IAPIProvider api; @@ -81,7 +86,7 @@ namespace osu.Game.Database // for now a failed import will be marked as a failed download for simplicity. if (!imported.Any()) - DownloadFailed?.Invoke(request); + downloadFailed.Value = new WeakReference>(request); currentDownloads.Remove(request); }, TaskCreationOptions.LongRunning); @@ -100,14 +105,14 @@ namespace osu.Game.Database api.PerformAsync(request); - DownloadBegan?.Invoke(request); + downloadBegan.Value = new WeakReference>(request); return true; void triggerFailure(Exception error) { currentDownloads.Remove(request); - DownloadFailed?.Invoke(request); + downloadFailed.Value = new WeakReference>(request); notification.State = ProgressNotificationState.Cancelled; diff --git a/osu.Game/Database/IModelDownloader.cs b/osu.Game/Database/IModelDownloader.cs index 99aeb4eacf..0cb633280e 100644 --- a/osu.Game/Database/IModelDownloader.cs +++ b/osu.Game/Database/IModelDownloader.cs @@ -1,8 +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 osu.Game.Online.API; using System; +using osu.Game.Online.API; +using osu.Framework.Bindables; namespace osu.Game.Database { @@ -17,13 +18,13 @@ namespace osu.Game.Database /// Fired when a download begins. /// This is NOT run on the update thread and should be scheduled. /// - event Action> DownloadBegan; + IBindable>> DownloadBegan { get; } /// /// Fired when a download is interrupted, either due to user cancellation or failure. /// This is NOT run on the update thread and should be scheduled. /// - event Action> DownloadFailed; + IBindable>> DownloadFailed { get; } /// /// Checks whether a given is already available in the local store. diff --git a/osu.Game/Database/IModelManager.cs b/osu.Game/Database/IModelManager.cs index 1bdbbb48e6..852b385798 100644 --- a/osu.Game/Database/IModelManager.cs +++ b/osu.Game/Database/IModelManager.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.Database { @@ -9,11 +10,11 @@ namespace osu.Game.Database /// Represents a model manager that publishes events when s are added or removed. /// /// The model type. - public interface IModelManager + public interface IModelManager where TModel : class { - event Action ItemAdded; + IBindable> ItemAdded { get; } - event Action ItemRemoved; + IBindable> ItemRemoved { get; } } } diff --git a/osu.Game/Online/DownloadTrackingComposite.cs b/osu.Game/Online/DownloadTrackingComposite.cs index 0769be2998..47de7d75ed 100644 --- a/osu.Game/Online/DownloadTrackingComposite.cs +++ b/osu.Game/Online/DownloadTrackingComposite.cs @@ -34,6 +34,11 @@ namespace osu.Game.Online Model.Value = model; } + private IBindable> managerAdded; + private IBindable> managerRemoved; + private IBindable>> managerDownloadBegan; + private IBindable>> managerDownloadFailed; + [BackgroundDependencyLoader(true)] private void load() { @@ -47,23 +52,39 @@ namespace osu.Game.Online attachDownload(manager.GetExistingDownload(modelInfo.NewValue)); }, true); - manager.DownloadBegan += downloadBegan; - manager.DownloadFailed += downloadFailed; - manager.ItemAdded += itemAdded; - manager.ItemRemoved += itemRemoved; + managerDownloadBegan = manager.DownloadBegan.GetBoundCopy(); + managerDownloadBegan.BindValueChanged(downloadBegan); + managerDownloadFailed = manager.DownloadFailed.GetBoundCopy(); + managerDownloadFailed.BindValueChanged(downloadFailed); + managerAdded = manager.ItemAdded.GetBoundCopy(); + managerAdded.BindValueChanged(itemAdded); + managerRemoved = manager.ItemRemoved.GetBoundCopy(); + managerRemoved.BindValueChanged(itemRemoved); } - private void downloadBegan(ArchiveDownloadRequest request) => Schedule(() => + private void downloadBegan(ValueChangedEvent>> weakRequest) { - if (request.Model.Equals(Model.Value)) - attachDownload(request); - }); + if (weakRequest.NewValue.TryGetTarget(out var request)) + { + Schedule(() => + { + if (request.Model.Equals(Model.Value)) + attachDownload(request); + }); + } + } - private void downloadFailed(ArchiveDownloadRequest request) => Schedule(() => + private void downloadFailed(ValueChangedEvent>> weakRequest) { - if (request.Model.Equals(Model.Value)) - attachDownload(null); - }); + if (weakRequest.NewValue.TryGetTarget(out var request)) + { + Schedule(() => + { + if (request.Model.Equals(Model.Value)) + attachDownload(null); + }); + } + } private ArchiveDownloadRequest attachedRequest; @@ -107,9 +128,17 @@ namespace osu.Game.Online private void onRequestFailure(Exception e) => Schedule(() => attachDownload(null)); - private void itemAdded(TModel s) => setDownloadStateFromManager(s, DownloadState.LocallyAvailable); + private void itemAdded(ValueChangedEvent> weakItem) + { + if (weakItem.NewValue.TryGetTarget(out var item)) + setDownloadStateFromManager(item, DownloadState.LocallyAvailable); + } - private void itemRemoved(TModel s) => setDownloadStateFromManager(s, DownloadState.NotDownloaded); + private void itemRemoved(ValueChangedEvent> weakItem) + { + if (weakItem.NewValue.TryGetTarget(out var item)) + setDownloadStateFromManager(item, DownloadState.NotDownloaded); + } private void setDownloadStateFromManager(TModel s, DownloadState state) => Schedule(() => { @@ -125,14 +154,6 @@ namespace osu.Game.Online { base.Dispose(isDisposing); - if (manager != null) - { - manager.DownloadBegan -= downloadBegan; - manager.DownloadFailed -= downloadFailed; - manager.ItemAdded -= itemAdded; - manager.ItemRemoved -= itemRemoved; - } - State.UnbindAll(); attachDownload(null); diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 11a3834c71..c367c3b636 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -186,8 +186,17 @@ namespace osu.Game return ScoreManager.QueryScores(s => beatmapIds.Contains(s.Beatmap.ID)).ToList(); } - BeatmapManager.ItemRemoved += i => ScoreManager.Delete(getBeatmapScores(i), true); - BeatmapManager.ItemAdded += i => ScoreManager.Undelete(getBeatmapScores(i), true); + BeatmapManager.ItemRemoved.BindValueChanged(i => + { + if (i.NewValue.TryGetTarget(out var item)) + ScoreManager.Delete(getBeatmapScores(item), true); + }); + + BeatmapManager.ItemAdded.BindValueChanged(i => + { + if (i.NewValue.TryGetTarget(out var item)) + ScoreManager.Undelete(getBeatmapScores(item), true); + }); dependencies.Cache(KeyBindingStore = new KeyBindingStore(contextFactory, RulesetStore)); dependencies.Cache(SettingsStore = new SettingsStore(contextFactory)); diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index ded641b262..35f3cb0e25 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -60,11 +60,16 @@ namespace osu.Game.Overlays [Resolved(canBeNull: true)] private OnScreenDisplay onScreenDisplay { get; set; } + private IBindable> managerAdded; + private IBindable> managerRemoved; + [BackgroundDependencyLoader] private void load() { - beatmaps.ItemAdded += handleBeatmapAdded; - beatmaps.ItemRemoved += handleBeatmapRemoved; + managerAdded = beatmaps.ItemAdded.GetBoundCopy(); + managerAdded.BindValueChanged(beatmapAdded); + managerRemoved = beatmaps.ItemRemoved.GetBoundCopy(); + managerRemoved.BindValueChanged(beatmapRemoved); beatmapSets.AddRange(beatmaps.GetAllUsableBeatmapSets(IncludedDetails.Minimal).OrderBy(_ => RNG.Next())); } @@ -93,16 +98,28 @@ namespace osu.Game.Overlays /// public bool IsPlaying => current?.Track.IsRunning ?? false; - private void handleBeatmapAdded(BeatmapSetInfo set) => Schedule(() => + private void beatmapAdded(ValueChangedEvent> weakSet) { - if (!beatmapSets.Contains(set)) - beatmapSets.Add(set); - }); + if (weakSet.NewValue.TryGetTarget(out var set)) + { + Schedule(() => + { + if (!beatmapSets.Contains(set)) + beatmapSets.Add(set); + }); + } + } - private void handleBeatmapRemoved(BeatmapSetInfo set) => Schedule(() => + private void beatmapRemoved(ValueChangedEvent> weakSet) { - beatmapSets.RemoveAll(s => s.ID == set.ID); - }); + if (weakSet.NewValue.TryGetTarget(out var set)) + { + Schedule(() => + { + beatmapSets.RemoveAll(s => s.ID == set.ID); + }); + } + } private ScheduledDelegate seekDelegate; @@ -299,17 +316,6 @@ namespace osu.Game.Overlays } } - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - if (beatmaps != null) - { - beatmaps.ItemAdded -= handleBeatmapAdded; - beatmaps.ItemRemoved -= handleBeatmapRemoved; - } - } - public bool OnPressed(GlobalAction action) { if (beatmap.Disabled) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 75c8db1612..94080f5592 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.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.Bindables; @@ -30,6 +31,9 @@ namespace osu.Game.Overlays.Settings.Sections [Resolved] private SkinManager skins { get; set; } + private IBindable> managerAdded; + private IBindable> managerRemoved; + [BackgroundDependencyLoader] private void load(OsuConfigManager config) { @@ -66,8 +70,11 @@ namespace osu.Game.Overlays.Settings.Sections }, }; - skins.ItemAdded += itemAdded; - skins.ItemRemoved += itemRemoved; + managerAdded = skins.ItemAdded.GetBoundCopy(); + managerAdded.BindValueChanged(itemAdded); + + managerRemoved = skins.ItemRemoved.GetBoundCopy(); + managerRemoved.BindValueChanged(itemRemoved); config.BindWith(OsuSetting.Skin, configBindable); @@ -82,19 +89,16 @@ namespace osu.Game.Overlays.Settings.Sections dropdownBindable.BindValueChanged(skin => configBindable.Value = skin.NewValue.ID); } - private void itemRemoved(SkinInfo s) => Schedule(() => skinDropdown.Items = skinDropdown.Items.Where(i => i.ID != s.ID).ToArray()); - - private void itemAdded(SkinInfo s) => Schedule(() => skinDropdown.Items = skinDropdown.Items.Append(s).ToArray()); - - protected override void Dispose(bool isDisposing) + private void itemAdded(ValueChangedEvent> weakItem) { - base.Dispose(isDisposing); + if (weakItem.NewValue.TryGetTarget(out var item)) + Schedule(() => skinDropdown.Items = skinDropdown.Items.Append(item).ToArray()); + } - if (skins != null) - { - skins.ItemAdded -= itemAdded; - skins.ItemRemoved -= itemRemoved; - } + private void itemRemoved(ValueChangedEvent> weakItem) + { + if (weakItem.NewValue.TryGetTarget(out var item)) + Schedule(() => skinDropdown.Items = skinDropdown.Items.Where(i => i.ID != item.ID).ToArray()); } private class SizeSlider : OsuSliderBar diff --git a/osu.Game/Screens/Multi/Match/Components/ReadyButton.cs b/osu.Game/Screens/Multi/Match/Components/ReadyButton.cs index 8f484d3672..4420b2d58a 100644 --- a/osu.Game/Screens/Multi/Match/Components/ReadyButton.cs +++ b/osu.Game/Screens/Multi/Match/Components/ReadyButton.cs @@ -32,11 +32,16 @@ namespace osu.Game.Screens.Multi.Match.Components Text = "Start"; } + private IBindable> managerAdded; + private IBindable> managerRemoved; + [BackgroundDependencyLoader] private void load(OsuColour colours) { - beatmaps.ItemAdded += beatmapAdded; - beatmaps.ItemRemoved += beatmapRemoved; + managerAdded = beatmaps.ItemAdded.GetBoundCopy(); + managerAdded.BindValueChanged(beatmapAdded); + managerRemoved = beatmaps.ItemRemoved.GetBoundCopy(); + managerRemoved.BindValueChanged(beatmapRemoved); SelectedItem.BindValueChanged(item => updateSelectedItem(item.NewValue), true); @@ -56,24 +61,30 @@ namespace osu.Game.Screens.Multi.Match.Components hasBeatmap = beatmaps.QueryBeatmap(b => b.OnlineBeatmapID == beatmapId) != null; } - private void beatmapAdded(BeatmapSetInfo model) + private void beatmapAdded(ValueChangedEvent> weakSet) { - int? beatmapId = SelectedItem.Value?.Beatmap.Value?.OnlineBeatmapID; - if (beatmapId == null) - return; + if (weakSet.NewValue.TryGetTarget(out var set)) + { + int? beatmapId = SelectedItem.Value?.Beatmap.Value?.OnlineBeatmapID; + if (beatmapId == null) + return; - if (model.Beatmaps.Any(b => b.OnlineBeatmapID == beatmapId)) - Schedule(() => hasBeatmap = true); + if (set.Beatmaps.Any(b => b.OnlineBeatmapID == beatmapId)) + Schedule(() => hasBeatmap = true); + } } - private void beatmapRemoved(BeatmapSetInfo model) + private void beatmapRemoved(ValueChangedEvent> weakSet) { - int? beatmapId = SelectedItem.Value?.Beatmap.Value?.OnlineBeatmapID; - if (beatmapId == null) - return; + if (weakSet.NewValue.TryGetTarget(out var set)) + { + int? beatmapId = SelectedItem.Value?.Beatmap.Value?.OnlineBeatmapID; + if (beatmapId == null) + return; - if (model.Beatmaps.Any(b => b.OnlineBeatmapID == beatmapId)) - Schedule(() => hasBeatmap = false); + if (set.Beatmaps.Any(b => b.OnlineBeatmapID == beatmapId)) + Schedule(() => hasBeatmap = false); + } } protected override void Update() @@ -95,16 +106,5 @@ namespace osu.Game.Screens.Multi.Match.Components Enabled.Value = hasBeatmap && hasEnoughTime; } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - if (beatmaps != null) - { - beatmaps.ItemAdded -= beatmapAdded; - beatmaps.ItemRemoved -= beatmapRemoved; - } - } } } diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs index eef53126c0..caa547ac72 100644 --- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs @@ -50,6 +50,8 @@ namespace osu.Game.Screens.Multi.Match private LeaderboardChatDisplay leaderboardChatDisplay; private MatchSettingsOverlay settingsOverlay; + private IBindable> managerAdded; + public MatchSubScreen(Room room) { Title = room.RoomID.Value == null ? "New room" : room.Name.Value; @@ -181,7 +183,8 @@ namespace osu.Game.Screens.Multi.Match SelectedItem.BindValueChanged(_ => Scheduler.AddOnce(selectedItemChanged)); SelectedItem.Value = playlist.FirstOrDefault(); - beatmapManager.ItemAdded += beatmapAdded; + managerAdded = beatmapManager.ItemAdded.GetBoundCopy(); + managerAdded.BindValueChanged(beatmapAdded); } public override bool OnExiting(IScreen next) @@ -214,13 +217,16 @@ namespace osu.Game.Screens.Multi.Match Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap); } - private void beatmapAdded(BeatmapSetInfo model) => Schedule(() => + private void beatmapAdded(ValueChangedEvent> weakSet) { - if (Beatmap.Value != beatmapManager.DefaultBeatmap) - return; + Schedule(() => + { + if (Beatmap.Value != beatmapManager.DefaultBeatmap) + return; - updateWorkingBeatmap(); - }); + updateWorkingBeatmap(); + }); + } private void onStart() { @@ -235,13 +241,5 @@ namespace osu.Game.Screens.Multi.Match break; } } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - if (beatmapManager != null) - beatmapManager.ItemAdded -= beatmapAdded; - } } } diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 96b779cd20..f23e1b1ef2 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -131,6 +131,11 @@ namespace osu.Game.Screens.Select private CarouselRoot root; + private IBindable> itemAdded; + private IBindable> itemRemoved; + private IBindable> itemHidden; + private IBindable> itemRestored; + public BeatmapCarousel() { root = new CarouselRoot(this); @@ -161,10 +166,14 @@ namespace osu.Game.Screens.Select RightClickScrollingEnabled.ValueChanged += enabled => scroll.RightMouseScrollbar = enabled.NewValue; RightClickScrollingEnabled.TriggerChange(); - beatmaps.ItemAdded += beatmapAdded; - beatmaps.ItemRemoved += beatmapRemoved; - beatmaps.BeatmapHidden += beatmapHidden; - beatmaps.BeatmapRestored += beatmapRestored; + itemAdded = beatmaps.ItemAdded.GetBoundCopy(); + itemAdded.BindValueChanged(beatmapAdded); + itemRemoved = beatmaps.ItemRemoved.GetBoundCopy(); + itemRemoved.BindValueChanged(beatmapRemoved); + itemHidden = beatmaps.BeatmapHidden.GetBoundCopy(); + itemHidden.BindValueChanged(beatmapHidden); + itemRestored = beatmaps.BeatmapRestored.GetBoundCopy(); + itemRestored.BindValueChanged(beatmapRestored); loadBeatmapSets(GetLoadableBeatmaps()); } @@ -562,26 +571,34 @@ namespace osu.Game.Screens.Select { base.Dispose(isDisposing); - if (beatmaps != null) - { - beatmaps.ItemAdded -= beatmapAdded; - beatmaps.ItemRemoved -= beatmapRemoved; - beatmaps.BeatmapHidden -= beatmapHidden; - beatmaps.BeatmapRestored -= beatmapRestored; - } - // aggressively dispose "off-screen" items to reduce GC pressure. foreach (var i in Items) i.Dispose(); } - private void beatmapRemoved(BeatmapSetInfo item) => RemoveBeatmapSet(item); + private void beatmapRemoved(ValueChangedEvent> weakItem) + { + if (weakItem.NewValue.TryGetTarget(out var item)) + RemoveBeatmapSet(item); + } - private void beatmapAdded(BeatmapSetInfo item) => UpdateBeatmapSet(item); + private void beatmapAdded(ValueChangedEvent> weakItem) + { + if (weakItem.NewValue.TryGetTarget(out var item)) + UpdateBeatmapSet(item); + } - private void beatmapRestored(BeatmapInfo b) => UpdateBeatmapSet(beatmaps.QueryBeatmapSet(s => s.ID == b.BeatmapSetInfoID)); + private void beatmapRestored(ValueChangedEvent> weakItem) + { + if (weakItem.NewValue.TryGetTarget(out var b)) + UpdateBeatmapSet(beatmaps.QueryBeatmapSet(s => s.ID == b.BeatmapSetInfoID)); + } - private void beatmapHidden(BeatmapInfo b) => UpdateBeatmapSet(beatmaps.QueryBeatmapSet(s => s.ID == b.BeatmapSetInfoID)); + private void beatmapHidden(ValueChangedEvent> weakItem) + { + if (weakItem.NewValue.TryGetTarget(out var b)) + UpdateBeatmapSet(beatmaps.QueryBeatmapSet(s => s.ID == b.BeatmapSetInfoID)); + } private CarouselBeatmapSet createCarouselSet(BeatmapSetInfo beatmapSet) { diff --git a/osu.Game/Screens/Select/Carousel/TopLocalRank.cs b/osu.Game/Screens/Select/Carousel/TopLocalRank.cs index e981550c84..aed25787b0 100644 --- a/osu.Game/Screens/Select/Carousel/TopLocalRank.cs +++ b/osu.Game/Screens/Select/Carousel/TopLocalRank.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.Bindables; @@ -27,6 +28,9 @@ namespace osu.Game.Screens.Select.Carousel [Resolved] private IAPIProvider api { get; set; } + private IBindable> itemAdded; + private IBindable> itemRemoved; + public TopLocalRank(BeatmapInfo beatmap) : base(null) { @@ -36,17 +40,24 @@ namespace osu.Game.Screens.Select.Carousel [BackgroundDependencyLoader] private void load() { - scores.ItemAdded += scoreChanged; - scores.ItemRemoved += scoreChanged; + itemAdded = scores.ItemAdded.GetBoundCopy(); + itemAdded.BindValueChanged(scoreChanged); + + itemRemoved = scores.ItemRemoved.GetBoundCopy(); + itemRemoved.BindValueChanged(scoreChanged); + ruleset.ValueChanged += _ => fetchAndLoadTopScore(); fetchAndLoadTopScore(); } - private void scoreChanged(ScoreInfo score) + private void scoreChanged(ValueChangedEvent> weakScore) { - if (score.BeatmapInfoID == beatmap.ID) - fetchAndLoadTopScore(); + if (weakScore.NewValue.TryGetTarget(out var score)) + { + if (score.BeatmapInfoID == beatmap.ID) + fetchAndLoadTopScore(); + } } private ScheduledDelegate scheduledRankUpdate; @@ -75,16 +86,5 @@ namespace osu.Game.Screens.Select.Carousel .OrderByDescending(s => s.TotalScore) .FirstOrDefault(); } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - if (scores != null) - { - scores.ItemAdded -= scoreChanged; - scores.ItemRemoved -= scoreChanged; - } - } } } diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index e36493c82f..8e85eb4eb2 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -60,6 +60,8 @@ namespace osu.Game.Screens.Select.Leaderboards private UserTopScoreContainer topScoreContainer; + private IBindable> itemRemoved; + /// /// Whether to apply the game's currently selected mods as a filter when retrieving scores. /// @@ -104,7 +106,8 @@ namespace osu.Game.Screens.Select.Leaderboards ScoreSelected = s => ScoreSelected?.Invoke(s) }); - scoreManager.ItemRemoved += onScoreRemoved; + itemRemoved = scoreManager.ItemRemoved.GetBoundCopy(); + itemRemoved.BindValueChanged(onScoreRemoved); } protected override void Reset() @@ -113,7 +116,7 @@ namespace osu.Game.Screens.Select.Leaderboards TopScore = null; } - private void onScoreRemoved(ScoreInfo score) => Schedule(RefreshScores); + private void onScoreRemoved(ValueChangedEvent> score) => Schedule(RefreshScores); protected override bool IsOnlineScope => Scope != BeatmapLeaderboardScope.Local; @@ -190,13 +193,5 @@ namespace osu.Game.Screens.Select.Leaderboards { Action = () => ScoreSelected?.Invoke(model) }; - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - if (scoreManager != null) - scoreManager.ItemRemoved -= onScoreRemoved; - } } } diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 3d469ab6e1..d65c74ef62 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -43,12 +43,15 @@ namespace osu.Game.Skinning this.audio = audio; this.legacyDefaultResources = legacyDefaultResources; - ItemRemoved += removedInfo => + ItemRemoved.BindValueChanged(weakRemovedInfo => { - // check the removed skin is not the current user choice. if it is, switch back to default. - if (removedInfo.ID == CurrentSkinInfo.Value.ID) - CurrentSkinInfo.Value = SkinInfo.Default; - }; + if (weakRemovedInfo.NewValue.TryGetTarget(out var removedInfo)) + { + // check the removed skin is not the current user choice. if it is, switch back to default. + if (removedInfo.ID == CurrentSkinInfo.Value.ID) + CurrentSkinInfo.Value = SkinInfo.Default; + } + }); CurrentSkinInfo.ValueChanged += skin => CurrentSkin.Value = GetSkin(skin.NewValue); CurrentSkin.ValueChanged += skin => From d56466e2b9376318edc845c84c361fbf3927161d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 May 2020 19:07:35 +0900 Subject: [PATCH 0793/1117] Add very basic pooling of grid lines --- .../Edit/ManiaBeatSnapGrid.cs | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs index a16fb52f01..067438af39 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs @@ -90,10 +90,17 @@ namespace osu.Game.Rulesets.Mania.Edit beatDivisor.BindValueChanged(_ => createLines(), true); } + private readonly Stack availableLines = new Stack(); + private void createLines() { foreach (var grid in grids) - grid.Clear(); + { + foreach (var line in grid.Objects.OfType()) + availableLines.Push(line); + + grid.Clear(false); + } if (selectionTimeRange == null) return; @@ -127,7 +134,15 @@ namespace osu.Game.Rulesets.Mania.Edit BindableBeatDivisor.GetDivisorForBeatIndex(Math.Max(1, beat), beatDivisor.Value), colours); foreach (var grid in grids) - grid.Add(new DrawableGridLine(time, colour)); + { + if (!availableLines.TryPop(out var line)) + line = new DrawableGridLine(); + + line.HitObject.StartTime = time; + line.Colour = colour; + + grid.Add(line); + } beat++; time += timingPoint.BeatLength / beatDivisor.Value; @@ -190,17 +205,13 @@ namespace osu.Game.Rulesets.Mania.Edit private readonly IBindable direction = new Bindable(); - public DrawableGridLine(double startTime, Color4 colour) - : base(new HitObject { StartTime = startTime }) + public DrawableGridLine() + : base(new HitObject()) { RelativeSizeAxes = Axes.X; Height = 2; - AddInternal(new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colour - }); + AddInternal(new Box { RelativeSizeAxes = Axes.Both }); } [BackgroundDependencyLoader] From a6f3dc53f72e3100da0cb6d422a872522371e38e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 May 2020 23:01:36 +0900 Subject: [PATCH 0794/1117] Fix time value not being updated for next timing point --- osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs index 067438af39..5803c67b80 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs @@ -127,6 +127,7 @@ namespace osu.Game.Rulesets.Mania.Edit if (nextTimingPoint != timingPoint) { beat = 0; + time = nextTimingPoint.Time; timingPoint = nextTimingPoint; } From c28a9bdb804a05ee5fac61b5728931885650cdf5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 May 2020 23:02:20 +0900 Subject: [PATCH 0795/1117] Move load method up --- .../Edit/ManiaBeatSnapGrid.cs | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs index 5803c67b80..0b7834addb 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs @@ -51,7 +51,6 @@ namespace osu.Game.Rulesets.Mania.Edit public (double start, double end)? SelectionTimeRange { - get => selectionTimeRange; set { if (value == selectionTimeRange) @@ -62,17 +61,6 @@ namespace osu.Game.Rulesets.Mania.Edit } } - protected override void Update() - { - base.Update(); - - if (!lineCache.IsValid) - { - lineCache.Validate(); - createLines(); - } - } - [BackgroundDependencyLoader] private void load() { @@ -90,6 +78,17 @@ namespace osu.Game.Rulesets.Mania.Edit beatDivisor.BindValueChanged(_ => createLines(), true); } + protected override void Update() + { + base.Update(); + + if (!lineCache.IsValid) + { + lineCache.Validate(); + createLines(); + } + } + private readonly Stack availableLines = new Stack(); private void createLines() From 85156c62efd7900dbce4115e27f96327f4fd0345 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 May 2020 23:05:08 +0900 Subject: [PATCH 0796/1117] Add xmldoc and address some code quality concerns --- .../Edit/ManiaBeatSnapGrid.cs | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs index 0b7834addb..98e15e3fa8 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs @@ -21,12 +21,27 @@ using osuTK.Graphics; namespace osu.Game.Rulesets.Mania.Edit { + /// + /// A grid which displays coloured beat divisor lines in proximity to the selection or placement cursor. + /// public class ManiaBeatSnapGrid : Component { private const double visible_range = 750; - [Resolved] - private IManiaHitObjectComposer composer { get; set; } + /// + /// The range of time values of the current selection. + /// + public (double start, double end)? SelectionTimeRange + { + set + { + if (value == selectionTimeRange) + return; + + selectionTimeRange = value; + lineCache.Invalidate(); + } + } [Resolved] private EditorBeatmap beatmap { get; set; } @@ -49,20 +64,8 @@ namespace osu.Game.Rulesets.Mania.Edit private (double start, double end)? selectionTimeRange; - public (double start, double end)? SelectionTimeRange - { - set - { - if (value == selectionTimeRange) - return; - - selectionTimeRange = value; - lineCache.Invalidate(); - } - } - [BackgroundDependencyLoader] - private void load() + private void load(IManiaHitObjectComposer composer) { foreach (var stage in composer.Playfield.Stages) { From db4e3047ddf02cb09a7603c68a0ab854e1cfe6b2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 May 2020 23:28:13 +0900 Subject: [PATCH 0797/1117] Add test for final sample output --- .../TestSceneSampleOutput.cs | 47 +++++++++++++++++++ .../Objects/Drawables/DrawableHit.cs | 2 +- .../Drawables/DrawableTaikoHitObject.cs | 2 +- .../Objects/Drawables/DrawableHitObject.cs | 2 +- .../Tests/Beatmaps/BeatmapConversionTest.cs | 11 +++-- 5 files changed, 58 insertions(+), 6 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko.Tests/TestSceneSampleOutput.cs diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneSampleOutput.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneSampleOutput.cs new file mode 100644 index 0000000000..564ab91291 --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneSampleOutput.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 osu.Game.Audio; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Taiko.Objects.Drawables; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Taiko.Tests +{ + /// + /// Taiko has some interesting rules for legacy mappings. + /// + public class TestSceneSampleOutput : PlayerTestScene + { + public TestSceneSampleOutput() + : base(new TaikoRuleset()) + { + } + + public override void SetUpSteps() + { + base.SetUpSteps(); + AddAssert("has correct samples", () => + { + var names = Player.DrawableRuleset.Playfield.AllHitObjects.OfType().Select(h => string.Join(',', h.GetSamples().Select(s => s.Name))); + + var expected = new[] + { + string.Empty, + string.Empty, + string.Empty, + string.Empty, + HitSampleInfo.HIT_FINISH, + HitSampleInfo.HIT_WHISTLE, + HitSampleInfo.HIT_WHISTLE, + HitSampleInfo.HIT_WHISTLE, + }; + + return names.SequenceEqual(expected); + }); + } + + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TaikoBeatmapConversionTest().GetBeatmap("sample-to-type-conversions"); + } +} diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index 1e1f9ae09b..81b969eaf3 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables ? new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.CentreHit), _ => new CentreHitCirclePiece(), confineMode: ConfineMode.ScaleToFit) : new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.RimHit), _ => new RimHitCirclePiece(), confineMode: ConfineMode.ScaleToFit); - protected override IEnumerable GetSamples() + public override IEnumerable GetSamples() { // normal and claps are always handled by the drum (see DrumSampleMapping). // in addition, whistles are excluded as they are an alternative rim marker. diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs index 90daf3950c..3ab09d4cbe 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs @@ -166,7 +166,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables } // Most osu!taiko hitsounds are managed by the drum (see DrumSampleMapping). - protected override IEnumerable GetSamples() => Enumerable.Empty(); + public override IEnumerable GetSamples() => Enumerable.Empty(); protected abstract SkinnableDrawable CreateMainPiece(); diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index ba6571fe1a..33ea02c22f 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Objects.Drawables protected SkinnableSound Samples { get; private set; } - protected virtual IEnumerable GetSamples() => HitObject.Samples; + public virtual IEnumerable GetSamples() => HitObject.Samples; private readonly Lazy> nestedHitObjects = new Lazy>(); public IReadOnlyList NestedHitObjects => nestedHitObjects.IsValueCreated ? nestedHitObjects.Value : (IReadOnlyList)Array.Empty(); diff --git a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs index b60add6e3b..06e82394ec 100644 --- a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs +++ b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs @@ -99,7 +99,7 @@ namespace osu.Game.Tests.Beatmaps private ConvertResult convert(string name, Mod[] mods) { - var beatmap = getBeatmap(name); + var beatmap = GetBeatmap(name); var rulesetInstance = CreateRuleset(); beatmap.BeatmapInfo.Ruleset = beatmap.BeatmapInfo.RulesetID == rulesetInstance.RulesetInfo.ID ? rulesetInstance.RulesetInfo : new RulesetInfo(); @@ -143,14 +143,19 @@ namespace osu.Game.Tests.Beatmaps } } - private IBeatmap getBeatmap(string name) + public IBeatmap GetBeatmap(string name) { using (var resStream = openResource($"{resource_namespace}.{name}.osu")) using (var stream = new LineBufferedReader(resStream)) { var decoder = Decoder.GetDecoder(stream); ((LegacyBeatmapDecoder)decoder).ApplyOffsets = false; - return decoder.Decode(stream); + var beatmap = decoder.Decode(stream); + + // not sure but seems to be required. + beatmap.BeatmapInfo.Ruleset = CreateRuleset().RulesetInfo; + + return beatmap; } } From da8729e6bde39ec4255d498cde1e50a7135d5601 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 May 2020 23:28:42 +0900 Subject: [PATCH 0798/1117] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 650ebde54d..f0f16d3763 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index ee6206e166..010ef8578a 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -24,7 +24,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index cbf8600c62..88b0c7dd8a 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -80,7 +80,7 @@ - + From e21178570484780c8467f47529ea407a6fd5e24c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 18 May 2020 21:01:13 +0200 Subject: [PATCH 0799/1117] Add overlay layer to storyboard definition --- osu.Game/Storyboards/Storyboard.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/osu.Game/Storyboards/Storyboard.cs b/osu.Game/Storyboards/Storyboard.cs index d13c874ee2..b0fb583d62 100644 --- a/osu.Game/Storyboards/Storyboard.cs +++ b/osu.Game/Storyboards/Storyboard.cs @@ -19,19 +19,26 @@ namespace osu.Game.Storyboards public double FirstEventTime => Layers.Min(l => l.Elements.FirstOrDefault()?.StartTime ?? 0); + /// + /// Depth of the currently front-most storyboard layer, excluding the overlay layer. + /// + private int minimumLayerDepth; + public Storyboard() { layers.Add("Video", new StoryboardLayer("Video", 4, false)); layers.Add("Background", new StoryboardLayer("Background", 3)); layers.Add("Fail", new StoryboardLayer("Fail", 2) { VisibleWhenPassing = false, }); layers.Add("Pass", new StoryboardLayer("Pass", 1) { VisibleWhenFailing = false, }); - layers.Add("Foreground", new StoryboardLayer("Foreground", 0)); + layers.Add("Foreground", new StoryboardLayer("Foreground", minimumLayerDepth = 0)); + + layers.Add("Overlay", new StoryboardLayer("Overlay", int.MinValue)); } public StoryboardLayer GetLayer(string name) { if (!layers.TryGetValue(name, out var layer)) - layers[name] = layer = new StoryboardLayer(name, layers.Values.Min(l => l.Depth) - 1); + layers[name] = layer = new StoryboardLayer(name, --minimumLayerDepth); return layer; } From 6e27247cdf7bc46b4317cbb98f3dfb5b20c769a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 18 May 2020 22:10:02 +0200 Subject: [PATCH 0800/1117] Adjust storyboard decoder test in line with changes --- .../Beatmaps/Formats/LegacyStoryboardDecoderTest.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs index 2fdeadca02..9ebedb3c80 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs @@ -26,7 +26,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var storyboard = decoder.Decode(stream); Assert.IsTrue(storyboard.HasDrawable); - Assert.AreEqual(5, storyboard.Layers.Count()); + Assert.AreEqual(6, storyboard.Layers.Count()); StoryboardLayer background = storyboard.Layers.FirstOrDefault(l => l.Depth == 3); Assert.IsNotNull(background); @@ -56,6 +56,13 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.IsTrue(foreground.VisibleWhenPassing); Assert.AreEqual("Foreground", foreground.Name); + StoryboardLayer overlay = storyboard.Layers.FirstOrDefault(l => l.Depth == int.MinValue); + Assert.IsNotNull(overlay); + Assert.IsEmpty(overlay.Elements); + Assert.IsTrue(overlay.VisibleWhenFailing); + Assert.IsTrue(overlay.VisibleWhenPassing); + Assert.AreEqual("Overlay", overlay.Name); + int spriteCount = background.Elements.Count(x => x.GetType() == typeof(StoryboardSprite)); int animationCount = background.Elements.Count(x => x.GetType() == typeof(StoryboardAnimation)); int sampleCount = background.Elements.Count(x => x.GetType() == typeof(StoryboardSampleInfo)); From 2398f2e537e8375b63f0de39b637751c1cbaca96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 18 May 2020 21:12:14 +0200 Subject: [PATCH 0801/1117] Expose drawable overlay layer --- osu.Game/Storyboards/Drawables/DrawableStoryboard.cs | 3 +++ osu.Game/Storyboards/StoryboardLayer.cs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs index c4d796e30b..ec461fa095 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboard.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 System.Threading; using osuTK; using osu.Framework.Allocation; @@ -72,6 +73,8 @@ namespace osu.Game.Storyboards.Drawables } } + public DrawableStoryboardLayer OverlayLayer => Children.Single(layer => layer.Name == "Overlay"); + private void updateLayerVisibility() { foreach (var layer in Children) diff --git a/osu.Game/Storyboards/StoryboardLayer.cs b/osu.Game/Storyboards/StoryboardLayer.cs index 142bc60deb..1cde7cf67a 100644 --- a/osu.Game/Storyboards/StoryboardLayer.cs +++ b/osu.Game/Storyboards/StoryboardLayer.cs @@ -33,6 +33,6 @@ namespace osu.Game.Storyboards } public DrawableStoryboardLayer CreateDrawable() - => new DrawableStoryboardLayer(this) { Depth = Depth, }; + => new DrawableStoryboardLayer(this) { Depth = Depth, Name = Name }; } } From ce4301c5b8a5a36dafb4dac54d04c4eb49920b4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 19 May 2020 19:47:01 +0200 Subject: [PATCH 0802/1117] Add overlay layer to player by proxying --- osu.Game/Screens/Play/DimmableStoryboard.cs | 15 +++++++++++++-- osu.Game/Screens/Play/Player.cs | 1 + 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/DimmableStoryboard.cs b/osu.Game/Screens/Play/DimmableStoryboard.cs index eabdee95fb..74c84f648c 100644 --- a/osu.Game/Screens/Play/DimmableStoryboard.cs +++ b/osu.Game/Screens/Play/DimmableStoryboard.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.Containers; using osu.Game.Graphics.Containers; using osu.Game.Storyboards; using osu.Game.Storyboards.Drawables; @@ -13,6 +14,8 @@ namespace osu.Game.Screens.Play /// public class DimmableStoryboard : UserDimContainer { + public Container OverlayLayerContainer; + private readonly Storyboard storyboard; private DrawableStoryboard drawableStoryboard; @@ -24,6 +27,8 @@ namespace osu.Game.Screens.Play [BackgroundDependencyLoader] private void load() { + Add(OverlayLayerContainer = new Container()); + initializeStoryboard(false); } @@ -46,9 +51,15 @@ namespace osu.Game.Screens.Play drawableStoryboard = storyboard.CreateDrawable(); if (async) - LoadComponentAsync(drawableStoryboard, Add); + LoadComponentAsync(drawableStoryboard, onStoryboardCreated); else - Add(drawableStoryboard); + onStoryboardCreated(drawableStoryboard); + } + + private void onStoryboardCreated(DrawableStoryboard storyboard) + { + Add(storyboard); + OverlayLayerContainer.Add(storyboard.OverlayLayer.CreateProxy()); } } } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 1ec3a69b24..77da038ab3 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -264,6 +264,7 @@ namespace osu.Game.Screens.Play { target.AddRange(new[] { + DimmableStoryboard.OverlayLayerContainer.CreateProxy(), BreakOverlay = new BreakOverlay(working.Beatmap.BeatmapInfo.LetterboxInBreaks, ScoreProcessor) { Clock = DrawableRuleset.FrameStableClock, From 963806474148d80ddc706e0f47f8bf5d8a22e6a9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 May 2020 10:06:23 +0900 Subject: [PATCH 0803/1117] Tidy up ruleset assignment code --- osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs index 06e82394ec..6ada632850 100644 --- a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs +++ b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs @@ -101,9 +101,6 @@ namespace osu.Game.Tests.Beatmaps { var beatmap = GetBeatmap(name); - var rulesetInstance = CreateRuleset(); - beatmap.BeatmapInfo.Ruleset = beatmap.BeatmapInfo.RulesetID == rulesetInstance.RulesetInfo.ID ? rulesetInstance.RulesetInfo : new RulesetInfo(); - var converterResult = new Dictionary>(); var working = new ConversionWorkingBeatmap(beatmap) @@ -115,7 +112,7 @@ namespace osu.Game.Tests.Beatmaps } }; - working.GetPlayableBeatmap(rulesetInstance.RulesetInfo, mods); + working.GetPlayableBeatmap(CreateRuleset().RulesetInfo, mods); return new ConvertResult { @@ -152,8 +149,8 @@ namespace osu.Game.Tests.Beatmaps ((LegacyBeatmapDecoder)decoder).ApplyOffsets = false; var beatmap = decoder.Decode(stream); - // not sure but seems to be required. - beatmap.BeatmapInfo.Ruleset = CreateRuleset().RulesetInfo; + var rulesetInstance = CreateRuleset(); + beatmap.BeatmapInfo.Ruleset = beatmap.BeatmapInfo.RulesetID == rulesetInstance.RulesetInfo.ID ? rulesetInstance.RulesetInfo : new RulesetInfo(); return beatmap; } From 76080368e900f2b866f46235eebff0f7bda19a6d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 May 2020 10:14:08 +0900 Subject: [PATCH 0804/1117] Mark test as headless --- osu.Game.Rulesets.Taiko.Tests/TestSceneSampleOutput.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneSampleOutput.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneSampleOutput.cs index 564ab91291..d541aa8de8 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneSampleOutput.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneSampleOutput.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Linq; +using osu.Framework.Testing; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Rulesets.Taiko.Objects.Drawables; @@ -12,6 +13,7 @@ namespace osu.Game.Rulesets.Taiko.Tests /// /// Taiko has some interesting rules for legacy mappings. /// + [HeadlessTest] public class TestSceneSampleOutput : PlayerTestScene { public TestSceneSampleOutput() From d31a59b07466eae570e1127624c30286666339b4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 May 2020 14:55:36 +0900 Subject: [PATCH 0805/1117] Fix logic results in infinite loop on default timing point return --- osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs index 98e15e3fa8..05990eadd7 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs @@ -126,7 +126,7 @@ namespace osu.Game.Rulesets.Mania.Edit var nextTimingPoint = beatmap.ControlPointInfo.TimingPointAt(time); // switch to the next timing point if we have reached it. - if (nextTimingPoint != timingPoint) + if (nextTimingPoint.Time > timingPoint.Time) { beat = 0; time = nextTimingPoint.Time; From 0bc3073d49aeff5ed5647fca342f158725222510 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 May 2020 15:01:32 +0900 Subject: [PATCH 0806/1117] Fix test failures --- .../TestSceneManiaHitObjectComposer.cs | 14 +++++++++----- osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs index 6274bb1005..bad3d7854e 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs @@ -42,6 +42,7 @@ namespace osu.Game.Rulesets.Mania.Tests public void TestDragOffscreenSelectionVerticallyUpScroll() { DrawableHitObject lastObject = null; + double originalTime = 0; Vector2 originalPosition = Vector2.Zero; setScrollStep(ScrollingDirection.Up); @@ -49,6 +50,7 @@ namespace osu.Game.Rulesets.Mania.Tests AddStep("seek to last object", () => { lastObject = this.ChildrenOfType().Single(d => d.HitObject == composer.EditorBeatmap.HitObjects.Last()); + originalTime = lastObject.HitObject.StartTime; Clock.Seek(composer.EditorBeatmap.HitObjects.Last().StartTime); }); @@ -64,19 +66,20 @@ namespace osu.Game.Rulesets.Mania.Tests AddStep("move mouse downwards", () => { - InputManager.MoveMouseTo(lastObject, new Vector2(0, 20)); + InputManager.MoveMouseTo(lastObject, new Vector2(0, lastObject.ScreenSpaceDrawQuad.Height * 2)); InputManager.ReleaseButton(MouseButton.Left); }); AddAssert("hitobjects not moved columns", () => composer.EditorBeatmap.HitObjects.All(h => ((ManiaHitObject)h).Column == 0)); AddAssert("hitobjects moved downwards", () => lastObject.DrawPosition.Y - originalPosition.Y > 0); - AddAssert("hitobjects not moved too far", () => lastObject.DrawPosition.Y - originalPosition.Y < 50); + AddAssert("hitobject has moved time", () => lastObject.HitObject.StartTime == originalTime + 125); } [Test] public void TestDragOffscreenSelectionVerticallyDownScroll() { DrawableHitObject lastObject = null; + double originalTime = 0; Vector2 originalPosition = Vector2.Zero; setScrollStep(ScrollingDirection.Down); @@ -84,6 +87,7 @@ namespace osu.Game.Rulesets.Mania.Tests AddStep("seek to last object", () => { lastObject = this.ChildrenOfType().Single(d => d.HitObject == composer.EditorBeatmap.HitObjects.Last()); + originalTime = lastObject.HitObject.StartTime; Clock.Seek(composer.EditorBeatmap.HitObjects.Last().StartTime); }); @@ -99,13 +103,13 @@ namespace osu.Game.Rulesets.Mania.Tests AddStep("move mouse upwards", () => { - InputManager.MoveMouseTo(lastObject, new Vector2(0, -20)); + InputManager.MoveMouseTo(lastObject, new Vector2(0, -lastObject.ScreenSpaceDrawQuad.Height * 2)); InputManager.ReleaseButton(MouseButton.Left); }); AddAssert("hitobjects not moved columns", () => composer.EditorBeatmap.HitObjects.All(h => ((ManiaHitObject)h).Column == 0)); AddAssert("hitobjects moved upwards", () => originalPosition.Y - lastObject.DrawPosition.Y > 0); - AddAssert("hitobjects not moved too far", () => originalPosition.Y - lastObject.DrawPosition.Y < 50); + AddAssert("hitobject has moved time", () => lastObject.HitObject.StartTime == originalTime + 125); } [Test] @@ -207,7 +211,7 @@ namespace osu.Game.Rulesets.Mania.Tests }; for (int i = 0; i < 10; i++) - EditorBeatmap.Add(new Note { StartTime = 100 * i }); + EditorBeatmap.Add(new Note { StartTime = 125 * i }); } } } diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs index 05990eadd7..fa8f8a755a 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs @@ -180,7 +180,7 @@ namespace osu.Game.Rulesets.Mania.Edit foreach (var grid in grids) { - foreach (var line in grid.AliveObjects.OfType()) + foreach (var line in grid.Objects.OfType()) { Vector2 linePos = line.ToSpaceOfOtherDrawable(line.OriginPosition, this); float d = Vector2.Distance(position, linePos); From 85088c9b3baa96ece3ad5a1404585eeb23e5c8bb Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 20 May 2020 15:08:33 +0900 Subject: [PATCH 0807/1117] Privatise setter --- osu.Game/Screens/Play/DimmableStoryboard.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/DimmableStoryboard.cs b/osu.Game/Screens/Play/DimmableStoryboard.cs index 74c84f648c..58eb95b7c6 100644 --- a/osu.Game/Screens/Play/DimmableStoryboard.cs +++ b/osu.Game/Screens/Play/DimmableStoryboard.cs @@ -14,7 +14,7 @@ namespace osu.Game.Screens.Play ///