diff --git a/.gitmodules b/.gitmodules index f1c4f5d172..e69de29bb2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "osu-resources"] - path = osu-resources - url = https://github.com/ppy/osu-resources \ No newline at end of file diff --git a/README.md b/README.md index a54b28b74a..94ab385bfd 100644 --- a/README.md +++ b/README.md @@ -31,18 +31,14 @@ If your platform is not listed above, there is still a chance you can manually b Clone the repository **including submodules**: ```shell -git clone --recurse-submodules https://github.com/ppy/osu +git clone https://github.com/ppy/osu cd osu ``` -> If you forgot the `--recurse-submodules` option, run this command inside the `osu` directory: -> -> `git submodule update --init --recursive` - To update the source code to the latest commit, run the following command inside the `osu` directory: ```shell -git pull --recurse-submodules +git pull ``` ## Building @@ -73,6 +69,10 @@ For example, you can run osu! with the following command: LD_LIBRARY_PATH="$(pwd)/osu.Desktop/bin/Debug/netcoreapp2.2" dotnet run --project osu.Desktop ``` +## Testing with resource/framework modifications + +Sometimes it may be necessary to cross-test changes in [osu-resources](https://github.com/ppy/osu-resources) or [osu-framework](https://github.com/ppy/osu-framework). This can be achieved by running some commands as documented on the [osu-resources](https://github.com/ppy/osu-resources/wiki/Testing-local-resources-checkout-with-other-projects) and [osu-framework](https://github.com/ppy/osu-framework/wiki/Testing-local-framework-checkout-with-other-projects) wiki pages. + ## Code analysis Code analysis can be run with `powershell ./build.ps1` or `build.sh`. This is currently only supported under windows due to [resharper cli shortcomings](https://youtrack.jetbrains.com/issue/RSRP-410004). Alternative, you can install resharper or use rider to get inline support in your IDE of choice. diff --git a/osu-resources b/osu-resources deleted file mode 160000 index 677897728f..0000000000 --- a/osu-resources +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 677897728f4332fa1200e0280ca02c4b987c6c47 diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index c4c0cc1fdd..ef8ec0c105 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -16,7 +16,6 @@ using osu.Desktop.Updater; using osu.Framework; using osu.Framework.Platform.Windows; using osu.Framework.Screens; -using osu.Game.Screens; using osu.Game.Screens.Menu; namespace osu.Desktop @@ -63,9 +62,10 @@ namespace osu.Desktop } } - protected override void ScreenChanged(OsuScreen current, Screen newScreen) + protected override void ScreenChanged(IScreen lastScreen, IScreen newScreen) { - base.ScreenChanged(current, newScreen); + base.ScreenChanged(lastScreen, newScreen); + switch (newScreen) { case Intro _: diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 4f0ae3d65d..874f73da6d 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -22,7 +22,6 @@ - diff --git a/osu.Game.Rulesets.Catch.Tests.iOS/osu.Game.Rulesets.Catch.Tests.iOS.csproj b/osu.Game.Rulesets.Catch.Tests.iOS/osu.Game.Rulesets.Catch.Tests.iOS.csproj index da053f7598..37e7c45a4e 100644 --- a/osu.Game.Rulesets.Catch.Tests.iOS/osu.Game.Rulesets.Catch.Tests.iOS.csproj +++ b/osu.Game.Rulesets.Catch.Tests.iOS/osu.Game.Rulesets.Catch.Tests.iOS.csproj @@ -39,10 +39,6 @@ {58F6C80C-1253-4A0E-A465-B8C85EBEADF3} osu.Game.Rulesets.Catch - - {D9A367C9-4C1A-489F-9B05-A0CEA2B53B58} - osu.Game.Resources - diff --git a/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs index fd30a8d4ae..d40a108c5e 100644 --- a/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs @@ -21,6 +21,7 @@ namespace osu.Game.Rulesets.Catch.Tests [TestCase("basic")] [TestCase("spinner")] [TestCase("spinner-and-circles")] + [TestCase("slider")] public new void Test(string name) { base.Test(name); diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs index ef7573c70b..61bb4335f3 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs @@ -55,6 +55,13 @@ namespace osu.Game.Rulesets.Catch.Objects var minDistanceFromEnd = Velocity * 0.01; + var tickSamples = Samples.Select(s => new SampleInfo + { + Bank = s.Bank, + Name = @"slidertick", + Volume = s.Volume + }).ToList(); + AddNested(new Fruit { Samples = Samples, @@ -62,15 +69,22 @@ namespace osu.Game.Rulesets.Catch.Objects X = X }); - double lastDropletTime = StartTime; + double lastTickTime = StartTime; for (int span = 0; span < this.SpanCount(); span++) { var spanStartTime = StartTime + span * spanDuration; var reversed = span % 2 == 1; - for (double d = 0; d <= length; d += tickDistance) + for (double d = tickDistance;; d += tickDistance) { + bool isLastTick = false; + if (d + minDistanceFromEnd >= length) + { + d = length; + isLastTick = true; + } + var timeProgress = d / length; var distanceProgress = reversed ? 1 - timeProgress : timeProgress; @@ -79,47 +93,42 @@ namespace osu.Game.Rulesets.Catch.Objects if (LegacyLastTickOffset != null) { // If we're the last tick, apply the legacy offset - if (span == this.SpanCount() - 1 && d + tickDistance > length) + if (span == this.SpanCount() - 1 && isLastTick) time = Math.Max(StartTime + Duration / 2, time - LegacyLastTickOffset.Value); } - double tinyTickInterval = time - lastDropletTime; - while (tinyTickInterval > 100) - tinyTickInterval /= 2; - - for (double t = lastDropletTime + tinyTickInterval; t < time; t += tinyTickInterval) + int tinyTickCount = 1; + double tinyTickInterval = time - lastTickTime; + while (tinyTickInterval > 100 && tinyTickCount < 10000) { + tinyTickInterval /= 2; + tinyTickCount *= 2; + } + + for (int tinyTickIndex = 0; tinyTickIndex < tinyTickCount - 1; tinyTickIndex++) + { + var t = lastTickTime + (tinyTickIndex + 1) * tinyTickInterval; double progress = reversed ? 1 - (t - spanStartTime) / spanDuration : (t - spanStartTime) / spanDuration; AddNested(new TinyDroplet { StartTime = t, X = X + Path.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH, - Samples = new List(Samples.Select(s => new SampleInfo - { - Bank = s.Bank, - Name = @"slidertick", - Volume = s.Volume - })) + Samples = tickSamples }); } - if (d > minDistanceFromEnd && Math.Abs(d - length) > minDistanceFromEnd) + lastTickTime = time; + + if (isLastTick) + break; + + AddNested(new Droplet { - AddNested(new Droplet - { - StartTime = time, - X = X + Path.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH, - Samples = new List(Samples.Select(s => new SampleInfo - { - Bank = s.Bank, - Name = @"slidertick", - Volume = s.Volume - })) - }); - } - - lastDropletTime = time; + StartTime = time, + X = X + Path.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH, + Samples = tickSamples + }); } AddNested(new Fruit diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/slider-expected-conversion.json b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/slider-expected-conversion.json new file mode 100644 index 0000000000..58c52b6867 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/slider-expected-conversion.json @@ -0,0 +1 @@ +{"Mappings":[{"StartTime":19184.0,"Objects":[{"StartTime":19184.0,"Position":320.0},{"StartTime":19263.0,"Position":311.730255},{"StartTime":19343.0,"Position":324.6205},{"StartTime":19423.0,"Position":343.0907},{"StartTime":19503.0,"Position":372.2917},{"StartTime":19582.0,"Position":385.194733},{"StartTime":19662.0,"Position":379.0426},{"StartTime":19742.0,"Position":385.1066},{"StartTime":19822.0,"Position":391.624664},{"StartTime":19919.0,"Position":386.27832},{"StartTime":20016.0,"Position":380.117035},{"StartTime":20113.0,"Position":381.664154},{"StartTime":20247.0,"Position":370.872864}]}]} \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/slider.osu b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/slider.osu new file mode 100644 index 0000000000..d48b2d7769 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/slider.osu @@ -0,0 +1,18 @@ +osu file format v14 + +[General] +Mode: 2 + +[Difficulty] +HPDrainRate:3 +CircleSize:2 +OverallDifficulty:4 +ApproachRate:4 +SliderMultiplier:0.9 +SliderTickRate:1 + +[TimingPoints] +35.4473684210527,638.298947368422,4,2,1,60,1,0 + +[HitObjects] +320,176,19184,2,8,P|384:168|368:232,1,150 diff --git a/osu.Game.Rulesets.Mania.Tests.iOS/osu.Game.Rulesets.Mania.Tests.iOS.csproj b/osu.Game.Rulesets.Mania.Tests.iOS/osu.Game.Rulesets.Mania.Tests.iOS.csproj index 45ed2091a7..24abccb19d 100644 --- a/osu.Game.Rulesets.Mania.Tests.iOS/osu.Game.Rulesets.Mania.Tests.iOS.csproj +++ b/osu.Game.Rulesets.Mania.Tests.iOS/osu.Game.Rulesets.Mania.Tests.iOS.csproj @@ -39,10 +39,6 @@ {48F4582B-7687-4621-9CBE-5C24197CB536} osu.Game.Rulesets.Mania - - {D9A367C9-4C1A-489F-9B05-A0CEA2B53B58} - osu.Game.Resources - diff --git a/osu.Game.Rulesets.Osu.Tests.iOS/osu.Game.Rulesets.Osu.Tests.iOS.csproj b/osu.Game.Rulesets.Osu.Tests.iOS/osu.Game.Rulesets.Osu.Tests.iOS.csproj index 349e46e02d..9930a166e3 100644 --- a/osu.Game.Rulesets.Osu.Tests.iOS/osu.Game.Rulesets.Osu.Tests.iOS.csproj +++ b/osu.Game.Rulesets.Osu.Tests.iOS/osu.Game.Rulesets.Osu.Tests.iOS.csproj @@ -39,10 +39,6 @@ {C92A607B-1FDD-4954-9F92-03FF547D9080} osu.Game.Rulesets.Osu - - {D9A367C9-4C1A-489F-9B05-A0CEA2B53B58} - osu.Game.Resources - diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 83deac78a1..13a21c5c55 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -88,7 +88,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty private double computeAimValue() { - double aimValue = Math.Pow(5.0f * Math.Max(1.0f, Attributes.AimStrain / 0.0675f) - 4.0f, 3.0f) / 100000.0f; + double rawAim = Attributes.AimStrain; + + if (mods.Any(m => m is OsuModTouchDevice)) + rawAim = Math.Pow(rawAim, 0.8); + + double aimValue = Math.Pow(5.0f * Math.Max(1.0f, rawAim / 0.0675f) - 4.0f, 3.0f) / 100000.0f; // Longer maps are worth more double lengthBonus = 0.95f + 0.4f * Math.Min(1.0f, totalHits / 2000.0f) + diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyBeatmap.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyBeatmap.cs index 1736912e1f..068564d50c 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyBeatmap.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyBeatmap.cs @@ -38,7 +38,13 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing // The first jump is formed by the first two hitobjects of the map. // If the map has less than two OsuHitObjects, the enumerator will not return anything. for (int i = 1; i < objects.Count; i++) - yield return new OsuDifficultyHitObject(objects[i], objects[i - 1], timeRate); + { + var lastLast = i > 1 ? objects[i - 2] : null; + var last = objects[i - 1]; + var current = objects[i]; + + yield return new OsuDifficultyHitObject(lastLast, last, current, timeRate); + } } } } diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs index 91d6c3d7a3..4e9ac26dc5 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs @@ -40,14 +40,22 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing /// public double StrainTime { get; private set; } + /// + /// Angle the player has to take to hit this . + /// Calculated as the angle between the circles (current-2, current-1, current). + /// + public double? Angle { get; private set; } + + private readonly OsuHitObject lastLastObject; private readonly OsuHitObject lastObject; private readonly double timeRate; /// /// Initializes the object calculating extra data required for difficulty calculation. /// - public OsuDifficultyHitObject(OsuHitObject currentObject, OsuHitObject lastObject, double timeRate) + public OsuDifficultyHitObject(OsuHitObject lastLastObject, OsuHitObject lastObject, OsuHitObject currentObject, double timeRate) { + this.lastLastObject = lastLastObject; this.lastObject = lastObject; this.timeRate = timeRate; @@ -68,20 +76,30 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing scalingFactor *= 1 + smallCircleBonus; } - Vector2 lastCursorPosition = lastObject.StackedPosition; - - var lastSlider = lastObject as Slider; - if (lastSlider != null) + if (lastObject is Slider lastSlider) { computeSliderCursorPosition(lastSlider); - lastCursorPosition = lastSlider.LazyEndPosition ?? lastCursorPosition; - TravelDistance = lastSlider.LazyTravelDistance * scalingFactor; } + Vector2 lastCursorPosition = getEndCursorPosition(lastObject); + // Don't need to jump to reach spinners if (!(BaseObject is Spinner)) JumpDistance = (BaseObject.StackedPosition * scalingFactor - lastCursorPosition * scalingFactor).Length; + + if (lastLastObject != null) + { + Vector2 lastLastCursorPosition = getEndCursorPosition(lastLastObject); + + Vector2 v1 = lastLastCursorPosition - lastObject.StackedPosition; + Vector2 v2 = BaseObject.StackedPosition - lastCursorPosition; + + float dot = Vector2.Dot(v1, v2); + float det = v1.X * v2.Y - v1.Y * v2.X; + + Angle = Math.Abs(Math.Atan2(det, dot)); + } } private void setTimingValues() @@ -127,5 +145,19 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing computeVertex(time); computeVertex(slider.EndTime); } + + private Vector2 getEndCursorPosition(OsuHitObject hitObject) + { + Vector2 pos = hitObject.StackedPosition; + + var slider = hitObject as Slider; + if (slider != null) + { + computeSliderCursorPosition(slider); + pos = slider.LazyEndPosition ?? pos; + } + + return pos; + } } } diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs index 9e5f13de62..b5e57985e9 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs @@ -11,10 +11,39 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills /// public class Aim : Skill { + private const double angle_bonus_begin = Math.PI / 3; + private const double timing_threshold = 107; + protected override double SkillMultiplier => 26.25; protected override double StrainDecayBase => 0.15; protected override double StrainValueOf(OsuDifficultyHitObject current) - => (Math.Pow(current.TravelDistance, 0.99) + Math.Pow(current.JumpDistance, 0.99)) / current.StrainTime; + { + double result = 0; + + const double scale = 90; + + double applyDiminishingExp(double val) => Math.Pow(val, 0.99); + + if (Previous.Count > 0) + { + if (current.Angle != null && current.Angle.Value > angle_bonus_begin) + { + var angleBonus = Math.Sqrt( + Math.Max(Previous[0].JumpDistance - scale, 0) + * Math.Pow(Math.Sin(current.Angle.Value - angle_bonus_begin), 2) + * Math.Max(current.JumpDistance - scale, 0)); + result = 1.5 * applyDiminishingExp(Math.Max(0, angleBonus)) / Math.Max(timing_threshold, Previous[0].StrainTime); + } + } + + double jumpDistanceExp = applyDiminishingExp(current.JumpDistance); + double travelDistanceExp = applyDiminishingExp(current.TravelDistance); + + return Math.Max( + result + (jumpDistanceExp + travelDistanceExp + Math.Sqrt(travelDistanceExp * jumpDistanceExp)) / Math.Max(current.StrainTime, timing_threshold), + (Math.Sqrt(travelDistanceExp * jumpDistanceExp) + jumpDistanceExp + travelDistanceExp) / current.StrainTime + ); + } } } diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Skill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Skill.cs index 436d3d0853..2f23552eb9 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Skill.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Skill.cs @@ -15,6 +15,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills /// public abstract class Skill { + protected const double SINGLE_SPACING_THRESHOLD = 125; + protected const double STREAM_SPACING_THRESHOLD = 110; + /// /// Strain values are multiplied by this number for the given skill. Used to balance the value of different skills between each other. /// diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 809632806b..e78691ce53 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.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.Rulesets.Osu.Difficulty.Preprocessing; namespace osu.Game.Rulesets.Osu.Difficulty.Skills @@ -10,30 +11,41 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills /// public class Speed : Skill { + private const double angle_bonus_begin = 5 * Math.PI / 6; + private const double pi_over_4 = Math.PI / 4; + private const double pi_over_2 = Math.PI / 2; + protected override double SkillMultiplier => 1400; protected override double StrainDecayBase => 0.3; - private const double single_spacing_threshold = 125; - private const double stream_spacing_threshold = 110; - private const double almost_diameter = 90; + private const double min_speed_bonus = 75; // ~200BPM + private const double max_speed_bonus = 45; // ~330BPM + private const double speed_balancing_factor = 40; protected override double StrainValueOf(OsuDifficultyHitObject current) { - double distance = current.TravelDistance + current.JumpDistance; + double distance = Math.Min(SINGLE_SPACING_THRESHOLD, current.TravelDistance + current.JumpDistance); + double deltaTime = Math.Max(max_speed_bonus, current.DeltaTime); - double speedValue; - if (distance > single_spacing_threshold) - speedValue = 2.5; - else if (distance > stream_spacing_threshold) - speedValue = 1.6 + 0.9 * (distance - stream_spacing_threshold) / (single_spacing_threshold - stream_spacing_threshold); - else if (distance > almost_diameter) - speedValue = 1.2 + 0.4 * (distance - almost_diameter) / (stream_spacing_threshold - almost_diameter); - else if (distance > almost_diameter / 2) - speedValue = 0.95 + 0.25 * (distance - almost_diameter / 2) / (almost_diameter / 2); - else - speedValue = 0.95; + double speedBonus = 1.0; + if (deltaTime < min_speed_bonus) + speedBonus = 1 + Math.Pow((min_speed_bonus - deltaTime) / speed_balancing_factor, 2); - return speedValue / current.StrainTime; + double angleBonus = 1.0; + if (current.Angle != null && current.Angle.Value < angle_bonus_begin) + { + angleBonus = 1 + Math.Pow(Math.Sin(1.5 * (angle_bonus_begin - current.Angle.Value)), 2) / 3.57; + if (current.Angle.Value < pi_over_2) + { + angleBonus = 1.28; + if (distance < 90 && current.Angle.Value < pi_over_4) + angleBonus += (1 - angleBonus) * Math.Min((90 - distance) / 10, 1); + else if (distance < 90) + angleBonus += (1 - angleBonus) * Math.Min((90 - distance) / 10, 1) * Math.Sin((pi_over_2 - current.Angle.Value) / pi_over_4); + } + } + + return (1 + (speedBonus - 1) * 0.75) * angleBonus * (0.95 + speedBonus * Math.Pow(distance / SINGLE_SPACING_THRESHOLD, 3.5)) / current.StrainTime; } } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs new file mode 100644 index 0000000000..571756d056 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.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.Mods; + +namespace osu.Game.Rulesets.Osu.Mods +{ + public class OsuModTouchDevice : Mod + { + public override string Name => "Touch Device"; + public override string Acronym => "TD"; + public override double ScoreMultiplier => 1; + + public override bool Ranked => true; + } +} diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 9415adc411..12d0a28a8f 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -82,6 +82,9 @@ namespace osu.Game.Rulesets.Osu if (mods.HasFlag(LegacyMods.Target)) yield return new OsuModTarget(); + + if (mods.HasFlag(LegacyMods.TouchDevice)) + yield return new OsuModTouchDevice(); } public override IEnumerable GetModsFor(ModType type) diff --git a/osu.Game.Rulesets.Taiko.Tests.iOS/osu.Game.Rulesets.Taiko.Tests.iOS.csproj b/osu.Game.Rulesets.Taiko.Tests.iOS/osu.Game.Rulesets.Taiko.Tests.iOS.csproj index 2ab0633786..d2817b743c 100644 --- a/osu.Game.Rulesets.Taiko.Tests.iOS/osu.Game.Rulesets.Taiko.Tests.iOS.csproj +++ b/osu.Game.Rulesets.Taiko.Tests.iOS/osu.Game.Rulesets.Taiko.Tests.iOS.csproj @@ -39,10 +39,6 @@ {F167E17A-7DE6-4AF5-B920-A5112296C695} osu.Game.Rulesets.Taiko - - {D9A367C9-4C1A-489F-9B05-A0CEA2B53B58} - osu.Game.Resources - diff --git a/osu.Game.Tests.iOS/osu.Game.Tests.iOS.csproj b/osu.Game.Tests.iOS/osu.Game.Tests.iOS.csproj index 636bedac13..ea5ab699f3 100644 --- a/osu.Game.Tests.iOS/osu.Game.Tests.iOS.csproj +++ b/osu.Game.Tests.iOS/osu.Game.Tests.iOS.csproj @@ -51,10 +51,6 @@ {F167E17A-7DE6-4AF5-B920-A5112296C695} osu.Game.Rulesets.Taiko - - {D9A367C9-4C1A-489F-9B05-A0CEA2B53B58} - osu.Game.Resources - diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index 1221212f94..171ba91ada 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -27,7 +27,7 @@ namespace osu.Game.Tests.Beatmaps.Formats [Test] public void TestDecodeBeatmapVersion() { - using (var resStream = Resource.OpenResource("beatmap-version.osu")) + using (var resStream = TestResources.OpenResource("beatmap-version.osu")) using (var stream = new StreamReader(resStream)) { var decoder = Decoder.GetDecoder(stream); @@ -47,7 +47,7 @@ namespace osu.Game.Tests.Beatmaps.Formats public void TestDecodeBeatmapGeneral() { var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; - using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) + using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var stream = new StreamReader(resStream)) { var beatmap = decoder.Decode(stream); @@ -70,7 +70,7 @@ namespace osu.Game.Tests.Beatmaps.Formats public void TestDecodeBeatmapEditor() { var decoder = new LegacyBeatmapDecoder(); - using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) + using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var stream = new StreamReader(resStream)) { var beatmapInfo = decoder.Decode(stream).BeatmapInfo; @@ -95,7 +95,7 @@ namespace osu.Game.Tests.Beatmaps.Formats public void TestDecodeBeatmapMetadata() { var decoder = new LegacyBeatmapDecoder(); - using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) + using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var stream = new StreamReader(resStream)) { var beatmap = decoder.Decode(stream); @@ -119,7 +119,7 @@ namespace osu.Game.Tests.Beatmaps.Formats public void TestDecodeBeatmapDifficulty() { var decoder = new LegacyBeatmapDecoder(); - using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) + using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var stream = new StreamReader(resStream)) { var difficulty = decoder.Decode(stream).BeatmapInfo.BaseDifficulty; @@ -137,7 +137,7 @@ namespace osu.Game.Tests.Beatmaps.Formats public void TestDecodeBeatmapEvents() { var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; - using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) + using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var stream = new StreamReader(resStream)) { var beatmap = decoder.Decode(stream); @@ -155,7 +155,7 @@ namespace osu.Game.Tests.Beatmaps.Formats public void TestDecodeBeatmapTimingPoints() { var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; - using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) + using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var stream = new StreamReader(resStream)) { var beatmap = decoder.Decode(stream); @@ -190,7 +190,7 @@ namespace osu.Game.Tests.Beatmaps.Formats public void TestDecodeBeatmapColours() { var decoder = new LegacySkinDecoder(); - using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) + using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var stream = new StreamReader(resStream)) { var comboColors = decoder.Decode(stream).ComboColours; @@ -215,7 +215,7 @@ namespace osu.Game.Tests.Beatmaps.Formats public void TestDecodeBeatmapComboOffsetsOsu() { var decoder = new LegacyBeatmapDecoder(); - using (var resStream = Resource.OpenResource("hitobject-combo-offset.osu")) + using (var resStream = TestResources.OpenResource("hitobject-combo-offset.osu")) using (var stream = new StreamReader(resStream)) { var beatmap = decoder.Decode(stream); @@ -237,7 +237,7 @@ namespace osu.Game.Tests.Beatmaps.Formats public void TestDecodeBeatmapComboOffsetsCatch() { var decoder = new LegacyBeatmapDecoder(); - using (var resStream = Resource.OpenResource("hitobject-combo-offset.osu")) + using (var resStream = TestResources.OpenResource("hitobject-combo-offset.osu")) using (var stream = new StreamReader(resStream)) { var beatmap = decoder.Decode(stream); @@ -259,7 +259,7 @@ namespace osu.Game.Tests.Beatmaps.Formats public void TestDecodeBeatmapHitObjects() { var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; - using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) + using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var stream = new StreamReader(resStream)) { var hitObjects = decoder.Decode(stream).HitObjects; @@ -286,7 +286,7 @@ namespace osu.Game.Tests.Beatmaps.Formats public void TestDecodeControlPointCustomSampleBank() { var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; - using (var resStream = Resource.OpenResource("controlpoint-custom-samplebank.osu")) + using (var resStream = TestResources.OpenResource("controlpoint-custom-samplebank.osu")) using (var stream = new StreamReader(resStream)) { var hitObjects = decoder.Decode(stream).HitObjects; @@ -307,7 +307,7 @@ namespace osu.Game.Tests.Beatmaps.Formats public void TestDecodeHitObjectCustomSampleBank() { var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; - using (var resStream = Resource.OpenResource("hitobject-custom-samplebank.osu")) + using (var resStream = TestResources.OpenResource("hitobject-custom-samplebank.osu")) using (var stream = new StreamReader(resStream)) { var hitObjects = decoder.Decode(stream).HitObjects; @@ -324,7 +324,7 @@ namespace osu.Game.Tests.Beatmaps.Formats public void TestDecodeHitObjectFileSamples() { var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; - using (var resStream = Resource.OpenResource("hitobject-file-samples.osu")) + using (var resStream = TestResources.OpenResource("hitobject-file-samples.osu")) using (var stream = new StreamReader(resStream)) { var hitObjects = decoder.Decode(stream).HitObjects; @@ -342,7 +342,7 @@ namespace osu.Game.Tests.Beatmaps.Formats public void TestDecodeSliderSamples() { var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; - using (var resStream = Resource.OpenResource("slider-samples.osu")) + using (var resStream = TestResources.OpenResource("slider-samples.osu")) using (var stream = new StreamReader(resStream)) { var hitObjects = decoder.Decode(stream).HitObjects; @@ -385,7 +385,7 @@ namespace osu.Game.Tests.Beatmaps.Formats public void TestDecodeHitObjectNullAdditionBank() { var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; - using (var resStream = Resource.OpenResource("hitobject-no-addition-bank.osu")) + using (var resStream = TestResources.OpenResource("hitobject-no-addition-bank.osu")) using (var stream = new StreamReader(resStream)) { var hitObjects = decoder.Decode(stream).HitObjects; diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs index 5049349999..136d1de930 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs @@ -19,7 +19,7 @@ namespace osu.Game.Tests.Beatmaps.Formats public void TestDecodeStoryboardEvents() { var decoder = new LegacyStoryboardDecoder(); - using (var resStream = Resource.OpenResource("Himeringo - Yotsuya-san ni Yoroshiku (RLC) [Winber1's Extreme].osu")) + using (var resStream = TestResources.OpenResource("Himeringo - Yotsuya-san ni Yoroshiku (RLC) [Winber1's Extreme].osu")) using (var stream = new StreamReader(resStream)) { var storyboard = decoder.Decode(stream); @@ -91,7 +91,7 @@ namespace osu.Game.Tests.Beatmaps.Formats public void TestDecodeVariableWithSuffix() { var decoder = new LegacyStoryboardDecoder(); - using (var resStream = Resource.OpenResource("variable-with-suffix.osb")) + using (var resStream = TestResources.OpenResource("variable-with-suffix.osb")) using (var stream = new StreamReader(resStream)) { var storyboard = decoder.Decode(stream); diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs index 44291a6805..d5ab0e43d1 100644 --- a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs @@ -146,7 +146,7 @@ namespace osu.Game.Tests.Beatmaps.Formats /// The after being decoded by an . private Beatmap decode(string filename, out Beatmap jsonDecoded) { - using (var stream = Resource.OpenResource(filename)) + using (var stream = TestResources.OpenResource(filename)) using (var sr = new StreamReader(stream)) { var legacyDecoded = new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(sr); diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index a159659d35..b6a8b3b06c 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -12,6 +12,7 @@ using osu.Framework.Platform; using osu.Game.IPC; using osu.Framework.Allocation; using osu.Game.Beatmaps; +using osu.Game.Tests.Resources; using SharpCompress.Archives.Zip; namespace osu.Game.Tests.Beatmaps.IO @@ -19,8 +20,6 @@ namespace osu.Game.Tests.Beatmaps.IO [TestFixture] public class ImportBeatmapTest { - public const string TEST_OSZ_PATH = @"../../../../osu-resources/osu.Game.Resources/Beatmaps/241526 Soleily - Renatus.osz"; - [Test] public void TestImportWhenClosed() { @@ -114,7 +113,7 @@ namespace osu.Game.Tests.Beatmaps.IO Assert.AreEqual(0, fireCount -= 2); - var breakTemp = createTemporaryBeatmap(); + var breakTemp = TestResources.GetTestBeatmapForImport(); MemoryStream brokenOsu = new MemoryStream(new byte[] { 1, 3, 3, 7 }); MemoryStream brokenOsz = new MemoryStream(File.ReadAllBytes(breakTemp)); @@ -223,7 +222,7 @@ namespace osu.Game.Tests.Beatmaps.IO var osu = loadOsu(host); - var temp = createTemporaryBeatmap(); + var temp = TestResources.GetTestBeatmapForImport(); var importer = new ArchiveImportIPCChannel(client); if (!importer.ImportAsync(temp).Wait(10000)) @@ -248,7 +247,7 @@ namespace osu.Game.Tests.Beatmaps.IO try { var osu = loadOsu(host); - var temp = createTemporaryBeatmap(); + var temp = TestResources.GetTestBeatmapForImport(); using (File.OpenRead(temp)) osu.Dependencies.Get().Import(temp); ensureLoaded(osu); @@ -262,17 +261,9 @@ namespace osu.Game.Tests.Beatmaps.IO } } - private static string createTemporaryBeatmap() - { - var temp = Path.GetTempFileName() + ".osz"; - File.Copy(TEST_OSZ_PATH, temp, true); - Assert.IsTrue(File.Exists(temp)); - return temp; - } - public static BeatmapSetInfo LoadOszIntoOsu(OsuGameBase osu, string path = null) { - var temp = path ?? createTemporaryBeatmap(); + var temp = path ?? TestResources.GetTestBeatmapForImport(); var manager = osu.Dependencies.Get(); diff --git a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs index aa95e7390f..17197aff27 100644 --- a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs @@ -17,7 +17,7 @@ namespace osu.Game.Tests.Beatmaps.IO [Test] public void TestReadBeatmaps() { - using (var osz = Resource.OpenResource("Beatmaps.241526 Soleily - Renatus.osz")) + using (var osz = TestResources.GetTestBeatmapStream()) { var reader = new ZipArchiveReader(osz); string[] expected = @@ -44,7 +44,7 @@ namespace osu.Game.Tests.Beatmaps.IO [Test] public void TestReadMetadata() { - using (var osz = Resource.OpenResource("Beatmaps.241526 Soleily - Renatus.osz")) + using (var osz = TestResources.GetTestBeatmapStream()) { var reader = new ZipArchiveReader(osz); @@ -72,7 +72,7 @@ namespace osu.Game.Tests.Beatmaps.IO [Test] public void TestReadFile() { - using (var osz = Resource.OpenResource("Beatmaps.241526 Soleily - Renatus.osz")) + using (var osz = TestResources.GetTestBeatmapStream()) { var reader = new ZipArchiveReader(osz); using (var stream = new StreamReader( diff --git a/osu.Game.Tests/Resources/Resource.cs b/osu.Game.Tests/Resources/Resource.cs deleted file mode 100644 index 5405436422..0000000000 --- a/osu.Game.Tests/Resources/Resource.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 System; -using System.IO; -using System.Reflection; - -namespace osu.Game.Tests.Resources -{ - public static class Resource - { - public static Stream OpenResource(string name) - { - var localPath = Path.GetDirectoryName(Uri.UnescapeDataString(new UriBuilder(Assembly.GetExecutingAssembly().CodeBase).Path)); - - return Assembly.GetExecutingAssembly().GetManifestResourceStream($@"osu.Game.Tests.Resources.{name}") ?? - Assembly.LoadFrom(Path.Combine(localPath, @"osu.Game.Resources.dll")).GetManifestResourceStream($@"osu.Game.Resources.{name}"); - } - } -} diff --git a/osu.Game.Tests/Resources/TestResources.cs b/osu.Game.Tests/Resources/TestResources.cs new file mode 100644 index 0000000000..9cb85a63bf --- /dev/null +++ b/osu.Game.Tests/Resources/TestResources.cs @@ -0,0 +1,28 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.IO; +using NUnit.Framework; +using osu.Framework.IO.Stores; + +namespace osu.Game.Tests.Resources +{ + public static class TestResources + { + public static Stream OpenResource(string name) => new DllResourceStore("osu.Game.Tests.dll").GetStream($"Resources/{name}"); + + public static Stream GetTestBeatmapStream() => new DllResourceStore("osu.Game.Resources.dll").GetStream("Beatmaps/241526 Soleily - Renatus.osz"); + + public static string GetTestBeatmapForImport() + { + var temp = Path.GetTempFileName() + ".osz"; + + using (var stream = GetTestBeatmapStream()) + using (var newFile = File.Create(temp)) + stream.CopyTo(newFile); + + Assert.IsTrue(File.Exists(temp)); + return temp; + } + } +} diff --git a/osu.Game.Tests/Scores/IO/ImportScoreTest.cs b/osu.Game.Tests/Scores/IO/ImportScoreTest.cs index f5ebe313dd..e39f18c3cd 100644 --- a/osu.Game.Tests/Scores/IO/ImportScoreTest.cs +++ b/osu.Game.Tests/Scores/IO/ImportScoreTest.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -16,14 +15,13 @@ using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; +using osu.Game.Tests.Resources; using osu.Game.Users; namespace osu.Game.Tests.Scores.IO { public class ImportScoreTest { - public const string TEST_OSZ_PATH = @"../../../../osu-resources/osu.Game.Resources/Beatmaps/241526 Soleily - Renatus.osz"; - [Test] public void TestBasicImport() { @@ -132,21 +130,13 @@ namespace osu.Game.Tests.Scores.IO return scoreManager.GetAllUsableScores().First(); } - private string createTemporaryBeatmap() - { - var temp = Path.GetTempFileName() + ".osz"; - File.Copy(TEST_OSZ_PATH, temp, true); - Assert.IsTrue(File.Exists(temp)); - return temp; - } - 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"); - var beatmapFile = createTemporaryBeatmap(); + var beatmapFile = TestResources.GetTestBeatmapForImport(); var beatmapManager = osu.Dependencies.Get(); beatmapManager.Import(beatmapFile); diff --git a/osu.Game.Tests/Visual/TestCaseLoaderAnimation.cs b/osu.Game.Tests/Visual/TestCaseLoaderAnimation.cs index 70ed075101..2088f97580 100644 --- a/osu.Game.Tests/Visual/TestCaseLoaderAnimation.cs +++ b/osu.Game.Tests/Visual/TestCaseLoaderAnimation.cs @@ -4,6 +4,7 @@ using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; +using osu.Framework.Screens; using osu.Game.Screens; using osu.Game.Screens.Menu; using osuTK.Graphics; @@ -57,7 +58,7 @@ namespace osu.Game.Tests.Visual public OsuLogo Logo; private TestScreen screen; - public bool ScreenLoaded => screen.IsCurrentScreen; + public bool ScreenLoaded => screen.IsCurrentScreen(); public TestLoader(double delay) { @@ -96,7 +97,7 @@ namespace osu.Game.Tests.Visual { public TestScreen() { - Child = new Box + InternalChild = new Box { RelativeSizeAxes = Axes.Both, Colour = Color4.DarkSlateGray, @@ -107,7 +108,7 @@ namespace osu.Game.Tests.Visual protected override void LogoArriving(OsuLogo logo, bool resuming) { base.LogoArriving(logo, resuming); - Child.FadeInFromZero(200); + InternalChild.FadeInFromZero(200); } } } diff --git a/osu.Game.Tests/Visual/TestCaseMultiHeader.cs b/osu.Game.Tests/Visual/TestCaseMultiHeader.cs index 998cdcbad1..f7802e2d08 100644 --- a/osu.Game.Tests/Visual/TestCaseMultiHeader.cs +++ b/osu.Game.Tests/Visual/TestCaseMultiHeader.cs @@ -3,6 +3,7 @@ using NUnit.Framework; using osu.Framework.Graphics; +using osu.Framework.Screens; using osu.Game.Screens; using osu.Game.Screens.Multi; @@ -15,15 +16,15 @@ namespace osu.Game.Tests.Visual { int index = 0; - OsuScreen currentScreen = new TestMultiplayerSubScreen(index); + ScreenStack screenStack = new ScreenStack(new TestMultiplayerSubScreen(index)) { RelativeSizeAxes = Axes.Both }; Children = new Drawable[] { - currentScreen, - new Header(currentScreen) + screenStack, + new Header(screenStack) }; - AddStep("push multi screen", () => currentScreen.Push(currentScreen = new TestMultiplayerSubScreen(++index))); + AddStep("push multi screen", () => screenStack.CurrentScreen.Push(new TestMultiplayerSubScreen(++index))); } private class TestMultiplayerSubScreen : OsuScreen, IMultiplayerSubScreen diff --git a/osu.Game.Tests/Visual/TestCaseMultiScreen.cs b/osu.Game.Tests/Visual/TestCaseMultiScreen.cs index 13419167a7..d83b90d6e1 100644 --- a/osu.Game.Tests/Visual/TestCaseMultiScreen.cs +++ b/osu.Game.Tests/Visual/TestCaseMultiScreen.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using NUnit.Framework; +using osu.Framework.Screens; using osu.Game.Screens.Multi; using osu.Game.Screens.Multi.Lounge; using osu.Game.Screens.Multi.Lounge.Components; diff --git a/osu.Game.Tests/Visual/TestCasePlayerLoader.cs b/osu.Game.Tests/Visual/TestCasePlayerLoader.cs index fd3d838149..82ce7e125a 100644 --- a/osu.Game.Tests/Visual/TestCasePlayerLoader.cs +++ b/osu.Game.Tests/Visual/TestCasePlayerLoader.cs @@ -3,6 +3,7 @@ using System.Threading; using osu.Framework.Allocation; +using osu.Framework.Screens; using osu.Game.Beatmaps; using osu.Game.Screens.Play; @@ -26,7 +27,7 @@ namespace osu.Game.Tests.Visual AddStep("mouse in centre", () => InputManager.MoveMouseTo(loader.ScreenSpaceDrawQuad.Centre)); - AddUntilStep(() => !loader.IsCurrentScreen, "wait for no longer current"); + AddUntilStep(() => !loader.IsCurrentScreen(), "wait for no longer current"); AddStep("load slow dummy beatmap", () => { @@ -42,7 +43,7 @@ namespace osu.Game.Tests.Visual Scheduler.AddDelayed(() => slow.Ready = true, 5000); }); - AddUntilStep(() => !loader.IsCurrentScreen, "wait for no longer current"); + AddUntilStep(() => !loader.IsCurrentScreen(), "wait for no longer current"); } protected class SlowLoadPlayer : Player diff --git a/osu.Game.Tests/Visual/TestCaseScreenBreadcrumbControl.cs b/osu.Game.Tests/Visual/TestCaseScreenBreadcrumbControl.cs index 0f34e4f10a..7202861833 100644 --- a/osu.Game.Tests/Visual/TestCaseScreenBreadcrumbControl.cs +++ b/osu.Game.Tests/Visual/TestCaseScreenBreadcrumbControl.cs @@ -19,16 +19,18 @@ namespace osu.Game.Tests.Visual public class TestCaseScreenBreadcrumbControl : OsuTestCase { private readonly ScreenBreadcrumbControl breadcrumbs; - private Screen currentScreen, changedScreen; + private readonly ScreenStack screenStack; public TestCaseScreenBreadcrumbControl() { - TestScreen startScreen; OsuSpriteText titleText; + IScreen startScreen = new TestScreenOne(); + screenStack = new ScreenStack(startScreen) { RelativeSizeAxes = Axes.Both }; + Children = new Drawable[] { - currentScreen = startScreen = new TestScreenOne(), + screenStack, new FillFlowContainer { RelativeSizeAxes = Axes.X, @@ -37,7 +39,7 @@ namespace osu.Game.Tests.Visual Spacing = new Vector2(10), Children = new Drawable[] { - breadcrumbs = new ScreenBreadcrumbControl(startScreen) + breadcrumbs = new ScreenBreadcrumbControl(screenStack) { RelativeSizeAxes = Axes.X, }, @@ -46,12 +48,7 @@ namespace osu.Game.Tests.Visual }, }; - breadcrumbs.Current.ValueChanged += s => - { - titleText.Text = $"Changed to {s.ToString()}"; - changedScreen = s; - }; - + breadcrumbs.Current.ValueChanged += s => titleText.Text = $"Changed to {s.ToString()}"; breadcrumbs.Current.TriggerChange(); waitForCurrent(); @@ -60,18 +57,14 @@ namespace osu.Game.Tests.Visual pushNext(); waitForCurrent(); - AddStep(@"make start current", () => - { - startScreen.MakeCurrent(); - currentScreen = startScreen; - }); + AddStep(@"make start current", () => startScreen.MakeCurrent()); waitForCurrent(); pushNext(); waitForCurrent(); AddAssert(@"only 2 items", () => breadcrumbs.Items.Count() == 2); - AddStep(@"exit current", () => changedScreen.Exit()); - AddAssert(@"current screen is first", () => startScreen == changedScreen); + AddStep(@"exit current", () => screenStack.CurrentScreen.Exit()); + AddAssert(@"current screen is first", () => startScreen == screenStack.CurrentScreen); } [BackgroundDependencyLoader] @@ -80,8 +73,8 @@ namespace osu.Game.Tests.Visual breadcrumbs.StripColour = colours.Blue; } - private void pushNext() => AddStep(@"push next screen", () => currentScreen = ((TestScreen)currentScreen).PushNext()); - private void waitForCurrent() => AddUntilStep(() => currentScreen.IsCurrentScreen, "current screen"); + private void pushNext() => AddStep(@"push next screen", () => ((TestScreen)screenStack.CurrentScreen).PushNext()); + private void waitForCurrent() => AddUntilStep(() => screenStack.CurrentScreen.IsCurrentScreen(), "current screen"); private abstract class TestScreen : OsuScreen { @@ -91,14 +84,14 @@ namespace osu.Game.Tests.Visual public TestScreen PushNext() { TestScreen screen = CreateNextScreen(); - Push(screen); + this.Push(screen); return screen; } protected TestScreen() { - Child = new FillFlowContainer + InternalChild = new FillFlowContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Tests/WaveformTestBeatmap.cs b/osu.Game.Tests/WaveformTestBeatmap.cs index 72db4b0c17..2028671b0e 100644 --- a/osu.Game.Tests/WaveformTestBeatmap.cs +++ b/osu.Game.Tests/WaveformTestBeatmap.cs @@ -8,7 +8,7 @@ using osu.Framework.Graphics.Textures; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Formats; using osu.Game.IO.Archives; -using osu.Game.Tests.Beatmaps.IO; +using osu.Game.Tests.Resources; namespace osu.Game.Tests { @@ -18,12 +18,12 @@ namespace osu.Game.Tests public class WaveformTestBeatmap : WorkingBeatmap { private readonly ZipArchiveReader reader; - private readonly FileStream stream; + private readonly Stream stream; public WaveformTestBeatmap() : base(new BeatmapInfo()) { - stream = File.OpenRead(ImportBeatmapTest.TEST_OSZ_PATH); + stream = TestResources.GetTestBeatmapStream(); reader = new ZipArchiveReader(stream); } diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 42048692fc..21739f16c2 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -163,18 +163,14 @@ namespace osu.Game.Beatmaps downloadNotification.Progress = progress; }; - request.Success += data => + request.Success += filename => { downloadNotification.Text = $"Importing {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}"; Task.Factory.StartNew(() => { - BeatmapSetInfo importedBeatmap; - // This gets scheduled back to the update thread, but we want the import to run in the background. - using (var stream = new MemoryStream(data)) - using (var archive = new ZipArchiveReader(stream, beatmapSetInfo.ToString())) - importedBeatmap = Import(archive); + var importedBeatmap = Import(filename); downloadNotification.CompletionClickAction = () => { diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 4b6662178f..6bf9e2ff37 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -150,25 +150,9 @@ namespace osu.Game.Database { notification.Text = $"Importing ({++current} of {paths.Length})\n{Path.GetFileName(path)}"; - TModel import; - using (ArchiveReader reader = getReaderFrom(path)) - imported.Add(import = Import(reader)); + imported.Add(Import(path)); notification.Progress = (float)current / paths.Length; - - // We may or may not want to delete the file depending on where it is stored. - // e.g. reconstructing/repairing database with items from default storage. - // Also, not always a single file, i.e. for LegacyFilesystemReader - // TODO: Add a check to prevent files from storage to be deleted. - try - { - if (import != null && File.Exists(path)) - File.Delete(path); - } - catch (Exception e) - { - Logger.Error(e, $@"Could not delete original file after import ({Path.GetFileName(path)})"); - } } catch (Exception e) { @@ -195,6 +179,34 @@ namespace osu.Game.Database } } + /// + /// Import one from the filesystem and delete the file on success. + /// + /// The archive location on disk. + /// The imported model, if successful. + public TModel Import(string path) + { + TModel import; + using (ArchiveReader reader = getReaderFrom(path)) + import = Import(reader); + + // We may or may not want to delete the file depending on where it is stored. + // e.g. reconstructing/repairing database with items from default storage. + // Also, not always a single file, i.e. for LegacyFilesystemReader + // TODO: Add a check to prevent files from storage to be deleted. + try + { + if (import != null && File.Exists(path)) + File.Delete(path); + } + catch (Exception e) + { + Logger.Error(e, $@"Could not delete original file after import ({Path.GetFileName(path)})"); + } + + return import; + } + protected virtual void PresentCompletedImport(IEnumerable imported) { } diff --git a/osu.Game/Graphics/Cursor/MenuCursorContainer.cs b/osu.Game/Graphics/Cursor/MenuCursorContainer.cs index a8870f3cf3..5f9b428dfc 100644 --- a/osu.Game/Graphics/Cursor/MenuCursorContainer.cs +++ b/osu.Game/Graphics/Cursor/MenuCursorContainer.cs @@ -50,6 +50,7 @@ namespace osu.Game.Graphics.Cursor if (!CanShowCursor) { currentTarget?.Cursor?.Hide(); + currentTarget = null; return; } diff --git a/osu.Game/Graphics/UserInterface/ScreenBreadcrumbControl.cs b/osu.Game/Graphics/UserInterface/ScreenBreadcrumbControl.cs index f7c907c5ed..6e01c69df5 100644 --- a/osu.Game/Graphics/UserInterface/ScreenBreadcrumbControl.cs +++ b/osu.Game/Graphics/UserInterface/ScreenBreadcrumbControl.cs @@ -10,45 +10,30 @@ namespace osu.Game.Graphics.UserInterface /// /// A which follows the active screen (and allows navigation) in a stack. /// - public class ScreenBreadcrumbControl : BreadcrumbControl + public class ScreenBreadcrumbControl : BreadcrumbControl { - private Screen last; - - public ScreenBreadcrumbControl(Screen initialScreen) + public ScreenBreadcrumbControl(ScreenStack stack) { - Current.ValueChanged += newScreen => - { - if (last != newScreen && !newScreen.IsCurrentScreen) - newScreen.MakeCurrent(); - }; + stack.ScreenPushed += onPushed; + stack.ScreenExited += onExited; - onPushed(initialScreen); + onPushed(null, stack.CurrentScreen); + + Current.ValueChanged += newScreen => newScreen.MakeCurrent(); } - private void screenChanged(Screen newScreen) + private void onPushed(IScreen lastScreen, IScreen newScreen) { - if (newScreen == null) return; - - if (last != null) - { - last.Exited -= screenChanged; - last.ModePushed -= onPushed; - } - - last = newScreen; - - newScreen.Exited += screenChanged; - newScreen.ModePushed += onPushed; - + AddItem(newScreen); Current.Value = newScreen; } - private void onPushed(Screen screen) + private void onExited(IScreen lastScreen, IScreen newScreen) { - Items.ToList().SkipWhile(i => i != Current.Value).Skip(1).ForEach(RemoveItem); - AddItem(screen); + if (newScreen != null) + Current.Value = newScreen; - screenChanged(screen); + Items.ToList().SkipWhile(s => s != Current.Value).Skip(1).ForEach(RemoveItem); } } } diff --git a/osu.Game/Online/API/APIDownloadRequest.cs b/osu.Game/Online/API/APIDownloadRequest.cs index b9449b57f2..97b869bccd 100644 --- a/osu.Game/Online/API/APIDownloadRequest.cs +++ b/osu.Game/Online/API/APIDownloadRequest.cs @@ -1,15 +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.IO; using osu.Framework.IO.Network; namespace osu.Game.Online.API { public abstract class APIDownloadRequest : APIRequest { + private string filename; + protected override WebRequest CreateWebRequest() { - var request = new WebRequest(Uri); + var request = new FileWebRequest(filename = Path.GetTempFileName(), Uri); request.DownloadProgress += request_Progress; return request; } @@ -23,11 +26,11 @@ namespace osu.Game.Online.API private void onSuccess() { - Success?.Invoke(WebRequest.ResponseData); + Success?.Invoke(filename); } public event APIProgressHandler Progress; - public new event APISuccessHandler Success; + public new event APISuccessHandler Success; } } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index d8499f4159..3cc040ecb1 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -79,27 +79,23 @@ namespace osu.Game public virtual Storage GetStorageForStableInstall() => null; - private Intro intro - { - get - { - Screen screen = screenStack; - while (screen != null && !(screen is Intro)) - screen = screen.ChildScreen; - return screen as Intro; - } - } - public float ToolbarOffset => Toolbar.Position.Y + Toolbar.DrawHeight; private IdleTracker idleTracker; public readonly Bindable OverlayActivationMode = new Bindable(); - private OsuScreen screenStack; + private BackgroundScreenStack backgroundStack; + private ParallaxContainer backgroundParallax; + + private ScreenStack screenStack; private VolumeOverlay volume; private OnScreenDisplay onscreenDisplay; + private OsuLogo osuLogo; + + private MainMenu menuScreen; + private Intro introScreen; private Bindable configRuleset; private readonly Bindable ruleset = new Bindable(); @@ -173,6 +169,8 @@ namespace osu.Game dependencies.CacheAs(ruleset); dependencies.CacheAs>(ruleset); + dependencies.Cache(osuLogo = new OsuLogo()); + // bind config int to database RulesetInfo configRuleset = LocalConfig.GetBindable(OsuSetting.Ruleset); ruleset.Value = RulesetStore.GetRuleset(configRuleset.Value) ?? RulesetStore.AvailableRulesets.First(); @@ -211,6 +209,12 @@ namespace osu.Game /// The beatmap to select. public void PresentBeatmap(BeatmapSetInfo beatmap) { + if (menuScreen == null) + { + Schedule(() => PresentBeatmap(beatmap)); + return; + } + CloseAllOverlays(false); void setBeatmap() @@ -233,16 +237,15 @@ namespace osu.Game } } - switch (currentScreen) + switch (screenStack.CurrentScreen) { case SongSelect _: break; default: // navigate to song select if we are not already there. - var menu = (MainMenu)intro.ChildScreen; - menu.MakeCurrent(); - menu.LoadToSolo(); + menuScreen.MakeCurrent(); + menuScreen.LoadToSolo(); break; } @@ -268,9 +271,7 @@ namespace osu.Game scoreLoad?.Cancel(); - var menu = intro.ChildScreen; - - if (menu == null) + if (menuScreen == null) { scoreLoad = Schedule(() => LoadScore(score, false)); return; @@ -291,7 +292,7 @@ namespace osu.Game return; } - if (!currentScreen.AllowExternalScreenChange) + if ((screenStack.CurrentScreen as IOsuScreen)?.AllowExternalScreenChange != true) { notifications.Post(new SimpleNotification { @@ -310,9 +311,9 @@ namespace osu.Game void loadScore() { - if (!menu.IsCurrentScreen) + if (!menuScreen.IsCurrentScreen()) { - menu.MakeCurrent(); + menuScreen.MakeCurrent(); this.Delay(500).Schedule(loadScore, out scoreLoad); return; } @@ -322,7 +323,7 @@ namespace osu.Game Beatmap.Value = BeatmapManager.GetWorkingBeatmap(databasedBeatmap); Beatmap.Value.Mods.Value = databasedScoreInfo.Mods; - currentScreen.Push(new PlayerLoader(() => new ReplayPlayer(databasedScore))); + menuScreen.Push(new PlayerLoader(() => new ReplayPlayer(databasedScore))); } } @@ -336,11 +337,6 @@ namespace osu.Game { base.LoadComplete(); - // The next time this is updated is in UpdateAfterChildren, which occurs too late and results - // in the cursor being shown for a few frames during the intro. - // This prevents the cursor from showing until we have a screen with CursorVisible = true - MenuCursorContainer.CanShowCursor = currentScreen?.CursorVisible ?? false; - // todo: all archive managers should be able to be looped here. SkinManager.PostNotification = n => notifications?.Post(n); SkinManager.GetStableStorage = GetStorageForStableInstall; @@ -350,6 +346,8 @@ namespace osu.Game BeatmapManager.PresentBeatmap = PresentBeatmap; + Container logoContainer; + AddRange(new Drawable[] { new VolumeControlReceptor @@ -361,6 +359,16 @@ namespace osu.Game screenContainer = new ScalingContainer(ScalingMode.ExcludeOverlays) { RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + backgroundParallax = new ParallaxContainer + { + RelativeSizeAxes = Axes.Both, + Child = backgroundStack = new BackgroundScreenStack { RelativeSizeAxes = Axes.Both }, + }, + screenStack = new ScreenStack { RelativeSizeAxes = Axes.Both }, + logoContainer = new Container { RelativeSizeAxes = Axes.Both }, + } }, overlayContent = new Container { @@ -370,12 +378,17 @@ namespace osu.Game idleTracker = new GameIdleTracker(6000) }); - loadComponentSingleFile(screenStack = new Loader(), d => + dependencies.Cache(backgroundStack); + + screenStack.ScreenPushed += screenPushed; + screenStack.ScreenExited += screenExited; + + loadComponentSingleFile(osuLogo, logoContainer.Add); + + loadComponentSingleFile(new Loader { - screenStack.ModePushed += screenAdded; - screenStack.Exited += screenRemoved; - screenContainer.Add(screenStack); - }); + RelativeSizeAxes = Axes.Both + }, screenStack.Push); loadComponentSingleFile(Toolbar = new Toolbar { @@ -383,7 +396,7 @@ namespace osu.Game OnHome = delegate { CloseAllOverlays(false); - intro?.ChildScreen?.MakeCurrent(); + menuScreen?.MakeCurrent(); }, }, floatingOverlayContent.Add); @@ -617,7 +630,7 @@ namespace osu.Game public bool OnPressed(GlobalAction action) { - if (intro == null) return false; + if (introScreen == null) return false; switch (action) { @@ -674,19 +687,20 @@ namespace osu.Game private Container floatingOverlayContent; - private OsuScreen currentScreen; private FrameworkConfigManager frameworkConfig; private ScalingContainer screenContainer; protected override bool OnExiting() { - if (screenStack.ChildScreen == null) return false; + if (screenStack.CurrentScreen is Loader) + return false; - if (intro == null) return true; + if (introScreen == null) + return true; - if (!intro.DidLoadMenu || intro.ChildScreen != null) + if (!introScreen.DidLoadMenu || !(screenStack.CurrentScreen is Intro)) { - Scheduler.Add(intro.MakeCurrent); + Scheduler.Add(introScreen.MakeCurrent); return true; } @@ -711,7 +725,7 @@ namespace osu.Game // we only want to apply these restrictions when we are inside a screen stack. // the use case for not applying is in visual/unit tests. - bool applyBeatmapRulesetRestrictions = !currentScreen?.AllowBeatmapRulesetChange ?? false; + bool applyBeatmapRulesetRestrictions = !(screenStack.CurrentScreen as IOsuScreen)?.AllowBeatmapRulesetChange ?? false; ruleset.Disabled = applyBeatmapRulesetRestrictions; Beatmap.Disabled = applyBeatmapRulesetRestrictions; @@ -719,7 +733,7 @@ namespace osu.Game screenContainer.Padding = new MarginPadding { Top = ToolbarOffset }; overlayContent.Padding = new MarginPadding { Top = ToolbarOffset }; - MenuCursorContainer.CanShowCursor = currentScreen?.CursorVisible ?? false; + MenuCursorContainer.CanShowCursor = (screenStack.CurrentScreen as IOsuScreen)?.CursorVisible ?? false; } /// @@ -748,24 +762,41 @@ namespace osu.Game this.ruleset.Disabled = rulesetDisabled; } - protected virtual void ScreenChanged(OsuScreen current, Screen newScreen) + protected virtual void ScreenChanged(IScreen lastScreen, IScreen newScreen) { - currentScreen = (OsuScreen)newScreen; + switch (newScreen) + { + case Intro intro: + introScreen = intro; + break; + case MainMenu menu: + menuScreen = menu; + break; + } + + if (newScreen is IOsuScreen newOsuScreen) + { + backgroundParallax.ParallaxAmount = ParallaxContainer.DEFAULT_PARALLAX_AMOUNT * newOsuScreen.BackgroundParallaxAmount; + + OverlayActivationMode.Value = newOsuScreen.InitialOverlayActivationMode; + + if (newOsuScreen.HideOverlaysOnEnter) + CloseAllOverlays(); + else + Toolbar.State = Visibility.Visible; + } } - private void screenAdded(Screen newScreen) + private void screenPushed(IScreen lastScreen, IScreen newScreen) { - ScreenChanged(currentScreen, newScreen); + ScreenChanged(lastScreen, newScreen); Logger.Log($"Screen changed → {newScreen}"); - - newScreen.ModePushed += screenAdded; - newScreen.Exited += screenRemoved; } - private void screenRemoved(Screen newScreen) + private void screenExited(IScreen lastScreen, IScreen newScreen) { - ScreenChanged(currentScreen, newScreen); - Logger.Log($"Screen changed ← {currentScreen}"); + ScreenChanged(lastScreen, newScreen); + Logger.Log($"Screen changed ← {newScreen}"); if (newScreen == null) Exit(); diff --git a/osu.Game/Overlays/AccountCreation/AccountCreationScreen.cs b/osu.Game/Overlays/AccountCreation/AccountCreationScreen.cs index 071e1d0ffa..7e2ae405cb 100644 --- a/osu.Game/Overlays/AccountCreation/AccountCreationScreen.cs +++ b/osu.Game/Overlays/AccountCreation/AccountCreationScreen.cs @@ -8,22 +8,22 @@ namespace osu.Game.Overlays.AccountCreation { public abstract class AccountCreationScreen : Screen { - protected override void OnEntering(Screen last) + public override void OnEntering(IScreen last) { base.OnEntering(last); - Content.FadeOut().Delay(200).FadeIn(200); + this.FadeOut().Delay(200).FadeIn(200); } - protected override void OnResuming(Screen last) + public override void OnResuming(IScreen last) { base.OnResuming(last); - Content.FadeIn(200); + this.FadeIn(200); } - protected override void OnSuspending(Screen next) + public override void OnSuspending(IScreen next) { base.OnSuspending(next); - Content.FadeOut(200); + this.FadeOut(200); } } } diff --git a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs index b0d08d3c6c..02925a08e7 100644 --- a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs +++ b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs @@ -44,7 +44,7 @@ namespace osu.Game.Overlays.AccountCreation { this.api = api; - Children = new Drawable[] + InternalChildren = new Drawable[] { new FillFlowContainer { @@ -143,7 +143,7 @@ namespace osu.Game.Overlays.AccountCreation focusNextTextbox(); } - protected override void OnEntering(Screen last) + public override void OnEntering(IScreen last) { base.OnEntering(last); processingOverlay.Hide(); diff --git a/osu.Game/Overlays/AccountCreation/ScreenWarning.cs b/osu.Game/Overlays/AccountCreation/ScreenWarning.cs index cec0f1a767..3cc84e3562 100644 --- a/osu.Game/Overlays/AccountCreation/ScreenWarning.cs +++ b/osu.Game/Overlays/AccountCreation/ScreenWarning.cs @@ -26,12 +26,12 @@ namespace osu.Game.Overlays.AccountCreation private const string help_centre_url = "/help/wiki/Help_Centre#login"; - protected override void OnEntering(Screen last) + public override void OnEntering(IScreen last) { if (string.IsNullOrEmpty(api.ProvidedUsername)) { - Content.FadeOut(); - Push(new ScreenEntry()); + this.FadeOut(); + this.Push(new ScreenEntry()); return; } @@ -46,7 +46,7 @@ namespace osu.Game.Overlays.AccountCreation if (string.IsNullOrEmpty(api.ProvidedUsername)) return; - Children = new Drawable[] + InternalChildren = new Drawable[] { new Sprite { @@ -104,7 +104,7 @@ namespace osu.Game.Overlays.AccountCreation new DangerousSettingsButton { Text = "I understand. This account isn't for me.", - Action = () => Push(new ScreenEntry()) + Action = () => this.Push(new ScreenEntry()) }, furtherAssistance = new LinkFlowContainer(cp => { cp.TextSize = 12; }) { diff --git a/osu.Game/Overlays/AccountCreation/ScreenWelcome.cs b/osu.Game/Overlays/AccountCreation/ScreenWelcome.cs index da59b294e5..d4b8323394 100644 --- a/osu.Game/Overlays/AccountCreation/ScreenWelcome.cs +++ b/osu.Game/Overlays/AccountCreation/ScreenWelcome.cs @@ -4,6 +4,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Screens; using osu.Game.Graphics.Sprites; using osu.Game.Overlays.Settings; using osu.Game.Screens.Menu; @@ -16,7 +17,7 @@ namespace osu.Game.Overlays.AccountCreation [BackgroundDependencyLoader] private void load() { - Child = new FillFlowContainer + InternalChild = new FillFlowContainer { RelativeSizeAxes = Axes.Both, Direction = FillDirection.Vertical, @@ -56,7 +57,7 @@ namespace osu.Game.Overlays.AccountCreation { Text = "Let's create an account!", Margin = new MarginPadding { Vertical = 120 }, - Action = () => Push(new ScreenWarning()) + Action = () => this.Push(new ScreenWarning()) } } }; diff --git a/osu.Game/Overlays/AccountCreationOverlay.cs b/osu.Game/Overlays/AccountCreationOverlay.cs index 718a56a441..dab960db25 100644 --- a/osu.Game/Overlays/AccountCreationOverlay.cs +++ b/osu.Game/Overlays/AccountCreationOverlay.cs @@ -6,6 +6,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Screens; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Online.API; @@ -82,7 +83,7 @@ namespace osu.Game.Overlays base.PopIn(); this.FadeIn(transition_time, Easing.OutQuint); - if (welcomeScreen.ChildScreen != null) + if (welcomeScreen.GetChildScreen() != null) welcomeScreen.MakeCurrent(); } diff --git a/osu.Game/Screens/BackgroundScreen.cs b/osu.Game/Screens/BackgroundScreen.cs index d2308ac1c3..bbe162cf7c 100644 --- a/osu.Game/Screens/BackgroundScreen.cs +++ b/osu.Game/Screens/BackgroundScreen.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Threading; using osu.Framework.Screens; using osu.Framework.Graphics; using osu.Framework.Input.Events; @@ -12,6 +11,12 @@ namespace osu.Game.Screens { public abstract class BackgroundScreen : Screen, IEquatable { + protected BackgroundScreen() + { + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + } + public virtual bool Equals(BackgroundScreen other) { return other?.GetType() == GetType(); @@ -26,64 +31,40 @@ namespace osu.Game.Screens return false; } - public override void Push(Screen screen) - { - // When trying to push a non-loaded screen, load it asynchronously and re-invoke Push - // once it's done. - if (screen.LoadState == LoadState.NotLoaded) - { - LoadComponentAsync(screen, d => Push((BackgroundScreen)d)); - return; - } - - // Make sure the in-progress loading is complete before pushing the screen. - while (screen.LoadState < LoadState.Ready) - Thread.Sleep(1); - - try - { - base.Push(screen); - } - catch (ScreenAlreadyExitedException) - { - // screen may have exited before the push was successful. - } - } - protected override void Update() { base.Update(); - Content.Scale = new Vector2(1 + x_movement_amount / DrawSize.X * 2); + Scale = new Vector2(1 + x_movement_amount / DrawSize.X * 2); } - protected override void OnEntering(Screen last) + public override void OnEntering(IScreen last) { - Content.FadeOut(); - Content.MoveToX(x_movement_amount); + this.FadeOut(); + this.MoveToX(x_movement_amount); - Content.FadeIn(transition_length, Easing.InOutQuart); - Content.MoveToX(0, transition_length, Easing.InOutQuart); + this.FadeIn(transition_length, Easing.InOutQuart); + this.MoveToX(0, transition_length, Easing.InOutQuart); base.OnEntering(last); } - protected override void OnSuspending(Screen next) + public override void OnSuspending(IScreen next) { - Content.MoveToX(-x_movement_amount, transition_length, Easing.InOutQuart); + this.MoveToX(-x_movement_amount, transition_length, Easing.InOutQuart); base.OnSuspending(next); } - protected override bool OnExiting(Screen next) + public override bool OnExiting(IScreen next) { - Content.FadeOut(transition_length, Easing.OutExpo); - Content.MoveToX(x_movement_amount, transition_length, Easing.OutExpo); + this.FadeOut(transition_length, Easing.OutExpo); + this.MoveToX(x_movement_amount, transition_length, Easing.OutExpo); return base.OnExiting(next); } - protected override void OnResuming(Screen last) + public override void OnResuming(IScreen last) { - Content.MoveToX(0, transition_length, Easing.OutExpo); + this.MoveToX(0, transition_length, Easing.OutExpo); base.OnResuming(last); } } diff --git a/osu.Game/Screens/BackgroundScreenStack.cs b/osu.Game/Screens/BackgroundScreenStack.cs new file mode 100644 index 0000000000..7bf167295c --- /dev/null +++ b/osu.Game/Screens/BackgroundScreenStack.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.Collections.Generic; +using osu.Framework.Graphics; +using osu.Framework.Screens; +using osuTK; + +namespace osu.Game.Screens +{ + public class BackgroundScreenStack : ScreenStack + { + public BackgroundScreenStack() + { + Scale = new Vector2(1.06f); + RelativeSizeAxes = Axes.Both; + } + + //public float ParallaxAmount { set => parallax.ParallaxAmount = ParallaxContainer.DEFAULT_PARALLAX_AMOUNT * value; } + + public new void Push(BackgroundScreen screen) + { + if (screen == null) + return; + + if (EqualityComparer.Default.Equals((BackgroundScreen)CurrentScreen, screen)) + return; + + base.Push(screen); + } + } +} diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs index 3313db15e2..8706cc6668 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs @@ -37,7 +37,7 @@ namespace osu.Game.Screens.Backgrounds } b.Depth = newDepth; - Add(Background = b); + AddInternal(Background = b); Background.BlurSigma = BlurTarget; })); }); diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenBlack.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenBlack.cs index 2584739787..9e2559cc56 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenBlack.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenBlack.cs @@ -12,14 +12,14 @@ namespace osu.Game.Screens.Backgrounds { public BackgroundScreenBlack() { - Child = new Box + InternalChild = new Box { Colour = Color4.Black, RelativeSizeAxes = Axes.Both, }; } - protected override void OnEntering(Screen last) + public override void OnEntering(IScreen last) { Show(); } diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenCustom.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenCustom.cs index 8792721600..0cb41bc562 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenCustom.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenCustom.cs @@ -12,7 +12,7 @@ namespace osu.Game.Screens.Backgrounds public BackgroundScreenCustom(string textureName) { this.textureName = textureName; - Add(new Background(textureName)); + AddInternal(new Background(textureName)); } public override bool Equals(BackgroundScreen other) diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs index 9789248660..072da7c66e 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs @@ -42,7 +42,7 @@ namespace osu.Game.Screens.Backgrounds Background?.FadeOut(800, Easing.InOutSine); Background?.Expire(); - Add(Background = newBackground); + AddInternal(Background = newBackground); currentDisplay++; } diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index e4ef023a7a..ac5ff504a2 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -28,7 +28,7 @@ namespace osu.Game.Screens.Edit { protected override BackgroundScreen CreateBackground() => new BackgroundScreenCustom(@"Backgrounds/bg4"); - protected override bool HideOverlaysOnEnter => true; + public override bool HideOverlaysOnEnter => true; public override bool AllowBeatmapRulesetChange => false; private Box bottomBackground; @@ -65,7 +65,7 @@ namespace osu.Game.Screens.Edit SummaryTimeline timeline; PlaybackControl playback; - Children = new[] + InternalChildren = new[] { new Container { @@ -96,7 +96,7 @@ namespace osu.Game.Screens.Edit { new EditorMenuItem("Export", MenuItemType.Standard, exportBeatmap), new EditorMenuItemSpacer(), - new EditorMenuItem("Exit", MenuItemType.Standard, Exit) + new EditorMenuItem("Exit", MenuItemType.Standard, this.Exit) } } } @@ -194,20 +194,20 @@ namespace osu.Game.Screens.Edit return true; } - protected override void OnResuming(Screen last) + public override void OnResuming(IScreen last) { Beatmap.Value.Track?.Stop(); base.OnResuming(last); } - protected override void OnEntering(Screen last) + public override void OnEntering(IScreen last) { base.OnEntering(last); Background.FadeColour(Color4.DarkGray, 500); Beatmap.Value.Track?.Stop(); } - protected override bool OnExiting(Screen next) + public override bool OnExiting(IScreen next) { Background.FadeColour(Color4.White, 500); if (Beatmap.Value.Track != null) diff --git a/osu.Game/Screens/IOsuScreen.cs b/osu.Game/Screens/IOsuScreen.cs new file mode 100644 index 0000000000..532d4963a0 --- /dev/null +++ b/osu.Game/Screens/IOsuScreen.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.Framework.Screens; +using osu.Game.Overlays; + +namespace osu.Game.Screens +{ + public interface IOsuScreen : IScreen + { + /// + /// Whether the beatmap or ruleset should be allowed to be changed by the user or game. + /// Used to mark exclusive areas where this is strongly prohibited, like gameplay. + /// + bool AllowBeatmapRulesetChange { get; } + + bool AllowExternalScreenChange { get; } + + /// + /// Whether this allows the cursor to be displayed. + /// + bool CursorVisible { get; } + + /// + /// Whether all overlays should be hidden when this screen is entered or resumed. + /// + bool HideOverlaysOnEnter { get; } + + /// + /// Whether overlays should be able to be opened once this screen is entered or resumed. + /// + OverlayActivation InitialOverlayActivationMode { get; } + + /// + /// The amount of parallax to be applied while this screen is displayed. + /// + float BackgroundParallaxAmount { get; } + } +} diff --git a/osu.Game/Screens/Loader.cs b/osu.Game/Screens/Loader.cs index 78e80766b2..9703d79442 100644 --- a/osu.Game/Screens/Loader.cs +++ b/osu.Game/Screens/Loader.cs @@ -17,9 +17,9 @@ namespace osu.Game.Screens { private bool showDisclaimer; - protected override bool HideOverlaysOnEnter => true; + public override bool HideOverlaysOnEnter => true; - protected override OverlayActivation InitialOverlayActivationMode => OverlayActivation.Disabled; + public override OverlayActivation InitialOverlayActivationMode => OverlayActivation.Disabled; protected override bool AllowBackButton => false; @@ -55,11 +55,11 @@ namespace osu.Game.Screens protected virtual ShaderPrecompiler CreateShaderPrecompiler() => new ShaderPrecompiler(); - protected override void OnEntering(Screen last) + public override void OnEntering(IScreen last) { base.OnEntering(last); - LoadComponentAsync(precompiler = CreateShaderPrecompiler(), Add); + LoadComponentAsync(precompiler = CreateShaderPrecompiler(), AddInternal); LoadComponentAsync(loadableScreen = CreateLoadableScreen()); checkIfLoaded(); @@ -73,7 +73,7 @@ namespace osu.Game.Screens return; } - Push(loadableScreen); + this.Push(loadableScreen); } [BackgroundDependencyLoader] diff --git a/osu.Game/Screens/Menu/Disclaimer.cs b/osu.Game/Screens/Menu/Disclaimer.cs index ed0ae3309c..c0ff37cc0b 100644 --- a/osu.Game/Screens/Menu/Disclaimer.cs +++ b/osu.Game/Screens/Menu/Disclaimer.cs @@ -23,8 +23,8 @@ namespace osu.Game.Screens.Menu private Color4 iconColour; private LinkFlowContainer textFlow; - protected override bool HideOverlaysOnEnter => true; - protected override OverlayActivation InitialOverlayActivationMode => OverlayActivation.Disabled; + public override bool HideOverlaysOnEnter => true; + public override OverlayActivation InitialOverlayActivationMode => OverlayActivation.Disabled; public override bool CursorVisible => false; @@ -41,7 +41,7 @@ namespace osu.Game.Screens.Menu [BackgroundDependencyLoader] private void load(OsuColour colours) { - Children = new Drawable[] + InternalChildren = new Drawable[] { icon = new SpriteIcon { @@ -116,7 +116,7 @@ namespace osu.Game.Screens.Menu LoadComponentAsync(intro = new Intro()); } - protected override void OnEntering(Screen last) + public override void OnEntering(IScreen last) { base.OnEntering(last); @@ -130,12 +130,12 @@ namespace osu.Game.Screens.Menu supporterDrawables.ForEach(d => d.FadeOut().Delay(2000).FadeIn(500)); - Content + this .FadeInFromZero(500) .Then(5500) .FadeOut(250) .ScaleTo(0.9f, 250, Easing.InQuint) - .Finally(d => Push(intro)); + .Finally(d => this.Push(intro)); heart.FlashColour(Color4.White, 750, Easing.OutQuint).Loop(); } diff --git a/osu.Game/Screens/Menu/Intro.cs b/osu.Game/Screens/Menu/Intro.cs index 93a84ec14d..3a347342d7 100644 --- a/osu.Game/Screens/Menu/Intro.cs +++ b/osu.Game/Screens/Menu/Intro.cs @@ -34,8 +34,8 @@ namespace osu.Game.Screens.Menu private SampleChannel welcome; private SampleChannel seeya; - protected override bool HideOverlaysOnEnter => true; - protected override OverlayActivation InitialOverlayActivationMode => OverlayActivation.Disabled; + public override bool HideOverlaysOnEnter => true; + public override OverlayActivation InitialOverlayActivationMode => OverlayActivation.Disabled; public override bool CursorVisible => false; @@ -111,7 +111,7 @@ namespace osu.Game.Screens.Menu Scheduler.AddDelayed(delegate { DidLoadMenu = true; - Push(mainMenu); + this.Push(mainMenu); }, delay_step_one); }, delay_step_two); } @@ -145,22 +145,21 @@ namespace osu.Game.Screens.Menu } } - protected override void OnSuspending(Screen next) + public override void OnSuspending(IScreen next) { - Content.FadeOut(300); + this.FadeOut(300); base.OnSuspending(next); } - protected override bool OnExiting(Screen next) + public override bool OnExiting(IScreen next) { //cancel exiting if we haven't loaded the menu yet. return !DidLoadMenu; } - protected override void OnResuming(Screen last) + public override void OnResuming(IScreen last) { - if (!(last is MainMenu)) - Content.FadeIn(300); + this.FadeIn(300); double fadeOutTime = EXIT_DELAY; //we also handle the exit transition. @@ -169,7 +168,7 @@ namespace osu.Game.Screens.Menu else fadeOutTime = 500; - Scheduler.AddDelayed(Exit, fadeOutTime); + Scheduler.AddDelayed(this.Exit, fadeOutTime); //don't want to fade out completely else we will stop running updates and shit will hit the fan. Game.FadeTo(0.01f, fadeOutTime); diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 800bf8990e..04bc80ac87 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -25,28 +25,25 @@ namespace osu.Game.Screens.Menu { private readonly ButtonSystem buttons; - protected override bool HideOverlaysOnEnter => buttons.State == ButtonSystemState.Initial; + public override bool HideOverlaysOnEnter => buttons.State == ButtonSystemState.Initial; protected override bool AllowBackButton => buttons.State != ButtonSystemState.Initial; public override bool AllowExternalScreenChange => true; - private readonly BackgroundScreenDefault background; private Screen songSelect; private readonly MenuSideFlashes sideFlashes; - protected override BackgroundScreen CreateBackground() => background; + protected override BackgroundScreen CreateBackground() => new BackgroundScreenDefault(); public MainMenu() { - background = new BackgroundScreenDefault(); - - Children = new Drawable[] + InternalChildren = new Drawable[] { new ExitConfirmOverlay { - Action = Exit, + Action = this.Exit, }, new ParallaxContainer { @@ -55,12 +52,12 @@ namespace osu.Game.Screens.Menu { buttons = new ButtonSystem { - OnChart = delegate { Push(new ChartListing()); }, - OnDirect = delegate { Push(new OnlineListing()); }, - OnEdit = delegate { Push(new Editor()); }, + OnChart = delegate { this.Push(new ChartListing()); }, + OnDirect = delegate {this.Push(new OnlineListing()); }, + OnEdit = delegate {this.Push(new Editor()); }, OnSolo = onSolo, - OnMulti = delegate { Push(new Multiplayer()); }, - OnExit = Exit, + OnMulti = delegate {this.Push(new Multiplayer()); }, + OnExit = this.Exit, } } }, @@ -73,10 +70,10 @@ namespace osu.Game.Screens.Menu { case ButtonSystemState.Initial: case ButtonSystemState.Exit: - background.FadeColour(Color4.White, 500, Easing.OutSine); + Background.FadeColour(Color4.White, 500, Easing.OutSine); break; default: - background.FadeColour(OsuColour.Gray(0.8f), 500, Easing.OutSine); + Background.FadeColour(OsuColour.Gray(0.8f), 500, Easing.OutSine); break; } }; @@ -85,8 +82,6 @@ namespace osu.Game.Screens.Menu [BackgroundDependencyLoader(true)] private void load(OsuGame game = null) { - LoadComponentAsync(background); - if (game != null) { buttons.OnSettings = game.ToggleSettings; @@ -104,7 +99,7 @@ namespace osu.Game.Screens.Menu public void LoadToSolo() => Schedule(onSolo); - private void onSolo() => Push(consumeSongSelect()); + private void onSolo() =>this.Push(consumeSongSelect()); private Screen consumeSongSelect() { @@ -113,7 +108,7 @@ namespace osu.Game.Screens.Menu return s; } - protected override void OnEntering(Screen last) + public override void OnEntering(IScreen last) { base.OnEntering(last); buttons.FadeInFromZero(500); @@ -148,8 +143,8 @@ namespace osu.Game.Screens.Menu const float length = 300; - Content.FadeIn(length, Easing.OutQuint); - Content.MoveTo(new Vector2(0, 0), length, Easing.OutQuint); + this.FadeIn(length, Easing.OutQuint); + this.MoveTo(new Vector2(0, 0), length, Easing.OutQuint); sideFlashes.Delay(length).FadeIn(64, Easing.InQuint); } @@ -164,13 +159,13 @@ namespace osu.Game.Screens.Menu private void beatmap_ValueChanged(WorkingBeatmap newValue) { - if (!IsCurrentScreen) + if (!this.IsCurrentScreen()) return; - background.Next(); + ((BackgroundScreenDefault)Background).Next(); } - protected override void OnSuspending(Screen next) + public override void OnSuspending(IScreen next) { base.OnSuspending(next); @@ -178,26 +173,26 @@ namespace osu.Game.Screens.Menu buttons.State = ButtonSystemState.EnteringMode; - Content.FadeOut(length, Easing.InSine); - Content.MoveTo(new Vector2(-800, 0), length, Easing.InSine); + this.FadeOut(length, Easing.InSine); + this.MoveTo(new Vector2(-800, 0), length, Easing.InSine); sideFlashes.FadeOut(64, Easing.OutQuint); } - protected override void OnResuming(Screen last) + public override void OnResuming(IScreen last) { base.OnResuming(last); - background.Next(); + ((BackgroundScreenDefault)Background).Next(); //we may have consumed our preloaded instance, so let's make another. preloadSongSelect(); } - protected override bool OnExiting(Screen next) + public override bool OnExiting(IScreen next) { buttons.State = ButtonSystemState.Exit; - Content.FadeOut(3000); + this.FadeOut(3000); return base.OnExiting(next); } @@ -205,7 +200,7 @@ namespace osu.Game.Screens.Menu { if (!e.Repeat && e.ControlPressed && e.ShiftPressed && e.Key == Key.D) { - Push(new Drawings()); + this.Push(new Drawings()); return true; } diff --git a/osu.Game/Screens/Multi/Header.cs b/osu.Game/Screens/Multi/Header.cs index 3448a23ac8..687a28b7a6 100644 --- a/osu.Game/Screens/Multi/Header.cs +++ b/osu.Game/Screens/Multi/Header.cs @@ -22,7 +22,7 @@ namespace osu.Game.Screens.Multi private readonly OsuSpriteText screenType; private readonly HeaderBreadcrumbControl breadcrumbs; - public Header(Screen initialScreen) + public Header(ScreenStack stack) { RelativeSizeAxes = Axes.X; Height = HEIGHT; @@ -75,7 +75,7 @@ namespace osu.Game.Screens.Multi }, }, }, - breadcrumbs = new HeaderBreadcrumbControl(initialScreen) + breadcrumbs = new HeaderBreadcrumbControl(stack) { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, @@ -103,8 +103,8 @@ namespace osu.Game.Screens.Multi private class HeaderBreadcrumbControl : ScreenBreadcrumbControl { - public HeaderBreadcrumbControl(Screen initialScreen) - : base(initialScreen) + public HeaderBreadcrumbControl(ScreenStack stack) + : base(stack) { } diff --git a/osu.Game/Screens/Multi/IMultiplayerSubScreen.cs b/osu.Game/Screens/Multi/IMultiplayerSubScreen.cs index 542224262e..31ee123f83 100644 --- a/osu.Game/Screens/Multi/IMultiplayerSubScreen.cs +++ b/osu.Game/Screens/Multi/IMultiplayerSubScreen.cs @@ -3,8 +3,10 @@ namespace osu.Game.Screens.Multi { - public interface IMultiplayerSubScreen + public interface IMultiplayerSubScreen : IOsuScreen { + string Title { get; } + string ShortTitle { get; } } } diff --git a/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs index 28ec5d2d1a..a56d2892a4 100644 --- a/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; @@ -17,6 +16,8 @@ namespace osu.Game.Screens.Multi.Lounge { public class LoungeSubScreen : MultiplayerSubScreen { + public override string Title => "Lounge"; + protected readonly FilterControl Filter; private readonly Container content; @@ -24,20 +25,13 @@ namespace osu.Game.Screens.Multi.Lounge private readonly Action pushGameplayScreen; private readonly ProcessingOverlay processingOverlay; - [Resolved(CanBeNull = true)] - private IRoomManager roomManager { get; set; } - - public override string Title => "Lounge"; - - protected override Drawable TransitionContent => content; - public LoungeSubScreen(Action pushGameplayScreen) { this.pushGameplayScreen = pushGameplayScreen; RoomInspector inspector; - Children = new Drawable[] + InternalChildren = new Drawable[] { Filter = new FilterControl { Depth = -1 }, content = new Container @@ -81,7 +75,7 @@ namespace osu.Game.Screens.Multi.Lounge Filter.Search.Current.ValueChanged += s => filterRooms(); Filter.Tabs.Current.ValueChanged += t => filterRooms(); - Filter.Search.Exit += Exit; + Filter.Search.Exit += this.Exit; } protected override void UpdateAfterChildren() @@ -91,8 +85,8 @@ namespace osu.Game.Screens.Multi.Lounge content.Padding = new MarginPadding { Top = Filter.DrawHeight, - Left = SearchableListOverlay.WIDTH_PADDING - DrawableRoom.SELECTION_BORDER_WIDTH + HORIZONTAL_OVERFLOW_PADDING, - Right = SearchableListOverlay.WIDTH_PADDING + HORIZONTAL_OVERFLOW_PADDING, + Left = SearchableListOverlay.WIDTH_PADDING - DrawableRoom.SELECTION_BORDER_WIDTH + OsuScreen.HORIZONTAL_OVERFLOW_PADDING, + Right = SearchableListOverlay.WIDTH_PADDING + OsuScreen.HORIZONTAL_OVERFLOW_PADDING, }; } @@ -101,20 +95,19 @@ namespace osu.Game.Screens.Multi.Lounge Filter.Search.TakeFocus(); } - protected override void OnEntering(Screen last) + public override void OnEntering(IScreen last) { base.OnEntering(last); Filter.Search.HoldFocus = true; } - protected override bool OnExiting(Screen next) + public override bool OnExiting(IScreen next) { Filter.Search.HoldFocus = false; - // no base call; don't animate - return false; + return base.OnExiting(next); } - protected override void OnSuspending(Screen next) + public override void OnSuspending(IScreen next) { base.OnSuspending(next); Filter.Search.HoldFocus = false; @@ -123,13 +116,13 @@ namespace osu.Game.Screens.Multi.Lounge private void filterRooms() { rooms.Filter(Filter.CreateCriteria()); - roomManager?.Filter(Filter.CreateCriteria()); + Manager?.Filter(Filter.CreateCriteria()); } private void joinRequested(Room room) { processingOverlay.Show(); - roomManager?.JoinRoom(room, r => + Manager?.JoinRoom(room, r => { Push(room); processingOverlay.Hide(); @@ -142,10 +135,10 @@ namespace osu.Game.Screens.Multi.Lounge public void Push(Room room) { // Handles the case where a room is clicked 3 times in quick succession - if (!IsCurrentScreen) + if (!this.IsCurrentScreen()) return; - Push(new MatchSubScreen(room, s => pushGameplayScreen?.Invoke(s))); + this.Push(new MatchSubScreen(room, s => pushGameplayScreen?.Invoke(s))); } } } diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs index 1b0efbdf09..980f321c92 100644 --- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs @@ -21,6 +21,7 @@ namespace osu.Game.Screens.Multi.Match public class MatchSubScreen : MultiplayerSubScreen { public override bool AllowBeatmapRulesetChange => false; + public override string Title => room.RoomID.Value == null ? "New room" : room.Name.Value; public override string ShortTitle => "room"; @@ -36,12 +37,6 @@ namespace osu.Game.Screens.Multi.Match [Resolved] private BeatmapManager beatmapManager { get; set; } - [Resolved(CanBeNull = true)] - private OsuGame game { get; set; } - - [Resolved(CanBeNull = true)] - private IRoomManager manager { get; set; } - public MatchSubScreen(Room room, Action pushGameplayScreen) { this.room = room; @@ -55,7 +50,7 @@ namespace osu.Game.Screens.Multi.Match GridContainer bottomRow; MatchSettingsOverlay settings; - Children = new Drawable[] + InternalChildren = new Drawable[] { new GridContainer { @@ -77,7 +72,7 @@ namespace osu.Game.Screens.Multi.Match { Padding = new MarginPadding { - Left = 10 + HORIZONTAL_OVERFLOW_PADDING, + Left = 10 + OsuScreen.HORIZONTAL_OVERFLOW_PADDING, Right = 10, Vertical = 10, }, @@ -89,7 +84,7 @@ namespace osu.Game.Screens.Multi.Match Padding = new MarginPadding { Left = 10, - Right = 10 + HORIZONTAL_OVERFLOW_PADDING, + Right = 10 + OsuScreen.HORIZONTAL_OVERFLOW_PADDING, Vertical = 10, }, RelativeSizeAxes = Axes.Both, @@ -118,10 +113,9 @@ namespace osu.Game.Screens.Multi.Match }, }; - header.OnRequestSelectBeatmap = () => Push(new MatchSongSelect + header.OnRequestSelectBeatmap = () => this.Push(new MatchSongSelect { Selected = addPlaylistItem, - Padding = new MarginPadding { Horizontal = HORIZONTAL_OVERFLOW_PADDING } }); header.Tabs.Current.ValueChanged += t => @@ -141,7 +135,11 @@ namespace osu.Game.Screens.Multi.Match } }; - chat.Exit += Exit; + chat.Exit += () => + { + if (this.IsCurrentScreen()) + this.Exit(); + }; } [BackgroundDependencyLoader] @@ -150,9 +148,9 @@ namespace osu.Game.Screens.Multi.Match beatmapManager.ItemAdded += beatmapAdded; } - protected override bool OnExiting(Screen next) + public override bool OnExiting(IScreen next) { - manager?.PartRoom(); + Manager?.PartRoom(); return base.OnExiting(next); } @@ -169,7 +167,7 @@ namespace osu.Game.Screens.Multi.Match // Retrieve the corresponding local beatmap, since we can't directly use the playlist's beatmap info var localBeatmap = beatmap == null ? null : beatmapManager.QueryBeatmap(b => b.OnlineBeatmapID == beatmap.OnlineBeatmapID); - game?.ForcefullySetBeatmap(beatmapManager.GetWorkingBeatmap(localBeatmap)); + Game?.ForcefullySetBeatmap(beatmapManager.GetWorkingBeatmap(localBeatmap)); } private void setRuleset(RulesetInfo ruleset) @@ -177,7 +175,7 @@ namespace osu.Game.Screens.Multi.Match if (ruleset == null) return; - game?.ForcefullySetRuleset(ruleset); + Game?.ForcefullySetRuleset(ruleset); } private void beatmapAdded(BeatmapSetInfo model, bool existing, bool silent) => Schedule(() => @@ -192,7 +190,7 @@ namespace osu.Game.Screens.Multi.Match var localBeatmap = beatmapManager.QueryBeatmap(b => b.OnlineBeatmapID == bindings.CurrentBeatmap.Value.OnlineBeatmapID); if (localBeatmap != null) - game?.ForcefullySetBeatmap(beatmapManager.GetWorkingBeatmap(localBeatmap)); + Game?.ForcefullySetBeatmap(beatmapManager.GetWorkingBeatmap(localBeatmap)); }); private void addPlaylistItem(PlaylistItem item) @@ -209,11 +207,9 @@ namespace osu.Game.Screens.Multi.Match { default: case GameTypeTimeshift _: - pushGameplayScreen?.Invoke(new PlayerLoader(() => { - var player = new TimeshiftPlayer(room, room.Playlist.First().ID); - player.Exited += _ => leaderboard.RefreshScores(); - - return player; + pushGameplayScreen?.Invoke(new PlayerLoader(() => new TimeshiftPlayer(room, room.Playlist.First().ID) + { + Exited = () => leaderboard.RefreshScores() })); break; } diff --git a/osu.Game/Screens/Multi/Multiplayer.cs b/osu.Game/Screens/Multi/Multiplayer.cs index da15574029..1741ac0b7b 100644 --- a/osu.Game/Screens/Multi/Multiplayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Logging; using osu.Framework.Screens; +using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Containers; @@ -15,6 +16,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Input; using osu.Game.Online.API; using osu.Game.Online.Multiplayer; +using osu.Game.Overlays; using osu.Game.Overlays.BeatmapSet.Buttons; using osu.Game.Screens.Menu; using osu.Game.Screens.Multi.Lounge; @@ -24,31 +26,56 @@ using osuTK; namespace osu.Game.Screens.Multi { [Cached] - public class Multiplayer : OsuScreen, IOnlineComponent + public class Multiplayer : CompositeDrawable, IOsuScreen, IOnlineComponent { - private readonly MultiplayerWaveContainer waves; + public bool AllowBeatmapRulesetChange => (screenStack.CurrentScreen as IMultiplayerSubScreen)?.AllowBeatmapRulesetChange ?? true; + public bool AllowExternalScreenChange => (screenStack.CurrentScreen as IMultiplayerSubScreen)?.AllowExternalScreenChange ?? true; + public bool CursorVisible => (screenStack.CurrentScreen as IMultiplayerSubScreen)?.AllowExternalScreenChange ?? true; - public override bool AllowBeatmapRulesetChange => currentSubScreen?.AllowBeatmapRulesetChange ?? base.AllowBeatmapRulesetChange; + public bool HideOverlaysOnEnter => false; + public OverlayActivation InitialOverlayActivationMode => OverlayActivation.All; + + public float BackgroundParallaxAmount => 1; + + public bool ValidForResume { get; set; } = true; + public bool ValidForPush { get; set; } = true; + + public override bool RemoveWhenNotAlive => false; + + private readonly MultiplayerWaveContainer waves; private readonly OsuButton createButton; private readonly LoungeSubScreen loungeSubScreen; - - private OsuScreen currentSubScreen; + private readonly ScreenStack screenStack; [Cached(Type = typeof(IRoomManager))] private RoomManager roomManager; + [Resolved] + private IBindableBeatmap beatmap { get; set; } + + [Resolved] + private OsuGameBase game { get; set; } + [Resolved] private APIAccess api { get; set; } + [Resolved(CanBeNull = true)] + private OsuLogo logo { get; set; } + public Multiplayer() { - Child = waves = new MultiplayerWaveContainer + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + RelativeSizeAxes = Axes.Both; + + InternalChild = waves = new MultiplayerWaveContainer { RelativeSizeAxes = Axes.Both, }; - Padding = new MarginPadding { Horizontal = -HORIZONTAL_OVERFLOW_PADDING }; + screenStack = new ScreenStack(loungeSubScreen = new LoungeSubScreen(this.Push)) { RelativeSizeAxes = Axes.Both }; + Padding = new MarginPadding { Horizontal = -OsuScreen.HORIZONTAL_OVERFLOW_PADDING }; waves.AddRange(new Drawable[] { @@ -76,9 +103,9 @@ namespace osu.Game.Screens.Multi { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Top = Header.HEIGHT }, - Child = loungeSubScreen = new LoungeSubScreen(Push), + Child = screenStack }, - new Header(loungeSubScreen), + new Header(screenStack), createButton = new HeaderButton { Anchor = Anchor.TopRight, @@ -88,7 +115,7 @@ namespace osu.Game.Screens.Multi Margin = new MarginPadding { Top = 10, - Right = 10 + HORIZONTAL_OVERFLOW_PADDING, + Right = 10 + OsuScreen.HORIZONTAL_OVERFLOW_PADDING, }, Text = "Create room", Action = () => loungeSubScreen.Push(new Room @@ -99,8 +126,8 @@ namespace osu.Game.Screens.Multi roomManager = new RoomManager() }); - screenAdded(loungeSubScreen); - loungeSubScreen.Exited += _ => Exit(); + screenStack.ScreenPushed += screenPushed; + screenStack.ScreenExited += screenExited; } private readonly IBindable isIdle = new BindableBool(); @@ -122,7 +149,7 @@ namespace osu.Game.Screens.Multi private void updatePollingRate(bool idle) { - roomManager.TimeBetweenPolls = !IsCurrentScreen || !(currentSubScreen is LoungeSubScreen) ? 0 : (idle ? 120000 : 15000); + roomManager.TimeBetweenPolls = !this.IsCurrentScreen() || !(screenStack.CurrentScreen is LoungeSubScreen) ? 0 : (idle ? 120000 : 15000); Logger.Log($"Polling adjusted to {roomManager.TimeBetweenPolls}"); } @@ -135,114 +162,106 @@ namespace osu.Game.Screens.Multi private void forcefullyExit() { // This is temporary since we don't currently have a way to force screens to be exited - if (IsCurrentScreen) - Exit(); + if (this.IsCurrentScreen()) + this.Exit(); else { - MakeCurrent(); + this.MakeCurrent(); Schedule(forcefullyExit); } } - protected override void OnEntering(Screen last) + public void OnEntering(IScreen last) { - Content.FadeIn(); + this.FadeIn(); - base.OnEntering(last); waves.Show(); } - protected override bool OnExiting(Screen next) + public bool OnExiting(IScreen next) { waves.Hide(); - Content.Delay(WaveContainer.DISAPPEAR_DURATION).FadeOut(); + this.Delay(WaveContainer.DISAPPEAR_DURATION).FadeOut(); cancelLooping(); - loungeSubScreen.MakeCurrent(); + + if (screenStack.CurrentScreen != null) + loungeSubScreen.MakeCurrent(); + updatePollingRate(isIdle.Value); - return base.OnExiting(next); + // the wave overlay transition takes longer than expected to run. + logo?.AppendAnimatingAction(() => logo.Delay(WaveContainer.DISAPPEAR_DURATION / 2).FadeOut(), false); + + return false; } - protected override void OnResuming(Screen last) + public void OnResuming(IScreen last) { - base.OnResuming(last); + this.FadeIn(250); + this.ScaleTo(1, 250, Easing.OutSine); - Content.FadeIn(250); - Content.ScaleTo(1, 250, Easing.OutSine); + logo?.AppendAnimatingAction(() => OsuScreen.ApplyLogoArrivingDefaults(logo), true); updatePollingRate(isIdle.Value); } - protected override void OnSuspending(Screen next) + public void OnSuspending(IScreen next) { - Content.ScaleTo(1.1f, 250, Easing.InSine); - Content.FadeOut(250); + this.ScaleTo(1.1f, 250, Easing.InSine); + this.FadeOut(250); cancelLooping(); roomManager.TimeBetweenPolls = 0; - - base.OnSuspending(next); } private void cancelLooping() { - var track = Beatmap.Value.Track; + var track = beatmap.Value.Track; if (track != null) track.Looping = false; } - protected override void LogoExiting(OsuLogo logo) - { - // the wave overlay transition takes longer than expected to run. - logo.Delay(WaveContainer.DISAPPEAR_DURATION / 2).FadeOut(); - base.LogoExiting(logo); - } - protected override void Update() { base.Update(); - if (!IsCurrentScreen) return; + if (!this.IsCurrentScreen()) return; - if (currentSubScreen is MatchSubScreen) + if (screenStack.CurrentScreen is MatchSubScreen) { - var track = Beatmap.Value.Track; + var track = beatmap.Value.Track; if (track != null) { track.Looping = true; if (!track.IsRunning) { - Game.Audio.AddItemToList(track); - track.Seek(Beatmap.Value.Metadata.PreviewTime); + game.Audio.AddItemToList(track); + track.Seek(beatmap.Value.Metadata.PreviewTime); track.Start(); } } createButton.Hide(); } - else if (currentSubScreen is LoungeSubScreen) + else if (screenStack.CurrentScreen is LoungeSubScreen) createButton.Show(); } - private void screenAdded(Screen newScreen) - { - currentSubScreen = (OsuScreen)newScreen; - updatePollingRate(isIdle.Value); + private void screenPushed(IScreen lastScreen, IScreen newScreen) + => updatePollingRate(isIdle.Value); - newScreen.ModePushed += screenAdded; - newScreen.Exited += screenRemoved; - } - - private void screenRemoved(Screen newScreen) + private void screenExited(IScreen lastScreen, IScreen newScreen) { - if (currentSubScreen is MatchSubScreen) + if (lastScreen is MatchSubScreen) cancelLooping(); - currentSubScreen = (OsuScreen)newScreen; updatePollingRate(isIdle.Value); + + if (screenStack.CurrentScreen == null) + this.Exit(); } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Screens/Multi/MultiplayerSubScreen.cs b/osu.Game/Screens/Multi/MultiplayerSubScreen.cs index 3e33de34fd..ddea4d5dad 100644 --- a/osu.Game/Screens/Multi/MultiplayerSubScreen.cs +++ b/osu.Game/Screens/Multi/MultiplayerSubScreen.cs @@ -1,49 +1,95 @@ // 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.Input.Bindings; using osu.Framework.Screens; +using osu.Game.Beatmaps; using osu.Game.Graphics.Containers; +using osu.Game.Input.Bindings; +using osu.Game.Overlays; namespace osu.Game.Screens.Multi { - public abstract class MultiplayerSubScreen : OsuScreen, IMultiplayerSubScreen + public abstract class MultiplayerSubScreen : CompositeDrawable, IMultiplayerSubScreen, IKeyBindingHandler { - protected virtual Drawable TransitionContent => Content; + public virtual bool AllowBeatmapRulesetChange => true; + public bool AllowExternalScreenChange => true; + public bool CursorVisible => true; + public bool HideOverlaysOnEnter => false; + public OverlayActivation InitialOverlayActivationMode => OverlayActivation.All; + + public float BackgroundParallaxAmount => 1; + + public bool ValidForResume { get; set; } = true; + public bool ValidForPush { get; set; } = true; + + public override bool RemoveWhenNotAlive => false; + + public abstract string Title { get; } public virtual string ShortTitle => Title; - protected override void OnEntering(Screen last) - { - base.OnEntering(last); + [Resolved] + protected IBindableBeatmap Beatmap { get; private set; } - Content.FadeInFromZero(WaveContainer.APPEAR_DURATION, Easing.OutQuint); - TransitionContent.FadeInFromZero(WaveContainer.APPEAR_DURATION, Easing.OutQuint); - TransitionContent.MoveToX(200).MoveToX(0, WaveContainer.APPEAR_DURATION, Easing.OutQuint); + [Resolved(CanBeNull = true)] + protected OsuGame Game { get; private set; } + + [Resolved(CanBeNull = true)] + protected IRoomManager Manager { get; private set; } + + protected MultiplayerSubScreen() + { + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + RelativeSizeAxes = Axes.Both; } - protected override bool OnExiting(Screen next) + public virtual void OnEntering(IScreen last) { - Content.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.OutQuint); - TransitionContent.MoveToX(200, WaveContainer.DISAPPEAR_DURATION, Easing.OutQuint); - - return base.OnExiting(next); + this.FadeInFromZero(WaveContainer.APPEAR_DURATION, Easing.OutQuint); + this.FadeInFromZero(WaveContainer.APPEAR_DURATION, Easing.OutQuint); + this.MoveToX(200).MoveToX(0, WaveContainer.APPEAR_DURATION, Easing.OutQuint); } - protected override void OnResuming(Screen last) + public virtual bool OnExiting(IScreen next) { - base.OnResuming(last); + this.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.OutQuint); + this.MoveToX(200, WaveContainer.DISAPPEAR_DURATION, Easing.OutQuint); - Content.FadeIn(WaveContainer.APPEAR_DURATION, Easing.OutQuint); - TransitionContent.MoveToX(0, WaveContainer.APPEAR_DURATION, Easing.OutQuint); + return false; } - protected override void OnSuspending(Screen next) + public virtual void OnResuming(IScreen last) { - base.OnSuspending(next); - - Content.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.OutQuint); - TransitionContent.MoveToX(-200, WaveContainer.DISAPPEAR_DURATION, Easing.OutQuint); + this.FadeIn(WaveContainer.APPEAR_DURATION, Easing.OutQuint); + this.MoveToX(0, WaveContainer.APPEAR_DURATION, Easing.OutQuint); } + + public virtual void OnSuspending(IScreen next) + { + this.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.OutQuint); + this.MoveToX(-200, WaveContainer.DISAPPEAR_DURATION, Easing.OutQuint); + } + + public virtual bool OnPressed(GlobalAction action) + { + if (!this.IsCurrentScreen()) return false; + + if (action == GlobalAction.Back) + { + this.Exit(); + return true; + } + + return false; + } + + public bool OnReleased(GlobalAction action) => action == GlobalAction.Back; + + public override string ToString() => Title; } } diff --git a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs index 50a4dedf3c..36cf0f0282 100644 --- a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs +++ b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using System.Threading; using osu.Framework.Allocation; using osu.Framework.Logging; +using osu.Framework.Screens; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.Multiplayer; @@ -18,6 +19,8 @@ namespace osu.Game.Screens.Multi.Play { public class TimeshiftPlayer : Player { + public Action Exited; + private readonly Room room; private readonly int playlistItemId; @@ -50,7 +53,7 @@ namespace osu.Game.Screens.Multi.Play Schedule(() => { ValidForResume = false; - Exit(); + this.Exit(); }); }; @@ -60,6 +63,16 @@ namespace osu.Game.Screens.Multi.Play Thread.Sleep(1000); } + public override bool OnExiting(IScreen next) + { + if (base.OnExiting(next)) + return true; + + Exited?.Invoke(); + + return false; + } + protected override ScoreInfo CreateScore() { submitScore(); @@ -79,6 +92,13 @@ namespace osu.Game.Screens.Multi.Play api.Queue(request); } + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + Exited = null; + } + protected override Results CreateResults(ScoreInfo score) => new MatchResults(score, room); } } diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index 45789d7892..ba5c7b2f0a 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.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 Microsoft.EntityFrameworkCore.Internal; using osu.Framework.Allocation; using osu.Framework.Audio; @@ -11,17 +10,14 @@ using osu.Framework.Graphics; using osu.Framework.Input.Bindings; using osu.Framework.Screens; using osu.Game.Beatmaps; -using osu.Game.Graphics.Containers; using osu.Game.Input.Bindings; using osu.Game.Rulesets; using osu.Game.Screens.Menu; -using osuTK; using osu.Game.Overlays; -using osu.Framework.Graphics.Containers; namespace osu.Game.Screens { - public abstract class OsuScreen : Screen, IKeyBindingHandler, IHasDescription + public abstract class OsuScreen : Screen, IOsuScreen, IKeyBindingHandler, IHasDescription { /// /// The amount of negative padding that should be applied to game background content which touches both the left and right sides of the screen. @@ -29,8 +25,6 @@ namespace osu.Game.Screens /// public const float HORIZONTAL_OVERFLOW_PADDING = 50; - public BackgroundScreen Background { get; private set; } - /// /// A user-facing title for this screen. /// @@ -42,80 +36,62 @@ namespace osu.Game.Screens public virtual bool AllowExternalScreenChange => false; - /// - /// Override to create a BackgroundMode for the current screen. - /// Note that the instance created may not be the used instance if it matches the BackgroundMode equality clause. - /// - protected virtual BackgroundScreen CreateBackground() => null; - - private Action updateOverlayStates; - /// /// Whether all overlays should be hidden when this screen is entered or resumed. /// - protected virtual bool HideOverlaysOnEnter => false; - - protected readonly Bindable OverlayActivationMode = new Bindable(); + public virtual bool HideOverlaysOnEnter => false; /// /// Whether overlays should be able to be opened once this screen is entered or resumed. /// - protected virtual OverlayActivation InitialOverlayActivationMode => OverlayActivation.All; + public virtual OverlayActivation InitialOverlayActivationMode => OverlayActivation.All; - /// - /// Whether this allows the cursor to be displayed. - /// public virtual bool CursorVisible => true; protected new OsuGameBase Game => base.Game as OsuGameBase; - private OsuLogo logo; - - /// - /// Whether the beatmap or ruleset should be allowed to be changed by the user or game. - /// Used to mark exclusive areas where this is strongly prohibited, like gameplay. - /// public virtual bool AllowBeatmapRulesetChange => true; protected readonly Bindable Beatmap = new Bindable(); - protected virtual float BackgroundParallaxAmount => 1; - - private ParallaxContainer backgroundParallaxContainer; + public virtual float BackgroundParallaxAmount => 1; protected readonly Bindable Ruleset = new Bindable(); private SampleChannel sampleExit; + protected BackgroundScreen Background => backgroundStack?.CurrentScreen as BackgroundScreen; + + private BackgroundScreen localBackground; + + [Resolved(canBeNull: true)] + private BackgroundScreenStack backgroundStack { get; set; } + + [Resolved(canBeNull: true)] + private OsuLogo logo { get; set; } + + protected OsuScreen() + { + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + } + [BackgroundDependencyLoader(true)] private void load(BindableBeatmap beatmap, OsuGame osu, AudioManager audio, Bindable ruleset) { Beatmap.BindTo(beatmap); Ruleset.BindTo(ruleset); - if (osu != null) - { - OverlayActivationMode.BindTo(osu.OverlayActivationMode); - - updateOverlayStates = () => - { - if (HideOverlaysOnEnter) - osu.CloseAllOverlays(); - else - osu.Toolbar.State = Visibility.Visible; - }; - } - sampleExit = audio.Sample.Get(@"UI/screen-back"); } public virtual bool OnPressed(GlobalAction action) { - if (!IsCurrentScreen) return false; + if (!this.IsCurrentScreen()) return false; if (action == GlobalAction.Back && AllowBackButton) { - Exit(); + this.Exit(); return true; } @@ -124,7 +100,7 @@ namespace osu.Game.Screens public bool OnReleased(GlobalAction action) => action == GlobalAction.Back && AllowBackButton; - protected override void OnResuming(Screen last) + public override void OnResuming(IScreen last) { sampleExit?.Play(); applyArrivingDefaults(true); @@ -132,71 +108,32 @@ namespace osu.Game.Screens base.OnResuming(last); } - protected override void OnSuspending(Screen next) + public override void OnSuspending(IScreen next) { base.OnSuspending(next); onSuspendingLogo(); } - protected override void OnEntering(Screen last) + public override void OnEntering(IScreen last) { - OsuScreen lastOsu = last as OsuScreen; - - BackgroundScreen bg = CreateBackground(); - - if (lastOsu?.Background != null) - { - backgroundParallaxContainer = lastOsu.backgroundParallaxContainer; - - if (bg == null || lastOsu.Background.Equals(bg)) - //we can keep the previous mode's background. - Background = lastOsu.Background; - else - { - lastOsu.Background.Push(Background = bg); - } - } - else if (bg != null) - { - // this makes up for the fact our padding changes when the global toolbar is visible. - bg.Scale = new Vector2(1.06f); - - AddInternal(backgroundParallaxContainer = new ParallaxContainer - { - Depth = float.MaxValue, - Children = new[] - { - Background = bg - } - }); - } - - if ((logo = lastOsu?.logo) == null) - LoadComponentAsync(logo = new OsuLogo { Alpha = 0 }, AddInternal); - applyArrivingDefaults(false); + backgroundStack?.Push(localBackground = CreateBackground()); + base.OnEntering(last); } - protected override bool OnExiting(Screen next) + public override bool OnExiting(IScreen next) { if (ValidForResume && logo != null) onExitingLogo(); - OsuScreen nextOsu = next as OsuScreen; - - if (Background != null && !Background.Equals(nextOsu?.Background)) - { - Background.Exit(); - - //We need to use MakeCurrent in case we are jumping up multiple game screens. - nextOsu?.Background?.MakeCurrent(); - } - if (base.OnExiting(next)) return true; + if (localBackground != null && backgroundStack?.CurrentScreen == localBackground) + backgroundStack?.Exit(); + Beatmap.UnbindAll(); return false; } @@ -205,6 +142,24 @@ namespace osu.Game.Screens /// Fired when this screen was entered or resumed and the logo state is required to be adjusted. /// protected virtual void LogoArriving(OsuLogo logo, bool resuming) + { + ApplyLogoArrivingDefaults(logo); + } + + private void applyArrivingDefaults(bool isResuming) + { + logo?.AppendAnimatingAction(() => + { + if (this.IsCurrentScreen()) LogoArriving(logo, isResuming); + }, true); + } + + /// + /// Applies default animations to an arriving logo. + /// Todo: This should not exist. + /// + /// The logo to apply animations to. + public static void ApplyLogoArrivingDefaults(OsuLogo logo) { logo.Action = null; logo.FadeOut(300, Easing.OutQuint); @@ -216,24 +171,9 @@ namespace osu.Game.Screens logo.Ripple = true; } - private void applyArrivingDefaults(bool isResuming) - { - logo.AppendAnimatingAction(() => - { - if (IsCurrentScreen) LogoArriving(logo, isResuming); - }, true); - - if (backgroundParallaxContainer != null) - backgroundParallaxContainer.ParallaxAmount = ParallaxContainer.DEFAULT_PARALLAX_AMOUNT * BackgroundParallaxAmount; - - OverlayActivationMode.Value = InitialOverlayActivationMode; - - updateOverlayStates?.Invoke(); - } - private void onExitingLogo() { - logo.AppendAnimatingAction(() => { LogoExiting(logo); }, false); + logo?.AppendAnimatingAction(() => LogoExiting(logo), false); } /// @@ -245,7 +185,7 @@ namespace osu.Game.Screens private void onSuspendingLogo() { - logo.AppendAnimatingAction(() => { LogoSuspending(logo); }, false); + logo?.AppendAnimatingAction(() => LogoSuspending(logo), false); } /// @@ -254,5 +194,11 @@ namespace osu.Game.Screens protected virtual void LogoSuspending(OsuLogo logo) { } + + /// + /// Override to create a BackgroundMode for the current screen. + /// Note that the instance created may not be the used instance if it matches the BackgroundMode equality clause. + /// + protected virtual BackgroundScreen CreateBackground() => null; } } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 54644b8d9c..71b7b77e5d 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -40,11 +40,11 @@ namespace osu.Game.Screens.Play { protected override bool AllowBackButton => false; // handled by HoldForMenuButton - protected override float BackgroundParallaxAmount => 0.1f; + public override float BackgroundParallaxAmount => 0.1f; - protected override bool HideOverlaysOnEnter => true; + public override bool HideOverlaysOnEnter => true; - protected override OverlayActivation InitialOverlayActivationMode => OverlayActivation.UserTriggered; + public override OverlayActivation InitialOverlayActivationMode => OverlayActivation.UserTriggered; public Action RestartRequested; @@ -166,7 +166,7 @@ namespace osu.Game.Screens.Play if (!ScoreProcessor.Mode.Disabled) config.BindWith(OsuSetting.ScoreDisplayMode, ScoreProcessor.Mode); - Children = new Drawable[] + InternalChildren = new Drawable[] { pauseContainer = new PauseContainer(offsetClock, adjustableClock) { @@ -225,7 +225,7 @@ namespace osu.Game.Screens.Play { Action = () => { - if (!IsCurrentScreen) return; + if (!this.IsCurrentScreen()) return; fadeOut(true); Restart(); @@ -260,18 +260,18 @@ namespace osu.Game.Screens.Play private void performUserRequestedExit() { - if (!IsCurrentScreen) return; - Exit(); + if (!this.IsCurrentScreen()) return; + this.Exit(); } public void Restart() { - if (!IsCurrentScreen) return; + if (!this.IsCurrentScreen()) return; sampleRestart?.Play(); ValidForResume = false; RestartRequested?.Invoke(); - Exit(); + this.Exit(); } private ScheduledDelegate onCompletionEvent; @@ -290,13 +290,13 @@ namespace osu.Game.Screens.Play { onCompletionEvent = Schedule(delegate { - if (!IsCurrentScreen) return; + if (!this.IsCurrentScreen()) return; var score = CreateScore(); if (RulesetContainer.ReplayScore == null) scoreManager.Import(score, true); - Push(CreateResults(score)); + this.Push(CreateResults(score)); onCompletionEvent = null; }); @@ -331,15 +331,15 @@ namespace osu.Game.Screens.Play return true; } - protected override void OnEntering(Screen last) + public override void OnEntering(IScreen last) { base.OnEntering(last); if (!LoadedBeatmapSuccessfully) return; - Content.Alpha = 0; - Content + Alpha = 0; + this .ScaleTo(0.7f) .ScaleTo(1, 750, Easing.OutQuint) .Delay(250) @@ -368,13 +368,13 @@ namespace osu.Game.Screens.Play pauseContainer.FadeIn(750, Easing.OutQuint); } - protected override void OnSuspending(Screen next) + public override void OnSuspending(IScreen next) { fadeOut(); base.OnSuspending(next); } - protected override bool OnExiting(Screen next) + public override bool OnExiting(IScreen next) { if (onCompletionEvent != null) { @@ -401,7 +401,7 @@ namespace osu.Game.Screens.Play private void fadeOut(bool instant = false) { float fadeOutDuration = instant ? 0 : 250; - Content.FadeOut(fadeOutDuration); + this.FadeOut(fadeOutDuration); Background?.FadeColour(Color4.White, fadeOutDuration, Easing.OutQuint); } @@ -425,7 +425,7 @@ namespace osu.Game.Screens.Play protected override void UpdateBackgroundElements() { - if (!IsCurrentScreen) return; + if (!this.IsCurrentScreen()) return; base.UpdateBackgroundElements(); diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index be3fdc8d14..58e59604dd 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -30,10 +30,12 @@ namespace osu.Game.Screens.Play private Player player; + private Container content; + private BeatmapMetadataDisplay info; private bool hideOverlays; - protected override bool HideOverlaysOnEnter => hideOverlays; + public override bool HideOverlaysOnEnter => hideOverlays; private Task loadTask; @@ -51,34 +53,42 @@ namespace osu.Game.Screens.Play [BackgroundDependencyLoader] private void load() { - Add(info = new BeatmapMetadataDisplay(Beatmap.Value) + InternalChild = content = new Container { - Alpha = 0, Anchor = Anchor.Centre, Origin = Anchor.Centre, - }); - - Add(new FillFlowContainer - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 20), - Margin = new MarginPadding(25), - Children = new PlayerSettingsGroup[] + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] { - visualSettings = new VisualSettings(), - new InputSettings() + info = new BeatmapMetadataDisplay(Beatmap.Value) + { + Alpha = 0, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + new FillFlowContainer + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 20), + Margin = new MarginPadding(25), + Children = new PlayerSettingsGroup[] + { + visualSettings = new VisualSettings(), + new InputSettings() + } + } } - }); + }; loadNewPlayer(); } private void playerLoaded(Player player) => info.Loading = false; - protected override void OnResuming(Screen last) + public override void OnResuming(IScreen last) { base.OnResuming(last); @@ -105,21 +115,21 @@ namespace osu.Game.Screens.Play private void contentIn() { - Content.ScaleTo(1, 650, Easing.OutQuint); - Content.FadeInFromZero(400); + content.ScaleTo(1, 650, Easing.OutQuint); + content.FadeInFromZero(400); } private void contentOut() { - Content.ScaleTo(0.7f, 300, Easing.InQuint); - Content.FadeOut(250); + content.ScaleTo(0.7f, 300, Easing.InQuint); + content.FadeOut(250); } - protected override void OnEntering(Screen last) + public override void OnEntering(IScreen last) { base.OnEntering(last); - Content.ScaleTo(0.7f); + content.ScaleTo(0.7f); contentIn(); @@ -154,11 +164,12 @@ namespace osu.Game.Screens.Play protected override void OnHoverLost(HoverLostEvent e) { - if (GetContainingInputManager().HoveredDrawables.Contains(visualSettings)) + if (GetContainingInputManager()?.HoveredDrawables.Contains(visualSettings) == true) { // show user setting preview UpdateBackgroundElements(); } + base.OnHoverLost(e); } @@ -170,7 +181,7 @@ namespace osu.Game.Screens.Play private void pushWhenLoaded() { - if (!IsCurrentScreen) return; + if (!this.IsCurrentScreen()) return; try { @@ -191,7 +202,7 @@ namespace osu.Game.Screens.Play this.Delay(250).Schedule(() => { - if (!IsCurrentScreen) return; + if (!this.IsCurrentScreen()) return; loadTask = null; @@ -200,9 +211,9 @@ namespace osu.Game.Screens.Play ValidForResume = false; if (player.LoadedBeatmapSuccessfully) - Push(player); + this.Push(player); else - Exit(); + this.Exit(); }); }, 500); } @@ -218,15 +229,15 @@ namespace osu.Game.Screens.Play pushDebounce = null; } - protected override void OnSuspending(Screen next) + public override void OnSuspending(IScreen next) { base.OnSuspending(next); cancelLoad(); } - protected override bool OnExiting(Screen next) + public override bool OnExiting(IScreen next) { - Content.ScaleTo(0.7f, 150, Easing.InQuint); + content.ScaleTo(0.7f, 150, Easing.InQuint); this.FadeOut(150); cancelLoad(); diff --git a/osu.Game/Screens/Play/ScreenWithBeatmapBackground.cs b/osu.Game/Screens/Play/ScreenWithBeatmapBackground.cs index 1c127dbdef..93ec7347c8 100644 --- a/osu.Game/Screens/Play/ScreenWithBeatmapBackground.cs +++ b/osu.Game/Screens/Play/ScreenWithBeatmapBackground.cs @@ -16,7 +16,7 @@ namespace osu.Game.Screens.Play { protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap.Value); - protected new BackgroundScreenBeatmap Background => (BackgroundScreenBeatmap)base.Background; + protected new BackgroundScreenBeatmap Background => base.Background as BackgroundScreenBeatmap; public override bool AllowBeatmapRulesetChange => false; @@ -40,7 +40,7 @@ namespace osu.Game.Screens.Play ShowStoryboard = config.GetBindable(OsuSetting.ShowStoryboard); } - protected override void OnEntering(Screen last) + public override void OnEntering(IScreen last) { base.OnEntering(last); DimLevel.ValueChanged += _ => UpdateBackgroundElements(); @@ -49,7 +49,7 @@ namespace osu.Game.Screens.Play InitializeBackgroundElements(); } - protected override void OnResuming(Screen last) + public override void OnResuming(IScreen last) { base.OnResuming(last); InitializeBackgroundElements(); @@ -66,7 +66,7 @@ namespace osu.Game.Screens.Play /// protected virtual void UpdateBackgroundElements() { - if (!IsCurrentScreen) return; + if (!this.IsCurrentScreen()) return; Background?.FadeColour(OsuColour.Gray(BackgroundOpacity), BACKGROUND_FADE_DURATION, Easing.OutQuint); Background?.BlurTo(new Vector2((float)BlurLevel.Value * 25), BACKGROUND_FADE_DURATION, Easing.OutQuint); diff --git a/osu.Game/Screens/Ranking/Results.cs b/osu.Game/Screens/Ranking/Results.cs index d17a19a7ce..31863cea9b 100644 --- a/osu.Game/Screens/Ranking/Results.cs +++ b/osu.Game/Screens/Ranking/Results.cs @@ -55,7 +55,7 @@ namespace osu.Game.Screens.Ranking private IEnumerable allCircles => new Drawable[] { circleOuterBackground, circleInner, circleOuter }; - protected override void OnEntering(Screen last) + public override void OnEntering(IScreen last) { base.OnEntering(last); (Background as BackgroundScreenBeatmap)?.BlurTo(background_blur, 2500, Easing.OutQuint); @@ -98,7 +98,7 @@ namespace osu.Game.Screens.Ranking } } - protected override bool OnExiting(Screen next) + public override bool OnExiting(IScreen next) { allCircles.ForEach(c => { @@ -107,7 +107,7 @@ namespace osu.Game.Screens.Ranking Background.ScaleTo(1f, transition_time / 4, Easing.OutQuint); - Content.FadeOut(transition_time / 4); + this.FadeOut(transition_time / 4); return base.OnExiting(next); } @@ -115,7 +115,7 @@ namespace osu.Game.Screens.Ranking [BackgroundDependencyLoader] private void load(OsuColour colours) { - Children = new Drawable[] + InternalChildren = new Drawable[] { new AspectContainer { @@ -260,7 +260,7 @@ namespace osu.Game.Screens.Ranking { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, - Action = Exit + Action = this.Exit }, }; diff --git a/osu.Game/Screens/ScreenWhiteBox.cs b/osu.Game/Screens/ScreenWhiteBox.cs index a513015d93..d250416b29 100644 --- a/osu.Game/Screens/ScreenWhiteBox.cs +++ b/osu.Game/Screens/ScreenWhiteBox.cs @@ -30,7 +30,7 @@ namespace osu.Game.Screens protected override BackgroundScreen CreateBackground() => new BackgroundScreenCustom(@"Backgrounds/bg2"); - protected override void OnEntering(Screen last) + public override void OnEntering(IScreen last) { base.OnEntering(last); @@ -38,51 +38,51 @@ namespace osu.Game.Screens if (last != null) popButton.Alpha = 1; - Content.Alpha = 0; + Alpha = 0; textContainer.Position = new Vector2(DrawSize.X / 16, 0); boxContainer.ScaleTo(0.2f); boxContainer.RotateTo(-20); - using (Content.BeginDelayedSequence(300, true)) + using (BeginDelayedSequence(300, true)) { boxContainer.ScaleTo(1, transition_time, Easing.OutElastic); boxContainer.RotateTo(0, transition_time / 2, Easing.OutQuint); textContainer.MoveTo(Vector2.Zero, transition_time, Easing.OutExpo); - Content.FadeIn(transition_time, Easing.OutExpo); + this.FadeIn(transition_time, Easing.OutExpo); } } - protected override bool OnExiting(Screen next) + public override bool OnExiting(IScreen next) { textContainer.MoveTo(new Vector2(DrawSize.X / 16, 0), transition_time, Easing.OutExpo); - Content.FadeOut(transition_time, Easing.OutExpo); + this.FadeOut(transition_time, Easing.OutExpo); return base.OnExiting(next); } - protected override void OnSuspending(Screen next) + public override void OnSuspending(IScreen next) { base.OnSuspending(next); textContainer.MoveTo(new Vector2(-(DrawSize.X / 16), 0), transition_time, Easing.OutExpo); - Content.FadeOut(transition_time, Easing.OutExpo); + this.FadeOut(transition_time, Easing.OutExpo); } - protected override void OnResuming(Screen last) + public override void OnResuming(IScreen last) { base.OnResuming(last); textContainer.MoveTo(Vector2.Zero, transition_time, Easing.OutExpo); - Content.FadeIn(transition_time, Easing.OutExpo); + this.FadeIn(transition_time, Easing.OutExpo); } public ScreenWhiteBox() { FillFlowContainer childModeButtons; - Children = new Drawable[] + InternalChildren = new Drawable[] { boxContainer = new Container { @@ -148,7 +148,7 @@ namespace osu.Game.Screens Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, Alpha = 0, - Action = Exit + Action = this.Exit }, childModeButtons = new FillFlowContainer { @@ -171,7 +171,7 @@ namespace osu.Game.Screens HoverColour = getColourFor(t).Lighten(0.2f), Action = delegate { - Push(Activator.CreateInstance(t) as Screen); + this.Push(Activator.CreateInstance(t) as Screen); } }); } diff --git a/osu.Game/Screens/Select/EditSongSelect.cs b/osu.Game/Screens/Select/EditSongSelect.cs index 30b5115770..bdf5f905fe 100644 --- a/osu.Game/Screens/Select/EditSongSelect.cs +++ b/osu.Game/Screens/Select/EditSongSelect.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 osu.Framework.Screens; + namespace osu.Game.Screens.Select { public class EditSongSelect : SongSelect @@ -9,7 +11,7 @@ namespace osu.Game.Screens.Select protected override bool OnStart() { - Exit(); + this.Exit(); return true; } } diff --git a/osu.Game/Screens/Select/MatchSongSelect.cs b/osu.Game/Screens/Select/MatchSongSelect.cs index 5435b2291e..298e936c1c 100644 --- a/osu.Game/Screens/Select/MatchSongSelect.cs +++ b/osu.Game/Screens/Select/MatchSongSelect.cs @@ -3,6 +3,8 @@ using System; using Humanizer; +using osu.Framework.Graphics; +using osu.Framework.Screens; using osu.Game.Online.Multiplayer; using osu.Game.Screens.Multi; @@ -15,6 +17,11 @@ namespace osu.Game.Screens.Select public string ShortTitle => "song selection"; public override string Title => ShortTitle.Humanize(); + public MatchSongSelect() + { + Padding = new MarginPadding { Horizontal = HORIZONTAL_OVERFLOW_PADDING }; + } + protected override bool OnStart() { var item = new PlaylistItem @@ -28,8 +35,8 @@ namespace osu.Game.Screens.Select Selected?.Invoke(item); - if (IsCurrentScreen) - Exit(); + if (this.IsCurrentScreen()) + this.Exit(); return true; } diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index 14f3ce6633..982a44a8d3 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -25,7 +25,7 @@ namespace osu.Game.Screens.Select }, Key.Number3); } - protected override void OnResuming(Screen last) + public override void OnResuming(IScreen last) { player = null; @@ -64,7 +64,7 @@ namespace osu.Game.Screens.Select LoadComponentAsync(player = new PlayerLoader(() => new Player()), l => { - if (IsCurrentScreen) Push(player); + if (this.IsCurrentScreen())this.Push(player); }); return true; diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index e30b5914be..d800cea736 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -88,7 +88,7 @@ namespace osu.Game.Screens.Select const float carousel_width = 640; const float filter_height = 100; - AddRange(new Drawable[] + AddRangeInternal(new Drawable[] { new ParallaxContainer { @@ -123,16 +123,6 @@ namespace osu.Game.Screens.Select Padding = new MarginPadding { Top = 10, Right = 5 }, } }, - beatmapInfoWedge = new BeatmapInfoWedge - { - Size = wedged_container_size, - RelativeSizeAxes = Axes.X, - Margin = new MarginPadding - { - Top = left_area_padding, - Right = left_area_padding, - }, - }, new Container { RelativeSizeAxes = Axes.Both, @@ -166,13 +156,23 @@ namespace osu.Game.Screens.Select Background = { Width = 2 }, Exit = () => { - if (IsCurrentScreen) - Exit(); + if (this.IsCurrentScreen()) + this.Exit(); }, }, } }, }, + beatmapInfoWedge = new BeatmapInfoWedge + { + Size = wedged_container_size, + RelativeSizeAxes = Axes.X, + Margin = new MarginPadding + { + Top = left_area_padding, + Right = left_area_padding, + }, + }, new ResetScrollContainer(() => Carousel.ScrollToSelected()) { RelativeSizeAxes = Axes.Y, @@ -182,7 +182,7 @@ namespace osu.Game.Screens.Select if (ShowFooter) { - Add(FooterPanels = new Container + AddInternal(FooterPanels = new Container { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, @@ -193,7 +193,7 @@ namespace osu.Game.Screens.Select Bottom = Footer.HEIGHT, }, }); - Add(Footer = new Footer + AddInternal(Footer = new Footer { OnBack = ExitFromBack, }); @@ -210,7 +210,7 @@ namespace osu.Game.Screens.Select }); } - BeatmapDetails.Leaderboard.ScoreSelected += s => Push(new SoloResults(s)); + BeatmapDetails.Leaderboard.ScoreSelected += s =>this.Push(new SoloResults(s)); } [BackgroundDependencyLoader(true)] @@ -281,13 +281,13 @@ namespace osu.Game.Screens.Select return; } - Exit(); + this.Exit(); } public void Edit(BeatmapInfo beatmap = null) { Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap ?? beatmapNoDebounce); - Push(new Editor()); + this.Push(new Editor()); } /// @@ -331,7 +331,7 @@ namespace osu.Game.Screens.Select { if (beatmap is DummyWorkingBeatmap) return; - if (IsCurrentScreen && !Carousel.SelectBeatmap(beatmap?.BeatmapInfo, false)) + if (this.IsCurrentScreen() && !Carousel.SelectBeatmap(beatmap?.BeatmapInfo, false)) // If selecting new beatmap without bypassing filters failed, there's possibly a ruleset mismatch if (beatmap?.BeatmapInfo?.Ruleset != null && beatmap.BeatmapInfo.Ruleset != Ruleset.Value) { @@ -411,7 +411,7 @@ namespace osu.Game.Screens.Select } } - if (IsCurrentScreen) ensurePlayingSelected(preview); + if (this.IsCurrentScreen()) ensurePlayingSelected(preview); UpdateBeatmap(Beatmap.Value); } @@ -431,11 +431,11 @@ namespace osu.Game.Screens.Select Carousel.SelectNextRandom(); } - protected override void OnEntering(Screen last) + public override void OnEntering(IScreen last) { base.OnEntering(last); - Content.FadeInFromZero(250); + this.FadeInFromZero(250); FilterControl.Activate(); } @@ -476,7 +476,7 @@ namespace osu.Game.Screens.Select logo.FadeOut(logo_transition / 2, Easing.Out); } - protected override void OnResuming(Screen last) + public override void OnResuming(IScreen last) { BeatmapDetails.Leaderboard.RefreshScores(); @@ -490,26 +490,26 @@ namespace osu.Game.Screens.Select base.OnResuming(last); - Content.FadeIn(250); + this.FadeIn(250); - Content.ScaleTo(1, 250, Easing.OutSine); + this.ScaleTo(1, 250, Easing.OutSine); FilterControl.Activate(); } - protected override void OnSuspending(Screen next) + public override void OnSuspending(IScreen next) { ModSelect.Hide(); - Content.ScaleTo(1.1f, 250, Easing.InSine); + this.ScaleTo(1.1f, 250, Easing.InSine); - Content.FadeOut(250); + this.FadeOut(250); FilterControl.Deactivate(); base.OnSuspending(next); } - protected override bool OnExiting(Screen next) + public override bool OnExiting(IScreen next) { if (ModSelect.State == Visibility.Visible) { @@ -521,7 +521,7 @@ namespace osu.Game.Screens.Select beatmapInfoWedge.State = Visibility.Hidden; - Content.FadeOut(100); + this.FadeOut(100); FilterControl.Deactivate(); @@ -627,7 +627,7 @@ namespace osu.Game.Screens.Select public override bool OnPressed(GlobalAction action) { - if (!IsCurrentScreen) return false; + if (!this.IsCurrentScreen()) return false; switch (action) { diff --git a/osu.Game/Screens/Tournament/Drawings.cs b/osu.Game/Screens/Tournament/Drawings.cs index dae53be8fe..43b194d8d0 100644 --- a/osu.Game/Screens/Tournament/Drawings.cs +++ b/osu.Game/Screens/Tournament/Drawings.cs @@ -22,6 +22,7 @@ using osuTK; using osuTK.Graphics; using osu.Framework.IO.Stores; using osu.Framework.Graphics.Shapes; +using osu.Framework.Screens; namespace osu.Game.Screens.Tournament { @@ -29,7 +30,7 @@ namespace osu.Game.Screens.Tournament { private const string results_filename = "drawings_results.txt"; - protected override bool HideOverlaysOnEnter => true; + public override bool HideOverlaysOnEnter => true; protected override BackgroundScreen CreateBackground() => new BackgroundScreenDefault(); @@ -70,13 +71,13 @@ namespace osu.Game.Screens.Tournament if (!TeamList.Teams.Any()) { - Exit(); + this.Exit(); return; } drawingsConfig = new DrawingsConfigManager(storage); - Children = new Drawable[] + InternalChildren = new Drawable[] { new Box { diff --git a/osu.Game/Tests/Visual/ScreenTestCase.cs b/osu.Game/Tests/Visual/ScreenTestCase.cs index df4af0e470..79c57ad9f4 100644 --- a/osu.Game/Tests/Visual/ScreenTestCase.cs +++ b/osu.Game/Tests/Visual/ScreenTestCase.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 osu.Framework.Allocation; +using osu.Framework.Graphics; using osu.Framework.Screens; using osu.Game.Screens; @@ -11,38 +13,25 @@ namespace osu.Game.Tests.Visual /// public abstract class ScreenTestCase : OsuTestCase { - private readonly TestOsuScreen baseScreen; + private readonly ScreenStack stack; + + [Cached] + private BackgroundScreenStack backgroundStack; protected ScreenTestCase() { - Add(baseScreen = new TestOsuScreen()); + Children = new Drawable[] + { + backgroundStack = new BackgroundScreenStack { RelativeSizeAxes = Axes.Both }, + stack = new ScreenStack { RelativeSizeAxes = Axes.Both } + }; } - protected void LoadScreen(OsuScreen screen) => baseScreen.LoadScreen(screen); - - public class TestOsuScreen : OsuScreen + protected void LoadScreen(OsuScreen screen) { - private OsuScreen nextScreen; - - public void LoadScreen(OsuScreen screen) => Schedule(() => - { - nextScreen = screen; - - if (IsCurrentScreen) - { - Push(screen); - nextScreen = null; - } - else - MakeCurrent(); - }); - - protected override void OnResuming(Screen last) - { - base.OnResuming(last); - if (nextScreen != null) - LoadScreen(nextScreen); - } + if (stack.CurrentScreen != null) + stack.Exit(); + stack.Push(screen); } } } diff --git a/osu.Game/Tests/Visual/TestCasePlayer.cs b/osu.Game/Tests/Visual/TestCasePlayer.cs index 008573cb8a..a926a06295 100644 --- a/osu.Game/Tests/Visual/TestCasePlayer.cs +++ b/osu.Game/Tests/Visual/TestCasePlayer.cs @@ -6,6 +6,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics.Shapes; using osu.Framework.Lists; +using osu.Framework.Screens; using osu.Game.Beatmaps; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 669c7a6d8d..596190fcf7 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -10,15 +10,13 @@ - - - - + + diff --git a/osu.TestProject.props b/osu.TestProject.props index 456ecfd468..a5c70f4edc 100644 --- a/osu.TestProject.props +++ b/osu.TestProject.props @@ -8,7 +8,6 @@ - diff --git a/osu.iOS.props b/osu.iOS.props index adc07d6bb8..e00c4fcf78 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -104,8 +104,9 @@ - - + + + diff --git a/osu.iOS.sln b/osu.iOS.sln index fe9741d767..21d02d33ab 100644 --- a/osu.iOS.sln +++ b/osu.iOS.sln @@ -5,8 +5,6 @@ VisualStudioVersion = 15.0.27004.2006 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game", "osu.Game\osu.Game.csproj", "{2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Resources", "osu-resources\osu.Game.Resources\osu.Game.Resources.csproj", "{D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Rulesets.Osu", "osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj", "{C92A607B-1FDD-4954-9F92-03FF547D9080}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Rulesets.Catch", "osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj", "{58F6C80C-1253-4A0E-A465-B8C85EBEADF3}" diff --git a/osu.iOS/osu.iOS.csproj b/osu.iOS/osu.iOS.csproj index fda26ddc94..9c69dd40ba 100644 --- a/osu.iOS/osu.iOS.csproj +++ b/osu.iOS/osu.iOS.csproj @@ -57,10 +57,6 @@ {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D} osu.Game - - {D9A367C9-4C1A-489F-9B05-A0CEA2B53B58} - osu.Game.Resources - {58F6C80C-1253-4A0E-A465-B8C85EBEADF3} osu.Game.Rulesets.Catch diff --git a/osu.sln b/osu.sln index 0737a9fbd8..3c38309d86 100644 --- a/osu.sln +++ b/osu.sln @@ -5,8 +5,6 @@ VisualStudioVersion = 15.0.27004.2006 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game", "osu.Game\osu.Game.csproj", "{2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Resources", "osu-resources\osu.Game.Resources\osu.Game.Resources.csproj", "{D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Rulesets.Osu", "osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj", "{C92A607B-1FDD-4954-9F92-03FF547D9080}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Rulesets.Catch", "osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj", "{58F6C80C-1253-4A0E-A465-B8C85EBEADF3}" @@ -31,10 +29,6 @@ Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU - Debug|iPhoneSimulator = Debug|iPhoneSimulator - Release|iPhone = Release|iPhone - Release|iPhoneSimulator = Release|iPhoneSimulator - Debug|iPhone = Debug|iPhone EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU