From 8eb8572c738b549be8915ea809a7ad20d600e307 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 11 Mar 2020 18:00:39 +0900 Subject: [PATCH 01/51] Apply osu!-side video sprite changes --- osu.Game.Tournament/Components/TourneyVideo.cs | 2 +- osu.Game/Screens/Menu/IntroTriangles.cs | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tournament/Components/TourneyVideo.cs b/osu.Game.Tournament/Components/TourneyVideo.cs index 43088d6b92..81620b017c 100644 --- a/osu.Game.Tournament/Components/TourneyVideo.cs +++ b/osu.Game.Tournament/Components/TourneyVideo.cs @@ -34,7 +34,7 @@ namespace osu.Game.Tournament.Components if (stream != null) { - InternalChild = video = new VideoSprite(stream) + InternalChild = video = new VideoSprite(stream, false) { RelativeSizeAxes = Axes.Both, FillMode = FillMode.Fit, diff --git a/osu.Game/Screens/Menu/IntroTriangles.cs b/osu.Game/Screens/Menu/IntroTriangles.cs index 4e51ff939a..be5762e68d 100644 --- a/osu.Game/Screens/Menu/IntroTriangles.cs +++ b/osu.Game/Screens/Menu/IntroTriangles.cs @@ -259,11 +259,18 @@ namespace osu.Game.Screens.Menu private class LazerLogo : CompositeDrawable { + private readonly Stream videoStream; + public LazerLogo(Stream videoStream) { + this.videoStream = videoStream; Size = new Vector2(960); + } - InternalChild = new VideoSprite(videoStream) + [BackgroundDependencyLoader] + private void load() + { + InternalChild = new VideoSprite(videoStream, false) { RelativeSizeAxes = Axes.Both, Clock = new FramedOffsetClock(Clock) { Offset = -logo_1 } From 758bb3711f6f7105296a1922b0108a4129743013 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 11 Mar 2020 18:07:11 +0900 Subject: [PATCH 02/51] Add more sane limit for maximum slider length --- osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 1fc51d2ce8..8d3ad5984f 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -126,7 +126,7 @@ namespace osu.Game.Rulesets.Objects.Legacy if (split.Length > 7) { - length = Math.Max(0, Parsing.ParseDouble(split[7])); + length = Math.Max(0, Parsing.ParseDouble(split[7], Parsing.MAX_COORDINATE_VALUE)); if (length == 0) length = null; } From 9667934ed9d0a7a44f55af4c7515fdb0df3bded8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 11 Mar 2020 18:17:32 +0900 Subject: [PATCH 03/51] Remove unlimited timing points in difficulty calculation --- .../LegacyDifficultyCalculatorBeatmapDecoder.cs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyDifficultyCalculatorBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDifficultyCalculatorBeatmapDecoder.cs index 527f520172..bf52a87865 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDifficultyCalculatorBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDifficultyCalculatorBeatmapDecoder.cs @@ -26,17 +26,5 @@ namespace osu.Game.Beatmaps.Formats AddDecoder(@"osu file format v", m => new LegacyDifficultyCalculatorBeatmapDecoder(int.Parse(m.Split('v').Last()))); SetFallbackDecoder(() => new LegacyDifficultyCalculatorBeatmapDecoder()); } - - protected override TimingControlPoint CreateTimingControlPoint() - => new LegacyDifficultyCalculatorTimingControlPoint(); - - private class LegacyDifficultyCalculatorTimingControlPoint : TimingControlPoint - { - public LegacyDifficultyCalculatorTimingControlPoint() - { - BeatLengthBindable.MinValue = double.MinValue; - BeatLengthBindable.MaxValue = double.MaxValue; - } - } } } From 40ab860ab56f7b89264c0c7c3475d680a09c0951 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 11 Mar 2020 18:23:30 +0900 Subject: [PATCH 04/51] Remove unused using --- .../Beatmaps/Formats/LegacyDifficultyCalculatorBeatmapDecoder.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyDifficultyCalculatorBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDifficultyCalculatorBeatmapDecoder.cs index bf52a87865..3420fcf260 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDifficultyCalculatorBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDifficultyCalculatorBeatmapDecoder.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System.Linq; -using osu.Game.Beatmaps.ControlPoints; namespace osu.Game.Beatmaps.Formats { From 5b03b3e36304b1546c671aa875ce3d97bec79ced Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 11 Mar 2020 18:36:37 +0900 Subject: [PATCH 05/51] Fix hyperdashes not recalculated with HR application --- .../Beatmaps/CatchBeatmapProcessor.cs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index 1a5d0f983b..e0c65a8317 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -28,8 +28,6 @@ namespace osu.Game.Rulesets.Catch.Beatmaps ApplyPositionOffsets(Beatmap); - initialiseHyperDash((List)Beatmap.HitObjects); - int index = 0; foreach (var obj in Beatmap.HitObjects.OfType()) @@ -90,6 +88,8 @@ namespace osu.Game.Rulesets.Catch.Beatmaps break; } } + + initialiseHyperDash(beatmap); } private static void applyHardRockOffset(CatchHitObject hitObject, ref float? lastPosition, ref double lastStartTime, FastRandom rng) @@ -191,14 +191,14 @@ namespace osu.Game.Rulesets.Catch.Beatmaps } } - private void initialiseHyperDash(List objects) + private static void initialiseHyperDash(IBeatmap beatmap) { List objectWithDroplets = new List(); - foreach (var currentObject in objects) + foreach (var currentObject in beatmap.HitObjects) { - if (currentObject is Fruit) - objectWithDroplets.Add(currentObject); + if (currentObject is Fruit fruitObject) + objectWithDroplets.Add(fruitObject); if (currentObject is JuiceStream) { @@ -212,7 +212,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps objectWithDroplets.Sort((h1, h2) => h1.StartTime.CompareTo(h2.StartTime)); - double halfCatcherWidth = CatcherArea.GetCatcherSize(Beatmap.BeatmapInfo.BaseDifficulty) / 2; + double halfCatcherWidth = CatcherArea.GetCatcherSize(beatmap.BeatmapInfo.BaseDifficulty) / 2; int lastDirection = 0; double lastExcess = halfCatcherWidth; @@ -221,6 +221,10 @@ namespace osu.Game.Rulesets.Catch.Beatmaps CatchHitObject currentObject = objectWithDroplets[i]; CatchHitObject nextObject = objectWithDroplets[i + 1]; + // Reset variables in-case values have changed (e.g. after applying HR) + currentObject.HyperDashTarget = null; + currentObject.DistanceToHyperDash = 0; + int thisDirection = nextObject.X > currentObject.X ? 1 : -1; double timeToNext = nextObject.StartTime - currentObject.StartTime - 1000f / 60f / 4; // 1/4th of a frame of grace time, taken from osu-stable double distanceToNext = Math.Abs(nextObject.X - currentObject.X) - (lastDirection == thisDirection ? lastExcess : halfCatcherWidth); From f8e7579f4574aee46ae278fe853e43864d047710 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 11 Mar 2020 18:37:58 +0900 Subject: [PATCH 06/51] Fix juice stream position reset not ever being applied --- .../Beatmaps/CatchBeatmapProcessor.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index e0c65a8317..e76e95e9aa 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -74,6 +74,12 @@ namespace osu.Game.Rulesets.Catch.Beatmaps break; case JuiceStream juiceStream: + // Todo: BUG!! Stable used the last control point as the final position of the path, but it should use the computed path instead. + lastPosition = juiceStream.X + juiceStream.Path.ControlPoints[^1].Position.Value.X / CatchPlayfield.BASE_WIDTH; + + // Todo: BUG!! Stable attempted to use the end time of the stream, but referenced it too early in execution and used the start time instead. + lastStartTime = juiceStream.StartTime; + foreach (var nested in juiceStream.NestedHitObjects) { var catchObject = (CatchHitObject)nested; @@ -94,13 +100,6 @@ namespace osu.Game.Rulesets.Catch.Beatmaps private static void applyHardRockOffset(CatchHitObject hitObject, ref float? lastPosition, ref double lastStartTime, FastRandom rng) { - if (hitObject is JuiceStream stream) - { - lastPosition = stream.EndX; - lastStartTime = stream.EndTime; - return; - } - if (!(hitObject is Fruit)) return; From 919410c6277644329b96a6724a5045b6a94072ed Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 11 Mar 2020 18:39:47 +0900 Subject: [PATCH 07/51] Remove always-false condition --- osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index e76e95e9aa..5f23bf1428 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -100,9 +100,6 @@ namespace osu.Game.Rulesets.Catch.Beatmaps private static void applyHardRockOffset(CatchHitObject hitObject, ref float? lastPosition, ref double lastStartTime, FastRandom rng) { - if (!(hitObject is Fruit)) - return; - float offsetPosition = hitObject.X; double startTime = hitObject.StartTime; From 5c051027e707db7fea7bf6a11b121249d44afd70 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 11 Mar 2020 18:43:08 +0900 Subject: [PATCH 08/51] Fix different offset being applied from stable --- osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index 5f23bf1428..986dc9dbb9 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -112,7 +112,9 @@ namespace osu.Game.Rulesets.Catch.Beatmaps } float positionDiff = offsetPosition - lastPosition.Value; - double timeDiff = startTime - lastStartTime; + + // Todo: BUG!! Stable calculated time deltas as ints, which affects randomisation. This should be changed to a double. + int timeDiff = (int)(startTime - lastStartTime); if (timeDiff > 1000) { @@ -128,7 +130,8 @@ namespace osu.Game.Rulesets.Catch.Beatmaps return; } - if (Math.Abs(positionDiff * CatchPlayfield.BASE_WIDTH) < timeDiff / 3d) + // ReSharper disable once PossibleLossOfFraction + if (Math.Abs(positionDiff * CatchPlayfield.BASE_WIDTH) < timeDiff / 3) applyOffset(ref offsetPosition, positionDiff); hitObject.XOffset = offsetPosition - hitObject.X; From 2866d626531f678e07c81b44ab27d7fecae95d87 Mon Sep 17 00:00:00 2001 From: Olle Kelderman Date: Wed, 11 Mar 2020 16:17:28 +0100 Subject: [PATCH 09/51] Use environment variable for initializing osuInstallPath --- osu.Game.Tournament/IPC/FileBasedIPC.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/osu.Game.Tournament/IPC/FileBasedIPC.cs b/osu.Game.Tournament/IPC/FileBasedIPC.cs index b19f2bedf0..eefa9fcfe6 100644 --- a/osu.Game.Tournament/IPC/FileBasedIPC.cs +++ b/osu.Game.Tournament/IPC/FileBasedIPC.cs @@ -163,12 +163,7 @@ namespace osu.Game.Tournament.IPC { try { - stableInstallPath = "G:\\My Drive\\Main\\osu!tourney"; - - if (checkExists(stableInstallPath)) - return stableInstallPath; - - stableInstallPath = "G:\\My Drive\\Main\\osu!mappool"; + stableInstallPath = Environment.GetEnvironmentVariable("OSU_STABLE_PATH"); if (checkExists(stableInstallPath)) return stableInstallPath; From efceeba0769999db723bb74d0f0a2b5c143270c2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Mar 2020 02:22:02 +0900 Subject: [PATCH 10/51] Use fixed width for tournament score displays --- .../Screens/Gameplay/Components/MatchScoreDisplay.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs b/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs index ed14956793..2e7484542a 100644 --- a/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs +++ b/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs @@ -11,6 +11,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Tournament.IPC; using osu.Game.Tournament.Models; +using osuTK; namespace osu.Game.Tournament.Screens.Gameplay.Components { @@ -131,13 +132,15 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components Margin = new MarginPadding { Top = bar_height, Horizontal = 10 }; Winning = false; + + DisplayedCountSpriteText.Spacing = new Vector2(-6); } public bool Winning { set => DisplayedCountSpriteText.Font = value - ? OsuFont.Torus.With(weight: FontWeight.Bold, size: 50) - : OsuFont.Torus.With(weight: FontWeight.Regular, size: 40); + ? OsuFont.Torus.With(weight: FontWeight.Bold, size: 50, fixedWidth: true) + : OsuFont.Torus.With(weight: FontWeight.Regular, size: 40, fixedWidth: true); } } } From 09b9983286d5a1a7f960ed968f33a2b3bc139c67 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 11 Mar 2020 21:14:07 +0300 Subject: [PATCH 11/51] Fix CatcherAnimationState is Fail if missing banana shower --- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 9ee94636f1..d18f5e165f 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -345,7 +345,10 @@ namespace osu.Game.Rulesets.Catch.UI if (validCatch) updateState(fruit.Kiai ? CatcherAnimationState.Kiai : CatcherAnimationState.Idle); else - updateState(CatcherAnimationState.Fail); + { + if (!(fruit is Banana)) + updateState(CatcherAnimationState.Fail); + } return validCatch; } From e46c070d951341370f16733da1d63c33c0f116c6 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 11 Mar 2020 23:09:29 +0300 Subject: [PATCH 12/51] Add test scene --- .../TestSceneDrawableHitObjects.cs | 53 +++++++++++++++---- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 10 ++-- 2 files changed, 48 insertions(+), 15 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs index 070847c0c1..304c7e3854 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; -using osu.Framework.Allocation; +using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; @@ -34,8 +34,8 @@ namespace osu.Game.Rulesets.Catch.Tests private DrawableCatchRuleset drawableRuleset; private double playfieldTime => drawableRuleset.Playfield.Time.Current; - [BackgroundDependencyLoader] - private void load() + [SetUp] + public void Setup() => Schedule(() => { var controlPointInfo = new ControlPointInfo(); controlPointInfo.Add(0, new TimingControlPoint()); @@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Catch.Tests ControlPointInfo = controlPointInfo }); - Add(new Container + Child = new Container { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -66,16 +66,49 @@ namespace osu.Game.Rulesets.Catch.Tests { drawableRuleset = new DrawableCatchRuleset(new CatchRuleset(), beatmap.GetPlayableBeatmap(new CatchRuleset().RulesetInfo)) } - }); + }; + }); + + [Test] + public void TestFruits() + { + AddStep("hit fruits", () => spawnFruits(true)); + AddUntilStep("wait for completion", () => playfieldIsEmpty); + AddAssert("catcher state is idle", () => catcherState == CatcherAnimationState.Idle); AddStep("miss fruits", () => spawnFruits()); - AddStep("hit fruits", () => spawnFruits(true)); - AddStep("miss juicestream", () => spawnJuiceStream()); - AddStep("hit juicestream", () => spawnJuiceStream(true)); - AddStep("miss bananas", () => spawnBananas()); - AddStep("hit bananas", () => spawnBananas(true)); + AddUntilStep("wait for completion", () => playfieldIsEmpty); + AddAssert("catcher state is failed", () => catcherState == CatcherAnimationState.Fail); } + [Test] + public void TestJuicestream() + { + AddStep("hit juicestream", () => spawnJuiceStream(true)); + AddUntilStep("wait for completion", () => playfieldIsEmpty); + AddAssert("catcher state is idle", () => catcherState == CatcherAnimationState.Idle); + + AddStep("miss juicestream", () => spawnJuiceStream()); + AddUntilStep("wait for completion", () => playfieldIsEmpty); + AddAssert("catcher state is failed", () => catcherState == CatcherAnimationState.Fail); + } + + [Test] + public void TestBananas() + { + AddStep("hit bananas", () => spawnBananas(true)); + AddUntilStep("wait for completion", () => playfieldIsEmpty); + AddAssert("catcher state is idle", () => catcherState == CatcherAnimationState.Idle); + + AddStep("miss bananas", () => spawnBananas()); + AddUntilStep("wait for completion", () => playfieldIsEmpty); + AddAssert("catcher state is idle", () => catcherState == CatcherAnimationState.Idle); + } + + private bool playfieldIsEmpty => !((CatchPlayfield)drawableRuleset.Playfield).AllHitObjects.Any(h => h.IsAlive); + + private CatcherAnimationState catcherState => ((CatchPlayfield)drawableRuleset.Playfield).CatcherArea.MovableCatcher.CurrentState; + private void spawnFruits(bool hit = false) { for (int i = 1; i <= 4; i++) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index d18f5e165f..441f9126f6 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -188,7 +188,7 @@ namespace osu.Game.Rulesets.Catch.UI CatcherSprite current; - switch (currentState) + switch (CurrentState) { default: current = catcherIdle; @@ -274,7 +274,7 @@ namespace osu.Game.Rulesets.Catch.UI return additive; } - private Drawable createCatcherSprite() => new CatcherSprite(currentState); + private Drawable createCatcherSprite() => new CatcherSprite(CurrentState); /// /// Add a caught fruit to the catcher's stack. @@ -355,14 +355,14 @@ namespace osu.Game.Rulesets.Catch.UI private void updateState(CatcherAnimationState state) { - if (currentState == state) + if (CurrentState == state) return; - currentState = state; + CurrentState = state; updateCatcher(); } - private CatcherAnimationState currentState; + public CatcherAnimationState CurrentState; private double hyperDashModifier = 1; private int hyperDashDirection; From fd21e87670ebde3018e43a550aff6ef9b30383f9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Mar 2020 11:28:45 +0900 Subject: [PATCH 13/51] Disable adjusting volume via "select next" and "select previous" as fallbacks --- .../Overlays/Volume/VolumeControlReceptor.cs | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/osu.Game/Overlays/Volume/VolumeControlReceptor.cs b/osu.Game/Overlays/Volume/VolumeControlReceptor.cs index 4bff8146b4..3478f18a40 100644 --- a/osu.Game/Overlays/Volume/VolumeControlReceptor.cs +++ b/osu.Game/Overlays/Volume/VolumeControlReceptor.cs @@ -14,22 +14,8 @@ namespace osu.Game.Overlays.Volume public Func ActionRequested; public Func ScrollActionRequested; - public bool OnPressed(GlobalAction action) - { - // if nothing else handles selection actions in the game, it's safe to let volume be adjusted. - switch (action) - { - case GlobalAction.SelectPrevious: - action = GlobalAction.IncreaseVolume; - break; - - case GlobalAction.SelectNext: - action = GlobalAction.DecreaseVolume; - break; - } - - return ActionRequested?.Invoke(action) ?? false; - } + public bool OnPressed(GlobalAction action) => + ActionRequested?.Invoke(action) ?? false; public bool OnScroll(GlobalAction action, float amount, bool isPrecise) => ScrollActionRequested?.Invoke(action, amount, isPrecise) ?? false; From 39bb98bfb219a2c174c1193850ce500eb0b5aeba Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Mar 2020 13:26:58 +0900 Subject: [PATCH 14/51] Allow videos to be loaded with any extension Also moves all tournament user resources to a "tournament" subfolder. --- .../Components/TourneyVideo.cs | 5 ++--- osu.Game.Tournament/TournamentGameBase.cs | 6 +++++- osu.Game.Tournament/TournamentStorage.cs | 19 +++++++++++++++++++ 3 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 osu.Game.Tournament/TournamentStorage.cs diff --git a/osu.Game.Tournament/Components/TourneyVideo.cs b/osu.Game.Tournament/Components/TourneyVideo.cs index 43088d6b92..d8488ce4f6 100644 --- a/osu.Game.Tournament/Components/TourneyVideo.cs +++ b/osu.Game.Tournament/Components/TourneyVideo.cs @@ -7,7 +7,6 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Video; -using osu.Framework.Platform; using osu.Framework.Timing; using osu.Game.Graphics; @@ -28,9 +27,9 @@ namespace osu.Game.Tournament.Components } [BackgroundDependencyLoader] - private void load(Storage storage) + private void load(TournamentStorage storage) { - var stream = storage.GetStream($@"videos/{filename}.m4v"); + var stream = storage.GetStream($@"videos/{filename}"); if (stream != null) { diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index 41165ca141..41822ae2c3 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -37,6 +37,8 @@ namespace osu.Game.Tournament private Storage storage; + private TournamentStorage tournamentStorage; + private DependencyContainer dependencies; private Bindable windowSize; @@ -54,7 +56,9 @@ namespace osu.Game.Tournament { Resources.AddStore(new DllResourceStore(typeof(TournamentGameBase).Assembly)); - Textures.AddStore(new TextureLoaderStore(new ResourceStore(new StorageBackedResourceStore(storage)))); + dependencies.CacheAs(tournamentStorage = new TournamentStorage(storage)); + + Textures.AddStore(new TextureLoaderStore(tournamentStorage)); this.storage = storage; diff --git a/osu.Game.Tournament/TournamentStorage.cs b/osu.Game.Tournament/TournamentStorage.cs new file mode 100644 index 0000000000..139ad3857b --- /dev/null +++ b/osu.Game.Tournament/TournamentStorage.cs @@ -0,0 +1,19 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.IO.Stores; +using osu.Framework.Platform; + +namespace osu.Game.Tournament +{ + internal class TournamentStorage : NamespacedResourceStore + { + public TournamentStorage(Storage storage) + : base(new StorageBackedResourceStore(storage), "tournament") + { + AddExtension("m4v"); + AddExtension("avi"); + AddExtension("mp4"); + } + } +} From 190ff974862e9e379917eeccd40034c546448980 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Mar 2020 13:29:09 +0900 Subject: [PATCH 15/51] Rename classes to better suit purpose --- .../Components/DrawableTournamentHeaderLogo.cs | 18 ++++++++++++++++++ .../Components/DrawableTournamentHeaderText.cs | 18 ++++++++++++++++++ .../Components/DrawableTournamentTitleText.cs | 16 ---------------- osu.Game.Tournament/Components/RoundDisplay.cs | 2 +- .../Screens/Gameplay/Components/MatchHeader.cs | 4 ++-- .../{RoundDisplay.cs => MatchRoundDisplay.cs} | 2 +- .../Screens/Ladder/LadderScreen.cs | 2 +- .../Screens/Schedule/ScheduleScreen.cs | 2 +- 8 files changed, 42 insertions(+), 22 deletions(-) create mode 100644 osu.Game.Tournament/Components/DrawableTournamentHeaderLogo.cs create mode 100644 osu.Game.Tournament/Components/DrawableTournamentHeaderText.cs delete mode 100644 osu.Game.Tournament/Components/DrawableTournamentTitleText.cs rename osu.Game.Tournament/Screens/Gameplay/Components/{RoundDisplay.cs => MatchRoundDisplay.cs} (92%) diff --git a/osu.Game.Tournament/Components/DrawableTournamentHeaderLogo.cs b/osu.Game.Tournament/Components/DrawableTournamentHeaderLogo.cs new file mode 100644 index 0000000000..b6f74a75e7 --- /dev/null +++ b/osu.Game.Tournament/Components/DrawableTournamentHeaderLogo.cs @@ -0,0 +1,18 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; + +namespace osu.Game.Tournament.Components +{ + public class DrawableTournamentHeaderLogo : Sprite + { + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + Texture = textures.Get("header-text"); + } + } +} diff --git a/osu.Game.Tournament/Components/DrawableTournamentHeaderText.cs b/osu.Game.Tournament/Components/DrawableTournamentHeaderText.cs new file mode 100644 index 0000000000..38500fa857 --- /dev/null +++ b/osu.Game.Tournament/Components/DrawableTournamentHeaderText.cs @@ -0,0 +1,18 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; + +namespace osu.Game.Tournament.Components +{ + public class DrawableTournamentHeaderText : Sprite + { + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + Texture = textures.Get("header-text"); + } + } +} diff --git a/osu.Game.Tournament/Components/DrawableTournamentTitleText.cs b/osu.Game.Tournament/Components/DrawableTournamentTitleText.cs deleted file mode 100644 index 4fbc6cd060..0000000000 --- a/osu.Game.Tournament/Components/DrawableTournamentTitleText.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Game.Graphics; - -namespace osu.Game.Tournament.Components -{ - public class DrawableTournamentTitleText : TournamentSpriteText - { - public DrawableTournamentTitleText() - { - Text = "osu!taiko world cup 2020"; - Font = OsuFont.Torus.With(size: 26, weight: FontWeight.SemiBold); - } - } -} diff --git a/osu.Game.Tournament/Components/RoundDisplay.cs b/osu.Game.Tournament/Components/RoundDisplay.cs index dd56c83c57..bebede6782 100644 --- a/osu.Game.Tournament/Components/RoundDisplay.cs +++ b/osu.Game.Tournament/Components/RoundDisplay.cs @@ -22,7 +22,7 @@ namespace osu.Game.Tournament.Components Direction = FillDirection.Vertical, Children = new Drawable[] { - new DrawableTournamentTitleText(), + new DrawableTournamentHeaderText(), new TournamentSpriteText { Text = match.Round.Value?.Name.Value ?? "Unknown Round", diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs b/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs index 69a68c946b..aa4bd4a701 100644 --- a/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs +++ b/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs @@ -41,13 +41,13 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components Spacing = new Vector2(5), Children = new Drawable[] { - new DrawableTournamentTitleText + new DrawableTournamentHeaderText { Anchor = Anchor.Centre, Origin = Anchor.Centre, Scale = new Vector2(1.2f) }, - new RoundDisplay + new MatchRoundDisplay { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/RoundDisplay.cs b/osu.Game.Tournament/Screens/Gameplay/Components/MatchRoundDisplay.cs similarity index 92% rename from osu.Game.Tournament/Screens/Gameplay/Components/RoundDisplay.cs rename to osu.Game.Tournament/Screens/Gameplay/Components/MatchRoundDisplay.cs index c8b0d3bdda..87793f7e1b 100644 --- a/osu.Game.Tournament/Screens/Gameplay/Components/RoundDisplay.cs +++ b/osu.Game.Tournament/Screens/Gameplay/Components/MatchRoundDisplay.cs @@ -8,7 +8,7 @@ using osu.Game.Tournament.Models; namespace osu.Game.Tournament.Screens.Gameplay.Components { - public class RoundDisplay : TournamentSpriteTextWithBackground + public class MatchRoundDisplay : TournamentSpriteTextWithBackground { private readonly Bindable currentMatch = new Bindable(); diff --git a/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs b/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs index 6f62b3ddba..534c402f6c 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs @@ -47,7 +47,7 @@ namespace osu.Game.Tournament.Screens.Ladder RelativeSizeAxes = Axes.Both, Loop = true, }, - new DrawableTournamentTitleText + new DrawableTournamentHeaderText { Y = 100, Anchor = Anchor.TopCentre, diff --git a/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs b/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs index 0fcec645e3..88289ad6bd 100644 --- a/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs +++ b/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs @@ -62,7 +62,7 @@ namespace osu.Game.Tournament.Screens.Schedule Direction = FillDirection.Vertical, Children = new Drawable[] { - new DrawableTournamentTitleText(), + new DrawableTournamentHeaderText(), new Container { Margin = new MarginPadding { Top = 40 }, From b6b802e8212f44a669675f6705fd16db667fd758 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Mar 2020 14:06:40 +0900 Subject: [PATCH 16/51] Add back customisable header logo/text Also adds test scene for MatchHeader component. --- .../Components/TestSceneMatchHeader.cs | 33 +++++ .../DrawableTournamentHeaderLogo.cs | 27 +++- .../DrawableTournamentHeaderText.cs | 27 +++- .../Gameplay/Components/MatchHeader.cs | 119 +++++++----------- .../Gameplay/Components/TeamScoreDisplay.cs | 83 ++++++++++++ 5 files changed, 206 insertions(+), 83 deletions(-) create mode 100644 osu.Game.Tournament.Tests/Components/TestSceneMatchHeader.cs create mode 100644 osu.Game.Tournament/Screens/Gameplay/Components/TeamScoreDisplay.cs diff --git a/osu.Game.Tournament.Tests/Components/TestSceneMatchHeader.cs b/osu.Game.Tournament.Tests/Components/TestSceneMatchHeader.cs new file mode 100644 index 0000000000..b29e4964b6 --- /dev/null +++ b/osu.Game.Tournament.Tests/Components/TestSceneMatchHeader.cs @@ -0,0 +1,33 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Game.Tournament.Screens.Gameplay.Components; +using osuTK; + +namespace osu.Game.Tournament.Tests.Components +{ + public class TestSceneMatchHeader : TournamentTestScene + { + public TestSceneMatchHeader() + { + Child = new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(50), + Children = new Drawable[] + { + new TournamentSpriteText { Text = "with logo", Font = OsuFont.Torus.With(size: 30) }, + new MatchHeader(), + new TournamentSpriteText { Text = "without logo", Font = OsuFont.Torus.With(size: 30) }, + new MatchHeader { ShowLogo = false }, + new TournamentSpriteText { Text = "without scores", Font = OsuFont.Torus.With(size: 30) }, + new MatchHeader { ShowScores = false }, + } + }; + } + } +} diff --git a/osu.Game.Tournament/Components/DrawableTournamentHeaderLogo.cs b/osu.Game.Tournament/Components/DrawableTournamentHeaderLogo.cs index b6f74a75e7..a61cb59fed 100644 --- a/osu.Game.Tournament/Components/DrawableTournamentHeaderLogo.cs +++ b/osu.Game.Tournament/Components/DrawableTournamentHeaderLogo.cs @@ -2,17 +2,36 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; namespace osu.Game.Tournament.Components { - public class DrawableTournamentHeaderLogo : Sprite + public class DrawableTournamentHeaderLogo : CompositeDrawable { - [BackgroundDependencyLoader] - private void load(TextureStore textures) + public DrawableTournamentHeaderLogo() { - Texture = textures.Get("header-text"); + InternalChild = new LogoSprite(); + + Height = 50; + RelativeSizeAxes = Axes.X; + } + + private class LogoSprite : Sprite + { + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + RelativeSizeAxes = Axes.Both; + FillMode = FillMode.Fit; + + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + Texture = textures.Get("header-logo"); + } } } } diff --git a/osu.Game.Tournament/Components/DrawableTournamentHeaderText.cs b/osu.Game.Tournament/Components/DrawableTournamentHeaderText.cs index 38500fa857..2539075c0f 100644 --- a/osu.Game.Tournament/Components/DrawableTournamentHeaderText.cs +++ b/osu.Game.Tournament/Components/DrawableTournamentHeaderText.cs @@ -2,17 +2,36 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; namespace osu.Game.Tournament.Components { - public class DrawableTournamentHeaderText : Sprite + public class DrawableTournamentHeaderText : CompositeDrawable { - [BackgroundDependencyLoader] - private void load(TextureStore textures) + public DrawableTournamentHeaderText() { - Texture = textures.Get("header-text"); + InternalChild = new TextSprite(); + + Height = 25; + RelativeSizeAxes = Axes.X; + } + + private class TextSprite : Sprite + { + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + RelativeSizeAxes = Axes.Both; + FillMode = FillMode.Fit; + + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + Texture = textures.Get("header-text"); + } } } } diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs b/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs index aa4bd4a701..751a763333 100644 --- a/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs +++ b/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs @@ -2,14 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Input.Events; using osu.Game.Tournament.Components; using osu.Game.Tournament.Models; using osuTK; -using osuTK.Input; namespace osu.Game.Tournament.Screens.Gameplay.Components { @@ -17,13 +14,39 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components { private TeamScoreDisplay teamDisplay1; private TeamScoreDisplay teamDisplay2; + private DrawableTournamentHeaderLogo logo; + + private bool showScores = true; public bool ShowScores { + get => showScores; set { - teamDisplay1.ShowScore = value; - teamDisplay2.ShowScore = value; + if (value == showScores) + return; + + showScores = value; + + if (IsLoaded) + updateDisplay(); + } + } + + private bool showLogo = true; + + public bool ShowLogo + { + get => showLogo; + set + { + if (value == showLogo) + return; + + showLogo = value; + + if (IsLoaded) + updateDisplay(); } } @@ -38,19 +61,25 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components { RelativeSizeAxes = Axes.Both, Direction = FillDirection.Vertical, + Padding = new MarginPadding(5), Spacing = new Vector2(5), Children = new Drawable[] { + logo = new DrawableTournamentHeaderLogo + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Alpha = showLogo ? 1 : 0 + }, new DrawableTournamentHeaderText { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Scale = new Vector2(1.2f) + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, }, new MatchRoundDisplay { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, Scale = new Vector2(0.4f) }, } @@ -66,76 +95,16 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components Origin = Anchor.TopRight, }, }; - } - } - public class TeamScoreDisplay : CompositeDrawable - { - private readonly TeamColour teamColour; - - private readonly Bindable currentMatch = new Bindable(); - private readonly Bindable currentTeam = new Bindable(); - private readonly Bindable currentTeamScore = new Bindable(); - - private TeamDisplay teamDisplay; - - public bool ShowScore { set => teamDisplay.ShowScore = value; } - - public TeamScoreDisplay(TeamColour teamColour) - { - this.teamColour = teamColour; - - RelativeSizeAxes = Axes.Y; - AutoSizeAxes = Axes.X; + updateDisplay(); } - [BackgroundDependencyLoader] - private void load(LadderInfo ladder) + private void updateDisplay() { - currentMatch.BindTo(ladder.CurrentMatch); - currentMatch.BindValueChanged(matchChanged, true); - } + teamDisplay1.ShowScore = showScores; + teamDisplay2.ShowScore = showScores; - private void matchChanged(ValueChangedEvent match) - { - currentTeamScore.UnbindBindings(); - currentTeam.UnbindBindings(); - - if (match.NewValue != null) - { - currentTeamScore.BindTo(teamColour == TeamColour.Red ? match.NewValue.Team1Score : match.NewValue.Team2Score); - currentTeam.BindTo(teamColour == TeamColour.Red ? match.NewValue.Team1 : match.NewValue.Team2); - } - - // team may change to same team, which means score is not in a good state. - // thus we handle this manually. - teamChanged(currentTeam.Value); - } - - protected override bool OnMouseDown(MouseDownEvent e) - { - switch (e.Button) - { - case MouseButton.Left: - if (currentTeamScore.Value < currentMatch.Value.PointsToWin) - currentTeamScore.Value++; - return true; - - case MouseButton.Right: - if (currentTeamScore.Value > 0) - currentTeamScore.Value--; - return true; - } - - return base.OnMouseDown(e); - } - - private void teamChanged(TournamentTeam team) - { - InternalChildren = new Drawable[] - { - teamDisplay = new TeamDisplay(team, teamColour, currentTeamScore, currentMatch.Value?.PointsToWin ?? 0), - }; + logo.Alpha = showLogo ? 1 : 0; } } } diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/TeamScoreDisplay.cs b/osu.Game.Tournament/Screens/Gameplay/Components/TeamScoreDisplay.cs new file mode 100644 index 0000000000..462015f004 --- /dev/null +++ b/osu.Game.Tournament/Screens/Gameplay/Components/TeamScoreDisplay.cs @@ -0,0 +1,83 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input.Events; +using osu.Game.Tournament.Models; +using osuTK.Input; + +namespace osu.Game.Tournament.Screens.Gameplay.Components +{ + public class TeamScoreDisplay : CompositeDrawable + { + private readonly TeamColour teamColour; + + private readonly Bindable currentMatch = new Bindable(); + private readonly Bindable currentTeam = new Bindable(); + private readonly Bindable currentTeamScore = new Bindable(); + + private TeamDisplay teamDisplay; + + public bool ShowScore { set => teamDisplay.ShowScore = value; } + + public TeamScoreDisplay(TeamColour teamColour) + { + this.teamColour = teamColour; + + RelativeSizeAxes = Axes.Y; + AutoSizeAxes = Axes.X; + } + + [BackgroundDependencyLoader] + private void load(LadderInfo ladder) + { + currentMatch.BindTo(ladder.CurrentMatch); + currentMatch.BindValueChanged(matchChanged, true); + } + + private void matchChanged(ValueChangedEvent match) + { + currentTeamScore.UnbindBindings(); + currentTeam.UnbindBindings(); + + if (match.NewValue != null) + { + currentTeamScore.BindTo(teamColour == TeamColour.Red ? match.NewValue.Team1Score : match.NewValue.Team2Score); + currentTeam.BindTo(teamColour == TeamColour.Red ? match.NewValue.Team1 : match.NewValue.Team2); + } + + // team may change to same team, which means score is not in a good state. + // thus we handle this manually. + teamChanged(currentTeam.Value); + } + + protected override bool OnMouseDown(MouseDownEvent e) + { + switch (e.Button) + { + case MouseButton.Left: + if (currentTeamScore.Value < currentMatch.Value.PointsToWin) + currentTeamScore.Value++; + return true; + + case MouseButton.Right: + if (currentTeamScore.Value > 0) + currentTeamScore.Value--; + return true; + } + + return base.OnMouseDown(e); + } + + private void teamChanged(TournamentTeam team) + { + InternalChildren = new Drawable[] + { + teamDisplay = new TeamDisplay(team, teamColour, currentTeamScore, currentMatch.Value?.PointsToWin ?? 0), + }; + } + } +} From 7b1ac03b18dae6767ee4c13be887b33913570f31 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Mar 2020 14:26:22 +0900 Subject: [PATCH 17/51] Hide logo on gameplay screen --- osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs index 4d770855cd..8920990d1b 100644 --- a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs +++ b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs @@ -47,7 +47,10 @@ namespace osu.Game.Tournament.Screens.Gameplay Loop = true, RelativeSizeAxes = Axes.Both, }, - header = new MatchHeader(), + header = new MatchHeader + { + ShowLogo = false + }, new Container { RelativeSizeAxes = Axes.X, From ec1c6f88ee3889cbe9657e5745a4753255b7cbf2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Mar 2020 14:26:39 +0900 Subject: [PATCH 18/51] Adjust metrics to align logo pieces correctly on gameplay / map pool --- .../Components/TestSceneMatchHeader.cs | 9 +++++++++ .../Components/DrawableTournamentHeaderLogo.cs | 2 +- .../Components/DrawableTournamentHeaderText.cs | 2 +- .../Screens/Gameplay/Components/MatchHeader.cs | 2 +- osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs | 3 ++- 5 files changed, 14 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tournament.Tests/Components/TestSceneMatchHeader.cs b/osu.Game.Tournament.Tests/Components/TestSceneMatchHeader.cs index b29e4964b6..9f885ed827 100644 --- a/osu.Game.Tournament.Tests/Components/TestSceneMatchHeader.cs +++ b/osu.Game.Tournament.Tests/Components/TestSceneMatchHeader.cs @@ -1,9 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; +using osu.Game.Tournament.Components; using osu.Game.Tournament.Screens.Gameplay.Components; using osuTK; @@ -11,6 +14,12 @@ namespace osu.Game.Tournament.Tests.Components { public class TestSceneMatchHeader : TournamentTestScene { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(DrawableTournamentHeaderText), + typeof(DrawableTournamentHeaderLogo), + }; + public TestSceneMatchHeader() { Child = new FillFlowContainer diff --git a/osu.Game.Tournament/Components/DrawableTournamentHeaderLogo.cs b/osu.Game.Tournament/Components/DrawableTournamentHeaderLogo.cs index a61cb59fed..3f5ab42fd7 100644 --- a/osu.Game.Tournament/Components/DrawableTournamentHeaderLogo.cs +++ b/osu.Game.Tournament/Components/DrawableTournamentHeaderLogo.cs @@ -15,7 +15,7 @@ namespace osu.Game.Tournament.Components { InternalChild = new LogoSprite(); - Height = 50; + Height = 82; RelativeSizeAxes = Axes.X; } diff --git a/osu.Game.Tournament/Components/DrawableTournamentHeaderText.cs b/osu.Game.Tournament/Components/DrawableTournamentHeaderText.cs index 2539075c0f..bda696ba00 100644 --- a/osu.Game.Tournament/Components/DrawableTournamentHeaderText.cs +++ b/osu.Game.Tournament/Components/DrawableTournamentHeaderText.cs @@ -15,7 +15,7 @@ namespace osu.Game.Tournament.Components { InternalChild = new TextSprite(); - Height = 25; + Height = 22; RelativeSizeAxes = Axes.X; } diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs b/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs index 751a763333..d790f4b754 100644 --- a/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs +++ b/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs @@ -61,7 +61,7 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components { RelativeSizeAxes = Axes.Both, Direction = FillDirection.Vertical, - Padding = new MarginPadding(5), + Padding = new MarginPadding(20), Spacing = new Vector2(5), Children = new Drawable[] { diff --git a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs index 4f3f7cfdbf..2b0bfe0b74 100644 --- a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs +++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs @@ -50,7 +50,7 @@ namespace osu.Game.Tournament.Screens.MapPool new MatchHeader(), mapFlows = new FillFlowContainer> { - Y = 100, + Y = 140, Spacing = new Vector2(10, 10), Padding = new MarginPadding(25), Direction = FillDirection.Vertical, @@ -235,6 +235,7 @@ namespace osu.Game.Tournament.Screens.MapPool { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, + Height = 42, }); } } From 28ac5af91c9bc751f66eb634c0fb3b98e76ba5ba Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Mar 2020 15:26:22 +0900 Subject: [PATCH 19/51] Fix beatmap carousel tests loading beatmap manager beatmaps in test browser --- osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs | 4 +++- osu.Game/Screens/Select/BeatmapCarousel.cs | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs index 71ae47dc66..80e03d82e2 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs @@ -497,7 +497,7 @@ namespace osu.Game.Tests.Visual.SongSelect } bool changed = false; - AddStep($"Load {beatmapSets.Count} Beatmaps", () => + AddStep($"Load {(beatmapSets.Count > 0 ? beatmapSets.Count.ToString() : "some")} beatmaps", () => { carousel.Filter(new FilterCriteria()); carousel.BeatmapSetsChanged = () => changed = true; @@ -697,6 +697,8 @@ namespace osu.Game.Tests.Visual.SongSelect public new List Items => base.Items; public bool PendingFilterTask => PendingFilter != null; + + protected override IEnumerable GetLoadableBeatmaps() => Enumerable.Empty(); } } } diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 71744d8b80..04c08cdbd2 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -153,9 +153,11 @@ namespace osu.Game.Screens.Select beatmaps.BeatmapHidden += beatmapHidden; beatmaps.BeatmapRestored += beatmapRestored; - loadBeatmapSets(beatmaps.GetAllUsableBeatmapSetsEnumerable()); + loadBeatmapSets(GetLoadableBeatmaps()); } + protected virtual IEnumerable GetLoadableBeatmaps() => beatmaps.GetAllUsableBeatmapSetsEnumerable(); + public void RemoveBeatmapSet(BeatmapSetInfo beatmapSet) => Schedule(() => { var existingSet = beatmapSets.FirstOrDefault(b => b.BeatmapSet.ID == beatmapSet.ID); From bc2a1cdb623547e9a05740685eba26f75bd00c76 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 12 Mar 2020 12:04:36 +0300 Subject: [PATCH 20/51] Apply suggestions --- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 441f9126f6..2394110165 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -344,11 +344,8 @@ namespace osu.Game.Rulesets.Catch.UI if (validCatch) updateState(fruit.Kiai ? CatcherAnimationState.Kiai : CatcherAnimationState.Idle); - else - { - if (!(fruit is Banana)) - updateState(CatcherAnimationState.Fail); - } + else if (!(fruit is Banana)) + updateState(CatcherAnimationState.Fail); return validCatch; } @@ -362,7 +359,7 @@ namespace osu.Game.Rulesets.Catch.UI updateCatcher(); } - public CatcherAnimationState CurrentState; + public CatcherAnimationState CurrentState { get; private set; } private double hyperDashModifier = 1; private int hyperDashDirection; From a69fabbd1ff4255c7cad25881b31daa5a48f365e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Mar 2020 18:56:48 +0900 Subject: [PATCH 21/51] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 6a8e66ee6a..f623a92ade 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index cc1ab654ab..ba6f0e2251 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -23,7 +23,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 04b688cfa3..54cd400d51 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -71,7 +71,7 @@ - + @@ -79,7 +79,7 @@ - + From bab197553e3968263cf0ee93776707d8da7c53c9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Mar 2020 15:34:58 +0900 Subject: [PATCH 22/51] Update carousel test logic to match new carousel selection behaviour --- .../Visual/SongSelect/TestSceneBeatmapCarousel.cs | 2 +- .../Visual/SongSelect/TestScenePlaySongSelect.cs | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs index 71ae47dc66..d80add3015 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs @@ -399,7 +399,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("filter to ruleset 0", () => carousel.Filter(new FilterCriteria { Ruleset = rulesets.AvailableRulesets.ElementAt(0) }, false)); AddStep("select filtered map skipping filtered", () => carousel.SelectBeatmap(testMixed.Beatmaps[1], false)); - AddAssert("unfiltered beatmap selected", () => carousel.SelectedBeatmap.Equals(testMixed.Beatmaps[0])); + AddAssert("unfiltered beatmap not selected", () => carousel.SelectedBeatmap == null); AddStep("remove mixed set", () => { diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 55c1d8451f..d16fd0bceb 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -591,16 +591,16 @@ namespace osu.Game.Tests.Visual.SongSelect } })); + BeatmapInfo filteredBeatmap = null; DrawableCarouselBeatmapSet.FilterableDifficultyIcon filteredIcon = null; + AddStep("Get filtered icon", () => { - var filteredBeatmap = songSelect.Carousel.SelectedBeatmapSet.Beatmaps.Find(b => b.BPM < maxBPM); + filteredBeatmap = songSelect.Carousel.SelectedBeatmapSet.Beatmaps.Find(b => b.BPM < maxBPM); int filteredBeatmapIndex = getBeatmapIndex(filteredBeatmap.BeatmapSet, filteredBeatmap); filteredIcon = set.ChildrenOfType().ElementAt(filteredBeatmapIndex); }); - int? previousID = null; - AddStep("Store current ID", () => previousID = songSelect.Carousel.SelectedBeatmap.ID); AddStep("Click on a filtered difficulty", () => { InputManager.MoveMouseTo(filteredIcon); @@ -608,7 +608,8 @@ namespace osu.Game.Tests.Visual.SongSelect InputManager.PressButton(MouseButton.Left); InputManager.ReleaseButton(MouseButton.Left); }); - AddAssert("Selected beatmap has not changed", () => songSelect.Carousel.SelectedBeatmap.ID == previousID); + + AddAssert("Selected beatmap correct", () => songSelect.Carousel.SelectedBeatmap == filteredBeatmap); } private int getBeatmapIndex(BeatmapSetInfo set, BeatmapInfo info) => set.Beatmaps.FindIndex(b => b == info); From 317bb5d0a436e3f6af94c141beeeec314b8f10cc Mon Sep 17 00:00:00 2001 From: Kelvin <2yangk23@gmail.com> Date: Thu, 12 Mar 2020 03:55:45 -0700 Subject: [PATCH 23/51] Fallback on invalid AnimationFramerate for legacy skins --- osu.Game/Skinning/LegacySkinExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/LegacySkinExtensions.cs b/osu.Game/Skinning/LegacySkinExtensions.cs index fa4de21eec..9cc58f4490 100644 --- a/osu.Game/Skinning/LegacySkinExtensions.cs +++ b/osu.Game/Skinning/LegacySkinExtensions.cs @@ -61,7 +61,7 @@ namespace osu.Game.Skinning { var iniRate = source.GetConfig(GlobalSkinConfiguration.AnimationFramerate); - if (iniRate != null) + if (iniRate != null && iniRate.Value > 0) return 1000f / iniRate.Value; return 1000f / textures.Length; From c8ea92257765d05bba2ae3d7a0f0966625d2aacf Mon Sep 17 00:00:00 2001 From: Kelvin <2yangk23@users.noreply.github.com> Date: Thu, 12 Mar 2020 04:18:57 -0700 Subject: [PATCH 24/51] Update osu.Game/Skinning/LegacySkinExtensions.cs Co-Authored-By: Dean Herbert --- osu.Game/Skinning/LegacySkinExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/LegacySkinExtensions.cs b/osu.Game/Skinning/LegacySkinExtensions.cs index 9cc58f4490..52328d43b2 100644 --- a/osu.Game/Skinning/LegacySkinExtensions.cs +++ b/osu.Game/Skinning/LegacySkinExtensions.cs @@ -61,7 +61,7 @@ namespace osu.Game.Skinning { var iniRate = source.GetConfig(GlobalSkinConfiguration.AnimationFramerate); - if (iniRate != null && iniRate.Value > 0) + if (iniRate?.Value > 0) return 1000f / iniRate.Value; return 1000f / textures.Length; From 3f8b454ff4783b896516506b4ef192cda78134f2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Mar 2020 10:01:28 +0900 Subject: [PATCH 25/51] Reword comment to match new filtering behaviour MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Bartłomiej Dach --- osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs index e0d59e3b18..6d760df065 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs @@ -32,7 +32,7 @@ namespace osu.Game.Screens.Select.Carousel if (Beatmap.BeatmapSet?.Equals(criteria.SelectedBeatmapSet) == true) { - // bypass filtering for selected beatmap + // only check ruleset equality or convertability for selected beatmap Filtered.Value = !match; return; } From 04f1da04db8c685305b8688dd29d7d4af0985c6b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Mar 2020 10:52:08 +0900 Subject: [PATCH 26/51] Remove incorrect xmldoc from SelectBeatmap function --- osu.Game/Screens/Select/BeatmapCarousel.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index ca20b02bce..34d659cc90 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -201,9 +201,6 @@ namespace osu.Game.Screens.Select /// /// Selects a given beatmap on the carousel. - /// - /// If bypassFilters is false, we will try to select another unfiltered beatmap in the same set. If the - /// entire set is filtered, no selection is made. /// /// The beatmap to select. /// Whether to select the beatmap even if it is filtered (i.e., not visible on carousel). From ba0dec891d4a576f6158bff9235bc37cbb224f14 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Mar 2020 10:58:36 +0900 Subject: [PATCH 27/51] Update test temporarily --- osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index d16fd0bceb..f1ff08b92c 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -609,7 +609,8 @@ namespace osu.Game.Tests.Visual.SongSelect InputManager.ReleaseButton(MouseButton.Left); }); - AddAssert("Selected beatmap correct", () => songSelect.Carousel.SelectedBeatmap == filteredBeatmap); + // todo: this logic is changed in follow up PR. + AddAssert("Selected beatmap not changed", () => songSelect.Carousel.SelectedBeatmap != filteredBeatmap); } private int getBeatmapIndex(BeatmapSetInfo set, BeatmapInfo info) => set.Beatmaps.FindIndex(b => b == info); From c8cdc5fda5fe6050d0512a56d686e8ec9f28f0d5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 13 Mar 2020 12:43:01 +0900 Subject: [PATCH 28/51] Expose half catcher width to movement skill --- .../Difficulty/CatchDifficultyCalculator.cs | 27 ++++++++++--------- .../Difficulty/Skills/Movement.cs | 7 +++++ 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 44e1a8e5cc..a3315d36e8 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -23,6 +23,8 @@ namespace osu.Game.Rulesets.Catch.Difficulty protected override int SectionLength => 750; + private float halfCatcherWidth; + public CatchDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap) : base(ruleset, beatmap) { @@ -48,14 +50,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) { - float halfCatchWidth; - - using (var catcher = new CatcherArea.Catcher(beatmap.BeatmapInfo.BaseDifficulty)) - { - halfCatchWidth = catcher.CatchWidth * 0.5f; - halfCatchWidth *= 0.8f; // We're only using 80% of the catcher's width to simulate imperfect gameplay. - } - CatchHitObject lastObject = null; // In 2B beatmaps, it is possible that a normal Fruit is placed in the middle of a JuiceStream. @@ -69,16 +63,25 @@ namespace osu.Game.Rulesets.Catch.Difficulty continue; if (lastObject != null) - yield return new CatchDifficultyHitObject(hitObject, lastObject, clockRate, halfCatchWidth); + yield return new CatchDifficultyHitObject(hitObject, lastObject, clockRate, halfCatcherWidth); lastObject = hitObject; } } - protected override Skill[] CreateSkills(IBeatmap beatmap) => new Skill[] + protected override Skill[] CreateSkills(IBeatmap beatmap) { - new Movement(), - }; + using (var catcher = new CatcherArea.Catcher(beatmap.BeatmapInfo.BaseDifficulty)) + { + halfCatcherWidth = catcher.CatchWidth * 0.5f; + halfCatcherWidth *= 0.8f; // We're only using 80% of the catcher's width to simulate imperfect gameplay. + } + + return new Skill[] + { + new Movement(halfCatcherWidth), + }; + } protected override Mod[] DifficultyAdjustmentMods => new Mod[] { diff --git a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs index 7cd569035b..fd164907e0 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs @@ -20,9 +20,16 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills protected override double DecayWeight => 0.94; + protected readonly float HalfCatcherWidth; + private float? lastPlayerPosition; private float lastDistanceMoved; + public Movement(float halfCatcherWidth) + { + HalfCatcherWidth = halfCatcherWidth; + } + protected override double StrainValueOf(DifficultyHitObject current) { var catchCurrent = (CatchDifficultyHitObject)current; From 1733519c3a3f7e88cb6c96ecbe2041f4bf0dbb0e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Mar 2020 12:59:30 +0900 Subject: [PATCH 29/51] Split out CatcherArea nested classes and reorder methods --- .../TestSceneCatcher.cs | 2 +- .../TestSceneDrawableHitObjects.cs | 2 +- .../TestSceneHyperDash.cs | 2 +- .../Beatmaps/CatchBeatmapProcessor.cs | 2 +- .../Difficulty/CatchDifficultyCalculator.cs | 2 +- osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs | 2 +- .../Replays/CatchAutoGenerator.cs | 2 +- osu.Game.Rulesets.Catch/UI/Catcher.cs | 460 ++++++++++++++ osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 581 +----------------- osu.Game.Rulesets.Catch/UI/HitExplosion.cs | 122 ++++ 10 files changed, 605 insertions(+), 572 deletions(-) create mode 100644 osu.Game.Rulesets.Catch/UI/Catcher.cs create mode 100644 osu.Game.Rulesets.Catch/UI/HitExplosion.cs diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs index fbbe00bb6c..fe0d512166 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Catch.Tests [BackgroundDependencyLoader] private void load() { - SetContents(() => new CatcherArea.Catcher + SetContents(() => new Catcher { RelativePositionAxes = Axes.None, Anchor = Anchor.Centre, diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs index 304c7e3854..df5494aab0 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Catch.Tests { public override IReadOnlyList RequiredTypes => new[] { - typeof(CatcherArea.Catcher), + typeof(Catcher), typeof(DrawableCatchRuleset), typeof(DrawableFruit), typeof(DrawableJuiceStream), diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs index 6f0d8f0a3a..49ff9df4d7 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs @@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Catch.Tests } } - private CatcherArea.Catcher getCatcher() => Player.ChildrenOfType().First().MovableCatcher; + private Catcher getCatcher() => Player.ChildrenOfType().First().MovableCatcher; protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) { diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index 986dc9dbb9..7c81bcdf0c 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -227,7 +227,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps int thisDirection = nextObject.X > currentObject.X ? 1 : -1; double timeToNext = nextObject.StartTime - currentObject.StartTime - 1000f / 60f / 4; // 1/4th of a frame of grace time, taken from osu-stable double distanceToNext = Math.Abs(nextObject.X - currentObject.X) - (lastDirection == thisDirection ? lastExcess : halfCatcherWidth); - float distanceToHyper = (float)(timeToNext * CatcherArea.Catcher.BASE_SPEED - distanceToNext); + float distanceToHyper = (float)(timeToNext * Catcher.BASE_SPEED - distanceToNext); if (distanceToHyper < 0) { diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 44e1a8e5cc..8b7080b5bf 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty { float halfCatchWidth; - using (var catcher = new CatcherArea.Catcher(beatmap.BeatmapInfo.BaseDifficulty)) + using (var catcher = new Catcher(beatmap.BeatmapInfo.BaseDifficulty)) { halfCatchWidth = catcher.CatchWidth * 0.5f; halfCatchWidth *= 0.8f; // We're only using 80% of the catcher's width to simulate imperfect gameplay. diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs b/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs index 4c72b9fd3e..1ef235f764 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Catch.Mods private class MouseInputHelper : Drawable, IKeyBindingHandler, IRequireHighFrequencyMousePosition { - private readonly CatcherArea.Catcher catcher; + private readonly Catcher catcher; public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; diff --git a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs index 4649dcae90..b90b5812a6 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Catch.Replays public override Replay Generate() { // todo: add support for HT DT - const double dash_speed = CatcherArea.Catcher.BASE_SPEED; + const double dash_speed = Catcher.BASE_SPEED; const double movement_speed = dash_speed / 2; float lastPosition = 0.5f; double lastTime = 0; diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs new file mode 100644 index 0000000000..2c8b080ee3 --- /dev/null +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -0,0 +1,460 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Animations; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input.Bindings; +using osu.Framework.Utils; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.Objects.Drawables; +using osu.Game.Rulesets.Objects.Drawables; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Catch.UI +{ + public class Catcher : Container, IKeyBindingHandler + { + /// + /// Whether we are hyper-dashing or not. + /// + public bool HyperDashing => hyperDashModifier != 1; + + /// + /// The relative space to cover in 1 millisecond. based on 1 game pixel per millisecond as in osu-stable. + /// + public const double BASE_SPEED = 1.0 / 512; + + public Container ExplodingFruitTarget; + + public Container AdditiveTarget; + + public CatcherAnimationState CurrentState { get; private set; } + + /// + /// Width of the area that can be used to attempt catches during gameplay. + /// + internal float CatchWidth => CatcherArea.CATCHER_SIZE * Math.Abs(Scale.X); + + protected bool Dashing + { + get => dashing; + set + { + if (value == dashing) return; + + dashing = value; + + Trail |= dashing; + } + } + + /// + /// Activate or deactive the trail. Will be automatically deactivated when conditions to keep the trail displayed are no longer met. + /// + protected bool Trail + { + get => trail; + set + { + if (value == trail) return; + + trail = value; + + if (Trail) + beginTrail(); + } + } + + private Container caughtFruit; + + private CatcherSprite catcherIdle; + private CatcherSprite catcherKiai; + private CatcherSprite catcherFail; + + private int currentDirection; + + private bool dashing; + + private bool trail; + + private double hyperDashModifier = 1; + private int hyperDashDirection; + private float hyperDashTargetPosition; + + public Catcher(BeatmapDifficulty difficulty = null) + { + RelativePositionAxes = Axes.X; + X = 0.5f; + + Origin = Anchor.TopCentre; + + Size = new Vector2(CatcherArea.CATCHER_SIZE); + if (difficulty != null) + Scale = new Vector2(1.0f - 0.7f * (difficulty.CircleSize - 5) / 5); + } + + [BackgroundDependencyLoader] + private void load() + { + Children = new Drawable[] + { + caughtFruit = new Container + { + Anchor = Anchor.TopCentre, + Origin = Anchor.BottomCentre, + }, + catcherIdle = new CatcherSprite(CatcherAnimationState.Idle) + { + Anchor = Anchor.TopCentre, + Alpha = 0, + }, + catcherKiai = new CatcherSprite(CatcherAnimationState.Kiai) + { + Anchor = Anchor.TopCentre, + Alpha = 0, + }, + catcherFail = new CatcherSprite(CatcherAnimationState.Fail) + { + Anchor = Anchor.TopCentre, + Alpha = 0, + } + }; + + updateCatcher(); + } + + /// + /// Add a caught fruit to the catcher's stack. + /// + /// The fruit that was caught. + public void PlaceOnPlate(DrawableCatchHitObject fruit) + { + var ourRadius = fruit.DisplayRadius; + float theirRadius = 0; + + const float allowance = 6; + + while (caughtFruit.Any(f => + f.LifetimeEnd == double.MaxValue && + Vector2Extensions.Distance(f.Position, fruit.Position) < (ourRadius + (theirRadius = f.DrawSize.X / 2 * f.Scale.X)) / (allowance / 2))) + { + var diff = (ourRadius + theirRadius) / allowance; + fruit.X += (RNG.NextSingle() - 0.5f) * 2 * diff; + fruit.Y -= RNG.NextSingle() * diff; + } + + fruit.X = Math.Clamp(fruit.X, -CatcherArea.CATCHER_SIZE / 2, CatcherArea.CATCHER_SIZE / 2); + + caughtFruit.Add(fruit); + + Add(new HitExplosion(fruit) + { + X = fruit.X, + Scale = new Vector2(fruit.HitObject.Scale) + }); + } + + /// + /// Let the catcher attempt to catch a fruit. + /// + /// The fruit to catch. + /// Whether the catch is possible. + public bool AttemptCatch(CatchHitObject fruit) + { + var halfCatchWidth = CatchWidth * 0.5f; + + // this stuff wil disappear once we move fruit to non-relative coordinate space in the future. + var catchObjectPosition = fruit.X * CatchPlayfield.BASE_WIDTH; + var catcherPosition = Position.X * CatchPlayfield.BASE_WIDTH; + + var validCatch = + catchObjectPosition >= catcherPosition - halfCatchWidth && + catchObjectPosition <= catcherPosition + halfCatchWidth; + + // only update hyperdash state if we are catching a fruit. + // exceptions are Droplets and JuiceStreams. + if (!(fruit is Fruit)) return validCatch; + + if (validCatch && fruit.HyperDash) + { + var target = fruit.HyperDashTarget; + var timeDifference = target.StartTime - fruit.StartTime; + double positionDifference = target.X * CatchPlayfield.BASE_WIDTH - catcherPosition; + var velocity = positionDifference / Math.Max(1.0, timeDifference - 1000.0 / 60.0); + + SetHyperDashState(Math.Abs(velocity), target.X); + } + else + SetHyperDashState(); + + if (validCatch) + updateState(fruit.Kiai ? CatcherAnimationState.Kiai : CatcherAnimationState.Idle); + else if (!(fruit is Banana)) + updateState(CatcherAnimationState.Fail); + + return validCatch; + } + + /// + /// Set hyper-dash state. + /// + /// The speed multiplier. If this is less or equals to 1, this catcher will be non-hyper-dashing state. + /// When this catcher crosses this position, this catcher ends hyper-dashing. + public void SetHyperDashState(double modifier = 1, float targetPosition = -1) + { + const float hyper_dash_transition_length = 180; + + var wasHyperDashing = HyperDashing; + + if (modifier <= 1 || X == targetPosition) + { + hyperDashModifier = 1; + hyperDashDirection = 0; + + if (wasHyperDashing) + { + this.FadeColour(Color4.White, hyper_dash_transition_length, Easing.OutQuint); + this.FadeTo(1, hyper_dash_transition_length, Easing.OutQuint); + Trail &= Dashing; + } + } + else + { + hyperDashModifier = modifier; + hyperDashDirection = Math.Sign(targetPosition - X); + hyperDashTargetPosition = targetPosition; + + if (!wasHyperDashing) + { + this.FadeColour(Color4.OrangeRed, hyper_dash_transition_length, Easing.OutQuint); + this.FadeTo(0.2f, hyper_dash_transition_length, Easing.OutQuint); + Trail = true; + + var hyperDashEndGlow = createAdditiveSprite(true); + + hyperDashEndGlow.MoveToOffset(new Vector2(0, -20), 1200, Easing.In); + hyperDashEndGlow.ScaleTo(hyperDashEndGlow.Scale * 0.9f).ScaleTo(hyperDashEndGlow.Scale * 1.2f, 1200, Easing.In); + hyperDashEndGlow.FadeOut(1200); + hyperDashEndGlow.Expire(true); + } + } + } + + public bool OnPressed(CatchAction action) + { + switch (action) + { + case CatchAction.MoveLeft: + currentDirection--; + return true; + + case CatchAction.MoveRight: + currentDirection++; + return true; + + case CatchAction.Dash: + Dashing = true; + return true; + } + + return false; + } + + public void OnReleased(CatchAction action) + { + switch (action) + { + case CatchAction.MoveLeft: + currentDirection++; + break; + + case CatchAction.MoveRight: + currentDirection--; + break; + + case CatchAction.Dash: + Dashing = false; + break; + } + } + + public void UpdatePosition(float position) + { + position = Math.Clamp(position, 0, 1); + + if (position == X) + return; + + Scale = new Vector2(Math.Abs(Scale.X) * (position > X ? 1 : -1), Scale.Y); + X = position; + } + + /// + /// Drop any fruit off the plate. + /// + public void Drop() + { + foreach (var f in caughtFruit.ToArray()) + Drop(f); + } + + /// + /// Explode any fruit off the plate. + /// + public void Explode() + { + foreach (var f in caughtFruit.ToArray()) + Explode(f); + } + + public void Drop(DrawableHitObject fruit) + { + removeFromPlateWithTransform(fruit, f => + { + f.MoveToY(f.Y + 75, 750, Easing.InSine); + f.FadeOut(750); + }); + } + + public void Explode(DrawableHitObject fruit) + { + var originalX = fruit.X * Scale.X; + + removeFromPlateWithTransform(fruit, f => + { + f.MoveToY(f.Y - 50, 250, Easing.OutSine).Then().MoveToY(f.Y + 50, 500, Easing.InSine); + f.MoveToX(f.X + originalX * 6, 1000); + f.FadeOut(750); + }); + } + + protected override void Update() + { + base.Update(); + + if (currentDirection == 0) return; + + var direction = Math.Sign(currentDirection); + + var dashModifier = Dashing ? 1 : 0.5; + var speed = BASE_SPEED * dashModifier * hyperDashModifier; + + UpdatePosition((float)(X + direction * Clock.ElapsedFrameTime * speed)); + + // Correct overshooting. + if (hyperDashDirection > 0 && hyperDashTargetPosition < X || + hyperDashDirection < 0 && hyperDashTargetPosition > X) + { + X = hyperDashTargetPosition; + SetHyperDashState(); + } + } + + private void updateCatcher() + { + catcherIdle.Hide(); + catcherKiai.Hide(); + catcherFail.Hide(); + + CatcherSprite current; + + switch (CurrentState) + { + default: + current = catcherIdle; + break; + + case CatcherAnimationState.Fail: + current = catcherFail; + break; + + case CatcherAnimationState.Kiai: + current = catcherKiai; + break; + } + + current.Show(); + (current.Drawable as IAnimation)?.GotoFrame(0); + } + + private void beginTrail() + { + Trail &= dashing || HyperDashing; + Trail &= AdditiveTarget != null; + + if (!Trail) return; + + var additive = createAdditiveSprite(HyperDashing); + + additive.FadeTo(0.4f).FadeOut(800, Easing.OutQuint); + additive.Expire(true); + + Scheduler.AddDelayed(beginTrail, HyperDashing ? 25 : 50); + } + + private Drawable createAdditiveSprite(bool hyperDash) + { + var additive = createCatcherSprite(); + + additive.Anchor = Anchor; + additive.Scale = Scale; + additive.Colour = hyperDash ? Color4.Red : Color4.White; + additive.Blending = BlendingParameters.Additive; + additive.RelativePositionAxes = RelativePositionAxes; + additive.Position = Position; + + AdditiveTarget.Add(additive); + + return additive; + } + + private Drawable createCatcherSprite() + { + return new CatcherSprite(CurrentState); + } + + private void updateState(CatcherAnimationState state) + { + if (CurrentState == state) + return; + + CurrentState = state; + updateCatcher(); + } + + private void removeFromPlateWithTransform(DrawableHitObject fruit, Action action) + { + if (ExplodingFruitTarget != null) + { + fruit.Anchor = Anchor.TopLeft; + fruit.Position = caughtFruit.ToSpaceOfOtherDrawable(fruit.DrawPosition, ExplodingFruitTarget); + + if (!caughtFruit.Remove(fruit)) + // we may have already been removed by a previous operation (due to the weird OnLoadComplete scheduling). + // this avoids a crash on potentially attempting to Add a fruit to ExplodingFruitTarget twice. + return; + + ExplodingFruitTarget.Add(fruit); + } + + var actionTime = Clock.CurrentTime; + + fruit.ApplyCustomUpdateState += onFruitOnApplyCustomUpdateState; + onFruitOnApplyCustomUpdateState(fruit, fruit.State.Value); + + void onFruitOnApplyCustomUpdateState(DrawableHitObject o, ArmedState state) + { + using (fruit.BeginAbsoluteSequence(actionTime)) + action(fruit); + + fruit.Expire(); + } + } + } +} diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 2394110165..e0d9ff759d 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -2,15 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Animations; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Effects; -using osu.Framework.Input.Bindings; -using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.Judgements; using osu.Game.Rulesets.Catch.Objects; @@ -20,7 +13,6 @@ using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI; using osuTK; -using osuTK.Graphics; namespace osu.Game.Rulesets.Catch.UI { @@ -28,8 +20,6 @@ namespace osu.Game.Rulesets.Catch.UI { public const float CATCHER_SIZE = 106.75f; - protected internal readonly Catcher MovableCatcher; - public Func> CreateDrawableRepresentation; public Container ExplodingFruitTarget @@ -37,6 +27,8 @@ namespace osu.Game.Rulesets.Catch.UI set => MovableCatcher.ExplodingFruitTarget = value; } + private DrawableCatchHitObject lastPlateableFruit; + public CatcherArea(BeatmapDifficulty difficulty = null) { RelativeSizeAxes = Axes.X; @@ -47,7 +39,10 @@ namespace osu.Game.Rulesets.Catch.UI }; } - private DrawableCatchHitObject lastPlateableFruit; + public static float GetCatcherSize(BeatmapDifficulty difficulty) + { + return CATCHER_SIZE / CatchPlayfield.BASE_WIDTH * (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5); + } public void OnResult(DrawableCatchHitObject fruit, JudgementResult result) { @@ -100,6 +95,15 @@ namespace osu.Game.Rulesets.Catch.UI } } + public void OnReleased(CatchAction action) + { + } + + public bool AttemptCatch(CatchHitObject obj) + { + return MovableCatcher.AttemptCatch(obj); + } + protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); @@ -110,559 +114,6 @@ namespace osu.Game.Rulesets.Catch.UI MovableCatcher.X = state.CatcherX.Value; } - public void OnReleased(CatchAction action) - { - } - - public bool AttemptCatch(CatchHitObject obj) => MovableCatcher.AttemptCatch(obj); - - public static float GetCatcherSize(BeatmapDifficulty difficulty) - { - return CATCHER_SIZE / CatchPlayfield.BASE_WIDTH * (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5); - } - - public class Catcher : Container, IKeyBindingHandler - { - /// - /// Width of the area that can be used to attempt catches during gameplay. - /// - internal float CatchWidth => CATCHER_SIZE * Math.Abs(Scale.X); - - private Container caughtFruit; - - public Container ExplodingFruitTarget; - - public Container AdditiveTarget; - - public Catcher(BeatmapDifficulty difficulty = null) - { - RelativePositionAxes = Axes.X; - X = 0.5f; - - Origin = Anchor.TopCentre; - - Size = new Vector2(CATCHER_SIZE); - if (difficulty != null) - Scale = new Vector2(1.0f - 0.7f * (difficulty.CircleSize - 5) / 5); - } - - [BackgroundDependencyLoader] - private void load() - { - Children = new Drawable[] - { - caughtFruit = new Container - { - Anchor = Anchor.TopCentre, - Origin = Anchor.BottomCentre, - }, - catcherIdle = new CatcherSprite(CatcherAnimationState.Idle) - { - Anchor = Anchor.TopCentre, - Alpha = 0, - }, - catcherKiai = new CatcherSprite(CatcherAnimationState.Kiai) - { - Anchor = Anchor.TopCentre, - Alpha = 0, - }, - catcherFail = new CatcherSprite(CatcherAnimationState.Fail) - { - Anchor = Anchor.TopCentre, - Alpha = 0, - } - }; - - updateCatcher(); - } - - private CatcherSprite catcherIdle; - private CatcherSprite catcherKiai; - private CatcherSprite catcherFail; - - private void updateCatcher() - { - catcherIdle.Hide(); - catcherKiai.Hide(); - catcherFail.Hide(); - - CatcherSprite current; - - switch (CurrentState) - { - default: - current = catcherIdle; - break; - - case CatcherAnimationState.Fail: - current = catcherFail; - break; - - case CatcherAnimationState.Kiai: - current = catcherKiai; - break; - } - - current.Show(); - (current.Drawable as IAnimation)?.GotoFrame(0); - } - - private int currentDirection; - - private bool dashing; - - protected bool Dashing - { - get => dashing; - set - { - if (value == dashing) return; - - dashing = value; - - Trail |= dashing; - } - } - - private bool trail; - - /// - /// Activate or deactive the trail. Will be automatically deactivated when conditions to keep the trail displayed are no longer met. - /// - protected bool Trail - { - get => trail; - set - { - if (value == trail) return; - - trail = value; - - if (Trail) - beginTrail(); - } - } - - private void beginTrail() - { - Trail &= dashing || HyperDashing; - Trail &= AdditiveTarget != null; - - if (!Trail) return; - - var additive = createAdditiveSprite(HyperDashing); - - additive.FadeTo(0.4f).FadeOut(800, Easing.OutQuint); - additive.Expire(true); - - Scheduler.AddDelayed(beginTrail, HyperDashing ? 25 : 50); - } - - private Drawable createAdditiveSprite(bool hyperDash) - { - var additive = createCatcherSprite(); - - additive.Anchor = Anchor; - additive.Scale = Scale; - additive.Colour = hyperDash ? Color4.Red : Color4.White; - additive.Blending = BlendingParameters.Additive; - additive.RelativePositionAxes = RelativePositionAxes; - additive.Position = Position; - - AdditiveTarget.Add(additive); - - return additive; - } - - private Drawable createCatcherSprite() => new CatcherSprite(CurrentState); - - /// - /// Add a caught fruit to the catcher's stack. - /// - /// The fruit that was caught. - public void PlaceOnPlate(DrawableCatchHitObject fruit) - { - float ourRadius = fruit.DisplayRadius; - float theirRadius = 0; - - const float allowance = 6; - - while (caughtFruit.Any(f => - f.LifetimeEnd == double.MaxValue && - Vector2Extensions.Distance(f.Position, fruit.Position) < (ourRadius + (theirRadius = f.DrawSize.X / 2 * f.Scale.X)) / (allowance / 2))) - { - float diff = (ourRadius + theirRadius) / allowance; - fruit.X += (RNG.NextSingle() - 0.5f) * 2 * diff; - fruit.Y -= RNG.NextSingle() * diff; - } - - fruit.X = Math.Clamp(fruit.X, -CATCHER_SIZE / 2, CATCHER_SIZE / 2); - - caughtFruit.Add(fruit); - - Add(new HitExplosion(fruit) - { - X = fruit.X, - Scale = new Vector2(fruit.HitObject.Scale) - }); - } - - /// - /// Let the catcher attempt to catch a fruit. - /// - /// The fruit to catch. - /// Whether the catch is possible. - public bool AttemptCatch(CatchHitObject fruit) - { - float halfCatchWidth = CatchWidth * 0.5f; - - // this stuff wil disappear once we move fruit to non-relative coordinate space in the future. - var catchObjectPosition = fruit.X * CatchPlayfield.BASE_WIDTH; - var catcherPosition = Position.X * CatchPlayfield.BASE_WIDTH; - - var validCatch = - catchObjectPosition >= catcherPosition - halfCatchWidth && - catchObjectPosition <= catcherPosition + halfCatchWidth; - - // only update hyperdash state if we are catching a fruit. - // exceptions are Droplets and JuiceStreams. - if (!(fruit is Fruit)) return validCatch; - - if (validCatch && fruit.HyperDash) - { - var target = fruit.HyperDashTarget; - double timeDifference = target.StartTime - fruit.StartTime; - double positionDifference = target.X * CatchPlayfield.BASE_WIDTH - catcherPosition; - double velocity = positionDifference / Math.Max(1.0, timeDifference - 1000.0 / 60.0); - - SetHyperDashState(Math.Abs(velocity), target.X); - } - else - { - SetHyperDashState(); - } - - if (validCatch) - updateState(fruit.Kiai ? CatcherAnimationState.Kiai : CatcherAnimationState.Idle); - else if (!(fruit is Banana)) - updateState(CatcherAnimationState.Fail); - - return validCatch; - } - - private void updateState(CatcherAnimationState state) - { - if (CurrentState == state) - return; - - CurrentState = state; - updateCatcher(); - } - - public CatcherAnimationState CurrentState { get; private set; } - - private double hyperDashModifier = 1; - private int hyperDashDirection; - private float hyperDashTargetPosition; - - /// - /// Whether we are hyper-dashing or not. - /// - public bool HyperDashing => hyperDashModifier != 1; - - /// - /// Set hyper-dash state. - /// - /// The speed multiplier. If this is less or equals to 1, this catcher will be non-hyper-dashing state. - /// When this catcher crosses this position, this catcher ends hyper-dashing. - public void SetHyperDashState(double modifier = 1, float targetPosition = -1) - { - const float hyper_dash_transition_length = 180; - - bool wasHyperDashing = HyperDashing; - - if (modifier <= 1 || X == targetPosition) - { - hyperDashModifier = 1; - hyperDashDirection = 0; - - if (wasHyperDashing) - { - this.FadeColour(Color4.White, hyper_dash_transition_length, Easing.OutQuint); - this.FadeTo(1, hyper_dash_transition_length, Easing.OutQuint); - Trail &= Dashing; - } - } - else - { - hyperDashModifier = modifier; - hyperDashDirection = Math.Sign(targetPosition - X); - hyperDashTargetPosition = targetPosition; - - if (!wasHyperDashing) - { - this.FadeColour(Color4.OrangeRed, hyper_dash_transition_length, Easing.OutQuint); - this.FadeTo(0.2f, hyper_dash_transition_length, Easing.OutQuint); - Trail = true; - - var hyperDashEndGlow = createAdditiveSprite(true); - - hyperDashEndGlow.MoveToOffset(new Vector2(0, -20), 1200, Easing.In); - hyperDashEndGlow.ScaleTo(hyperDashEndGlow.Scale * 0.9f).ScaleTo(hyperDashEndGlow.Scale * 1.2f, 1200, Easing.In); - hyperDashEndGlow.FadeOut(1200); - hyperDashEndGlow.Expire(true); - } - } - } - - public bool OnPressed(CatchAction action) - { - switch (action) - { - case CatchAction.MoveLeft: - currentDirection--; - return true; - - case CatchAction.MoveRight: - currentDirection++; - return true; - - case CatchAction.Dash: - Dashing = true; - return true; - } - - return false; - } - - public void OnReleased(CatchAction action) - { - switch (action) - { - case CatchAction.MoveLeft: - currentDirection++; - break; - - case CatchAction.MoveRight: - currentDirection--; - break; - - case CatchAction.Dash: - Dashing = false; - break; - } - } - - /// - /// The relative space to cover in 1 millisecond. based on 1 game pixel per millisecond as in osu-stable. - /// - public const double BASE_SPEED = 1.0 / 512; - - protected override void Update() - { - base.Update(); - - if (currentDirection == 0) return; - - var direction = Math.Sign(currentDirection); - - double dashModifier = Dashing ? 1 : 0.5; - double speed = BASE_SPEED * dashModifier * hyperDashModifier; - - UpdatePosition((float)(X + direction * Clock.ElapsedFrameTime * speed)); - - // Correct overshooting. - if ((hyperDashDirection > 0 && hyperDashTargetPosition < X) || - (hyperDashDirection < 0 && hyperDashTargetPosition > X)) - { - X = hyperDashTargetPosition; - SetHyperDashState(); - } - } - - public void UpdatePosition(float position) - { - position = Math.Clamp(position, 0, 1); - - if (position == X) - return; - - Scale = new Vector2(Math.Abs(Scale.X) * (position > X ? 1 : -1), Scale.Y); - X = position; - } - - /// - /// Drop any fruit off the plate. - /// - public void Drop() - { - foreach (var f in caughtFruit.ToArray()) - Drop(f); - } - - /// - /// Explode any fruit off the plate. - /// - public void Explode() - { - foreach (var f in caughtFruit.ToArray()) - Explode(f); - } - - public void Drop(DrawableHitObject fruit) => removeFromPlateWithTransform(fruit, f => - { - f.MoveToY(f.Y + 75, 750, Easing.InSine); - f.FadeOut(750); - }); - - public void Explode(DrawableHitObject fruit) - { - var originalX = fruit.X * Scale.X; - - removeFromPlateWithTransform(fruit, f => - { - f.MoveToY(f.Y - 50, 250, Easing.OutSine).Then().MoveToY(f.Y + 50, 500, Easing.InSine); - f.MoveToX(f.X + originalX * 6, 1000); - f.FadeOut(750); - }); - } - - private void removeFromPlateWithTransform(DrawableHitObject fruit, Action action) - { - if (ExplodingFruitTarget != null) - { - fruit.Anchor = Anchor.TopLeft; - fruit.Position = caughtFruit.ToSpaceOfOtherDrawable(fruit.DrawPosition, ExplodingFruitTarget); - - if (!caughtFruit.Remove(fruit)) - // we may have already been removed by a previous operation (due to the weird OnLoadComplete scheduling). - // this avoids a crash on potentially attempting to Add a fruit to ExplodingFruitTarget twice. - return; - - ExplodingFruitTarget.Add(fruit); - } - - double actionTime = Clock.CurrentTime; - - fruit.ApplyCustomUpdateState += onFruitOnApplyCustomUpdateState; - onFruitOnApplyCustomUpdateState(fruit, fruit.State.Value); - - void onFruitOnApplyCustomUpdateState(DrawableHitObject o, ArmedState state) - { - using (fruit.BeginAbsoluteSequence(actionTime)) - action(fruit); - - fruit.Expire(); - } - } - } - } - - public class HitExplosion : CompositeDrawable - { - private readonly CircularContainer largeFaint; - - public HitExplosion(DrawableCatchHitObject fruit) - { - Size = new Vector2(20); - Anchor = Anchor.TopCentre; - Origin = Anchor.BottomCentre; - - Color4 objectColour = fruit.AccentColour.Value; - - // scale roughly in-line with visual appearance of notes - - const float angle_variangle = 15; // should be less than 45 - - const float roundness = 100; - - const float initial_height = 10; - - var colour = Interpolation.ValueAt(0.4f, objectColour, Color4.White, 0, 1); - - InternalChildren = new Drawable[] - { - largeFaint = new CircularContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Masking = true, - // we want our size to be very small so the glow dominates it. - Size = new Vector2(0.8f), - Blending = BlendingParameters.Additive, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Glow, - Colour = Interpolation.ValueAt(0.1f, objectColour, Color4.White, 0, 1).Opacity(0.3f), - Roundness = 160, - Radius = 200, - }, - }, - new CircularContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Masking = true, - Blending = BlendingParameters.Additive, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Glow, - Colour = Interpolation.ValueAt(0.6f, objectColour, Color4.White, 0, 1), - Roundness = 20, - Radius = 50, - }, - }, - new CircularContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Masking = true, - Size = new Vector2(0.01f, initial_height), - Blending = BlendingParameters.Additive, - Rotation = RNG.NextSingle(-angle_variangle, angle_variangle), - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Glow, - Colour = colour, - Roundness = roundness, - Radius = 40, - }, - }, - new CircularContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Masking = true, - Size = new Vector2(0.01f, initial_height), - Blending = BlendingParameters.Additive, - Rotation = RNG.NextSingle(-angle_variangle, angle_variangle), - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Glow, - Colour = colour, - Roundness = roundness, - Radius = 40, - }, - } - }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - const double duration = 400; - - largeFaint - .ResizeTo(largeFaint.Size * new Vector2(5, 1), duration, Easing.OutQuint) - .FadeOut(duration * 2); - - this.FadeInFromZero(50).Then().FadeOut(duration, Easing.Out); - Expire(true); - } + protected internal readonly Catcher MovableCatcher; } } diff --git a/osu.Game.Rulesets.Catch/UI/HitExplosion.cs b/osu.Game.Rulesets.Catch/UI/HitExplosion.cs new file mode 100644 index 0000000000..04a86f83be --- /dev/null +++ b/osu.Game.Rulesets.Catch/UI/HitExplosion.cs @@ -0,0 +1,122 @@ +// 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.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; +using osu.Framework.Utils; +using osu.Game.Rulesets.Catch.Objects.Drawables; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Catch.UI +{ + public class HitExplosion : CompositeDrawable + { + private readonly CircularContainer largeFaint; + + public HitExplosion(DrawableCatchHitObject fruit) + { + Size = new Vector2(20); + Anchor = Anchor.TopCentre; + Origin = Anchor.BottomCentre; + + Color4 objectColour = fruit.AccentColour.Value; + + // scale roughly in-line with visual appearance of notes + + const float angle_variangle = 15; // should be less than 45 + + const float roundness = 100; + + const float initial_height = 10; + + var colour = Interpolation.ValueAt(0.4f, objectColour, Color4.White, 0, 1); + + InternalChildren = new Drawable[] + { + largeFaint = new CircularContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Masking = true, + // we want our size to be very small so the glow dominates it. + Size = new Vector2(0.8f), + Blending = BlendingParameters.Additive, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Colour = Interpolation.ValueAt(0.1f, objectColour, Color4.White, 0, 1).Opacity(0.3f), + Roundness = 160, + Radius = 200, + }, + }, + new CircularContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Masking = true, + Blending = BlendingParameters.Additive, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Colour = Interpolation.ValueAt(0.6f, objectColour, Color4.White, 0, 1), + Roundness = 20, + Radius = 50, + }, + }, + new CircularContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Masking = true, + Size = new Vector2(0.01f, initial_height), + Blending = BlendingParameters.Additive, + Rotation = RNG.NextSingle(-angle_variangle, angle_variangle), + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Colour = colour, + Roundness = roundness, + Radius = 40, + }, + }, + new CircularContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Masking = true, + Size = new Vector2(0.01f, initial_height), + Blending = BlendingParameters.Additive, + Rotation = RNG.NextSingle(-angle_variangle, angle_variangle), + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Colour = colour, + Roundness = roundness, + Radius = 40, + }, + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + const double duration = 400; + + largeFaint + .ResizeTo(largeFaint.Size * new Vector2(5, 1), duration, Easing.OutQuint) + .FadeOut(duration * 2); + + this.FadeInFromZero(50).Then().FadeOut(duration, Easing.Out); + Expire(true); + } + } +} From 4a774d02e0cf639c8a20b98eaea80f3cd26be71e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 8 Mar 2020 14:43:27 +0900 Subject: [PATCH 30/51] Remove exo font loading --- osu.Game/OsuGameBase.cs | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index b2277e2abf..93a845a6ae 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -138,30 +138,17 @@ namespace osu.Game dependencies.Cache(LocalConfig); AddFont(Resources, @"Fonts/osuFont"); - AddFont(Resources, @"Fonts/Exo2.0-Medium"); - AddFont(Resources, @"Fonts/Exo2.0-MediumItalic"); - - AddFont(Resources, @"Fonts/Noto-Basic"); - AddFont(Resources, @"Fonts/Noto-Hangul"); - AddFont(Resources, @"Fonts/Noto-CJK-Basic"); - AddFont(Resources, @"Fonts/Noto-CJK-Compatibility"); - - AddFont(Resources, @"Fonts/Exo2.0-Regular"); - AddFont(Resources, @"Fonts/Exo2.0-RegularItalic"); - AddFont(Resources, @"Fonts/Exo2.0-SemiBold"); - AddFont(Resources, @"Fonts/Exo2.0-SemiBoldItalic"); - AddFont(Resources, @"Fonts/Exo2.0-Bold"); - AddFont(Resources, @"Fonts/Exo2.0-BoldItalic"); - AddFont(Resources, @"Fonts/Exo2.0-Light"); - AddFont(Resources, @"Fonts/Exo2.0-LightItalic"); - AddFont(Resources, @"Fonts/Exo2.0-Black"); - AddFont(Resources, @"Fonts/Exo2.0-BlackItalic"); AddFont(Resources, @"Fonts/Torus-SemiBold"); AddFont(Resources, @"Fonts/Torus-Bold"); AddFont(Resources, @"Fonts/Torus-Regular"); AddFont(Resources, @"Fonts/Torus-Light"); + AddFont(Resources, @"Fonts/Noto-Basic"); + AddFont(Resources, @"Fonts/Noto-Hangul"); + AddFont(Resources, @"Fonts/Noto-CJK-Basic"); + AddFont(Resources, @"Fonts/Noto-CJK-Compatibility"); + AddFont(Resources, @"Fonts/Venera-Light"); AddFont(Resources, @"Fonts/Venera-Bold"); AddFont(Resources, @"Fonts/Venera-Black"); From ae112cf14f2f419e73ae9c4e75d9d542e72b1312 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Mar 2020 13:31:59 +0900 Subject: [PATCH 31/51] Reorder torus loading to provide regular as default --- osu.Game/OsuGameBase.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 93a845a6ae..3c7ab27651 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -139,10 +139,10 @@ namespace osu.Game AddFont(Resources, @"Fonts/osuFont"); - AddFont(Resources, @"Fonts/Torus-SemiBold"); - AddFont(Resources, @"Fonts/Torus-Bold"); AddFont(Resources, @"Fonts/Torus-Regular"); AddFont(Resources, @"Fonts/Torus-Light"); + AddFont(Resources, @"Fonts/Torus-SemiBold"); + AddFont(Resources, @"Fonts/Torus-Bold"); AddFont(Resources, @"Fonts/Noto-Basic"); AddFont(Resources, @"Fonts/Noto-Hangul"); From 288470c3138713756b4f24fa1ef1e8f4ab63c20c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Mar 2020 13:32:16 +0900 Subject: [PATCH 32/51] Remove exo specification completely --- osu.Game/Graphics/OsuFont.cs | 6 +----- osu.Game/Graphics/UserInterface/OsuTabControl.cs | 2 +- osu.Game/Graphics/UserInterface/PageTabControl.cs | 2 +- osu.Game/Overlays/AccountCreation/ScreenEntry.cs | 2 +- osu.Game/Overlays/News/NewsArticleCover.cs | 6 +++--- osu.Game/Screens/Menu/Disclaimer.cs | 6 +++--- osu.Game/Screens/Multi/Lounge/Components/ParticipantInfo.cs | 2 +- osu.Game/Screens/Multi/Ranking/Pages/RoomLeaderboardPage.cs | 2 +- 8 files changed, 12 insertions(+), 16 deletions(-) diff --git a/osu.Game/Graphics/OsuFont.cs b/osu.Game/Graphics/OsuFont.cs index 841936d2c5..1b5a3199a2 100644 --- a/osu.Game/Graphics/OsuFont.cs +++ b/osu.Game/Graphics/OsuFont.cs @@ -30,7 +30,7 @@ namespace osu.Game.Graphics /// Whether the font is italic. /// Whether all characters should be spaced the same distance apart. /// The . - public static FontUsage GetFont(Typeface typeface = Typeface.Exo, float size = DEFAULT_FONT_SIZE, FontWeight weight = FontWeight.Medium, bool italics = false, bool fixedWidth = false) + public static FontUsage GetFont(Typeface typeface = Typeface.Torus, float size = DEFAULT_FONT_SIZE, FontWeight weight = FontWeight.Medium, bool italics = false, bool fixedWidth = false) => new FontUsage(GetFamilyString(typeface), size, GetWeightString(typeface, weight), italics, fixedWidth); /// @@ -42,9 +42,6 @@ namespace osu.Game.Graphics { switch (typeface) { - case Typeface.Exo: - return "Exo2.0"; - case Typeface.Venera: return "Venera"; @@ -96,7 +93,6 @@ namespace osu.Game.Graphics public enum Typeface { - Exo, Venera, Torus } diff --git a/osu.Game/Graphics/UserInterface/OsuTabControl.cs b/osu.Game/Graphics/UserInterface/OsuTabControl.cs index 6c883d9893..ca9f1330f9 100644 --- a/osu.Game/Graphics/UserInterface/OsuTabControl.cs +++ b/osu.Game/Graphics/UserInterface/OsuTabControl.cs @@ -173,7 +173,7 @@ namespace osu.Game.Graphics.UserInterface new HoverClickSounds() }; - Active.BindValueChanged(active => Text.Font = Text.Font.With(Typeface.Exo, weight: active.NewValue ? FontWeight.Bold : FontWeight.Medium), true); + Active.BindValueChanged(active => Text.Font = Text.Font.With(Typeface.Torus, weight: active.NewValue ? FontWeight.Bold : FontWeight.Medium), true); } protected override void OnActivated() => fadeActive(); diff --git a/osu.Game/Graphics/UserInterface/PageTabControl.cs b/osu.Game/Graphics/UserInterface/PageTabControl.cs index ddcb626701..d05a08108a 100644 --- a/osu.Game/Graphics/UserInterface/PageTabControl.cs +++ b/osu.Game/Graphics/UserInterface/PageTabControl.cs @@ -78,7 +78,7 @@ namespace osu.Game.Graphics.UserInterface new HoverClickSounds() }; - Active.BindValueChanged(active => Text.Font = Text.Font.With(Typeface.Exo, weight: active.NewValue ? FontWeight.Bold : FontWeight.Medium), true); + Active.BindValueChanged(active => Text.Font = Text.Font.With(Typeface.Torus, weight: active.NewValue ? FontWeight.Bold : FontWeight.Medium), true); } protected virtual string CreateText() => (Value as Enum)?.GetDescription() ?? Value.ToString(); diff --git a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs index 2576900db8..a0b1b27ebf 100644 --- a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs +++ b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs @@ -132,7 +132,7 @@ namespace osu.Game.Overlays.AccountCreation usernameDescription.AddText("This will be your public presence. No profanity, no impersonation. Avoid exposing your own personal details, too!"); emailAddressDescription.AddText("Will be used for notifications, account verification and in the case you forget your password. No spam, ever."); - emailAddressDescription.AddText(" Make sure to get it right!", cp => cp.Font = cp.Font.With(Typeface.Exo, weight: FontWeight.Bold)); + emailAddressDescription.AddText(" Make sure to get it right!", cp => cp.Font = cp.Font.With(Typeface.Torus, weight: FontWeight.Bold)); passwordDescription.AddText("At least "); characterCheckText = passwordDescription.AddText("8 characters long"); diff --git a/osu.Game/Overlays/News/NewsArticleCover.cs b/osu.Game/Overlays/News/NewsArticleCover.cs index f61b30b381..e381b629e4 100644 --- a/osu.Game/Overlays/News/NewsArticleCover.cs +++ b/osu.Game/Overlays/News/NewsArticleCover.cs @@ -75,7 +75,7 @@ namespace osu.Game.Overlays.News Left = 25, Bottom = 50, }, - Font = OsuFont.GetFont(Typeface.Exo, 24, FontWeight.Bold), + Font = OsuFont.GetFont(Typeface.Torus, 24, FontWeight.Bold), Text = info.Title, }, new OsuSpriteText @@ -87,7 +87,7 @@ namespace osu.Game.Overlays.News Left = 25, Bottom = 30, }, - Font = OsuFont.GetFont(Typeface.Exo, 16, FontWeight.Bold), + Font = OsuFont.GetFont(Typeface.Torus, 16, FontWeight.Bold), Text = "by " + info.Author } }; @@ -148,7 +148,7 @@ namespace osu.Game.Overlays.News { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Font = OsuFont.GetFont(Typeface.Exo, 12, FontWeight.Black, false, false), + Font = OsuFont.GetFont(Typeface.Torus, 12, FontWeight.Black, false, false), Text = date.ToString("d MMM yyy").ToUpper(), Margin = new MarginPadding { diff --git a/osu.Game/Screens/Menu/Disclaimer.cs b/osu.Game/Screens/Menu/Disclaimer.cs index ee8200321b..35091028ae 100644 --- a/osu.Game/Screens/Menu/Disclaimer.cs +++ b/osu.Game/Screens/Menu/Disclaimer.cs @@ -90,14 +90,14 @@ namespace osu.Game.Screens.Menu } }; - textFlow.AddText("This project is an ongoing ", t => t.Font = t.Font.With(Typeface.Exo, 30, FontWeight.Light)); - textFlow.AddText("work in progress", t => t.Font = t.Font.With(Typeface.Exo, 30, FontWeight.SemiBold)); + textFlow.AddText("This project is an ongoing ", t => t.Font = t.Font.With(Typeface.Torus, 30, FontWeight.Light)); + textFlow.AddText("work in progress", t => t.Font = t.Font.With(Typeface.Torus, 30, FontWeight.SemiBold)); textFlow.NewParagraph(); static void format(SpriteText t) => t.Font = OsuFont.GetFont(size: 15, weight: FontWeight.SemiBold); - textFlow.AddParagraph(getRandomTip(), t => t.Font = t.Font.With(Typeface.Exo, 20, FontWeight.SemiBold)); + textFlow.AddParagraph(getRandomTip(), t => t.Font = t.Font.With(Typeface.Torus, 20, FontWeight.SemiBold)); textFlow.NewParagraph(); textFlow.NewParagraph(); diff --git a/osu.Game/Screens/Multi/Lounge/Components/ParticipantInfo.cs b/osu.Game/Screens/Multi/Lounge/Components/ParticipantInfo.cs index a55db096af..4152a9a3b2 100644 --- a/osu.Game/Screens/Multi/Lounge/Components/ParticipantInfo.cs +++ b/osu.Game/Screens/Multi/Lounge/Components/ParticipantInfo.cs @@ -77,7 +77,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components if (host.NewValue != null) { hostText.AddText("hosted by "); - hostText.AddUserLink(host.NewValue, s => s.Font = s.Font.With(Typeface.Exo, weight: FontWeight.Bold, italics: true)); + hostText.AddUserLink(host.NewValue, s => s.Font = s.Font.With(Typeface.Torus, weight: FontWeight.Bold, italics: true)); flagContainer.Child = new UpdateableFlag(host.NewValue.Country) { RelativeSizeAxes = Axes.Both }; } diff --git a/osu.Game/Screens/Multi/Ranking/Pages/RoomLeaderboardPage.cs b/osu.Game/Screens/Multi/Ranking/Pages/RoomLeaderboardPage.cs index f8fb192b5c..0d31805774 100644 --- a/osu.Game/Screens/Multi/Ranking/Pages/RoomLeaderboardPage.cs +++ b/osu.Game/Screens/Multi/Ranking/Pages/RoomLeaderboardPage.cs @@ -91,7 +91,7 @@ namespace osu.Game.Screens.Multi.Ranking.Pages rankText.AddText($"#{index + 1} ", s => { - s.Font = s.Font.With(Typeface.Exo, weight: FontWeight.Bold); + s.Font = s.Font.With(Typeface.Torus, weight: FontWeight.Bold); s.Colour = colours.YellowDark; }); From c45f9cafd4384b3903952dfe74f6ca8b59dda587 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Mar 2020 13:33:55 +0900 Subject: [PATCH 33/51] Add medium -> regular fallback for torus --- osu.Game/Graphics/OsuFont.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/OsuFont.cs b/osu.Game/Graphics/OsuFont.cs index 1b5a3199a2..076cc241ed 100644 --- a/osu.Game/Graphics/OsuFont.cs +++ b/osu.Game/Graphics/OsuFont.cs @@ -59,7 +59,13 @@ namespace osu.Game.Graphics /// The . /// The string representation of in the specified . public static string GetWeightString(Typeface typeface, FontWeight weight) - => GetWeightString(GetFamilyString(typeface), weight); + { + if (typeface == Typeface.Torus && weight == FontWeight.Medium) + // torus doesn't have a medium; fallback to regular. + weight = FontWeight.Regular; + + return GetWeightString(GetFamilyString(typeface), weight); + } /// /// Retrieves the string representation of a . From edd444ea73fdc0df668b2e2c11f8fa15da1ac438 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Mar 2020 14:36:46 +0900 Subject: [PATCH 34/51] Fix mod sprite bleeding border colour --- .../Components/TournamentBeatmapPanel.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs index 4116ffbec6..dc4c4b9ec6 100644 --- a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs +++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs @@ -125,13 +125,21 @@ namespace osu.Game.Tournament.Components if (!string.IsNullOrEmpty(mods)) { - AddInternal(new Sprite + AddInternal(new Container { - Texture = textures.Get($"mods/{mods}"), + RelativeSizeAxes = Axes.Y, + Width = 60, Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, Margin = new MarginPadding(10), - Scale = new Vector2(0.8f) + Child = new Sprite + { + FillMode = FillMode.Fit, + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Texture = textures.Get($"mods/{mods}"), + } }); } } From b902e5039636ec42a975e3b355f8b1f5dddad871 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Mar 2020 15:44:13 +0900 Subject: [PATCH 35/51] Add resolution selector in tournament setup screen --- .../Components/ControlPanel.cs | 6 ++-- osu.Game.Tournament/Screens/SetupScreen.cs | 29 ++++++++++++++++++- osu.Game.Tournament/TournamentGameBase.cs | 2 +- osu.Game.Tournament/TournamentSceneManager.cs | 18 ++++++++---- 4 files changed, 44 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tournament/Components/ControlPanel.cs b/osu.Game.Tournament/Components/ControlPanel.cs index fa5c941f1a..ef8c8767e0 100644 --- a/osu.Game.Tournament/Components/ControlPanel.cs +++ b/osu.Game.Tournament/Components/ControlPanel.cs @@ -22,9 +22,9 @@ namespace osu.Game.Tournament.Components public ControlPanel() { - RelativeSizeAxes = Axes.Both; + RelativeSizeAxes = Axes.Y; AlwaysPresent = true; - Width = 0.15f; + Width = TournamentSceneManager.CONTROL_AREA_WIDTH; Anchor = Anchor.TopRight; InternalChildren = new Drawable[] @@ -47,8 +47,8 @@ namespace osu.Game.Tournament.Components Origin = Anchor.TopCentre, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Width = 0.75f, Position = new Vector2(0, 35f), + Padding = new MarginPadding(5), Direction = FillDirection.Vertical, Spacing = new Vector2(0, 5f), }, diff --git a/osu.Game.Tournament/Screens/SetupScreen.cs b/osu.Game.Tournament/Screens/SetupScreen.cs index 023582166c..b7f8b2bfd6 100644 --- a/osu.Game.Tournament/Screens/SetupScreen.cs +++ b/osu.Game.Tournament/Screens/SetupScreen.cs @@ -3,7 +3,10 @@ using System; using System.Collections.Generic; +using System.Drawing; using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.UserInterface; @@ -22,6 +25,7 @@ namespace osu.Game.Tournament.Screens private FillFlowContainer fillFlow; private LoginOverlay loginOverlay; + private ActionableInfo resolution; [Resolved] private MatchIPCInfo ipc { get; set; } @@ -32,9 +36,13 @@ namespace osu.Game.Tournament.Screens [Resolved] private RulesetStore rulesets { get; set; } + private Bindable windowSize; + [BackgroundDependencyLoader] - private void load() + private void load(FrameworkConfigManager frameworkConfig) { + windowSize = frameworkConfig.GetBindable(FrameworkSetting.WindowedSize); + InternalChild = fillFlow = new FillFlowContainer { RelativeSizeAxes = Axes.X, @@ -48,6 +56,9 @@ namespace osu.Game.Tournament.Screens reload(); } + [Resolved] + private Framework.Game game { get; set; } + private void reload() { var fileBasedIpc = ipc as FileBasedIPC; @@ -97,9 +108,25 @@ namespace osu.Game.Tournament.Screens Items = rulesets.AvailableRulesets, Current = LadderInfo.Ruleset, }, + resolution = new ActionableInfo + { + Label = "Stream area resolution", + ButtonText = "Set to 1080p", + Action = () => + { + windowSize.Value = new Size((int)(1920 / TournamentSceneManager.STREAM_AREA_WIDTH * TournamentSceneManager.REQUIRED_WIDTH), 1080); + } + } }; } + protected override void Update() + { + base.Update(); + + resolution.Value = $"{ScreenSpaceDrawQuad.Width:N0}x{ScreenSpaceDrawQuad.Height:N0}"; + } + public class LabelledDropdown : LabelledComponent, T> { public LabelledDropdown() diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index 41822ae2c3..85db9e61fb 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -65,7 +65,7 @@ namespace osu.Game.Tournament windowSize = frameworkConfig.GetBindable(FrameworkSetting.WindowedSize); windowSize.BindValueChanged(size => ScheduleAfterChildren(() => { - var minWidth = (int)(size.NewValue.Height / 9f * 16 + 400); + var minWidth = (int)(size.NewValue.Height / 768f * TournamentSceneManager.REQUIRED_WIDTH) - 1; heightWarning.Alpha = size.NewValue.Width < minWidth ? 1 : 0; }), true); diff --git a/osu.Game.Tournament/TournamentSceneManager.cs b/osu.Game.Tournament/TournamentSceneManager.cs index ef8d16011d..23fcb01db7 100644 --- a/osu.Game.Tournament/TournamentSceneManager.cs +++ b/osu.Game.Tournament/TournamentSceneManager.cs @@ -33,6 +33,12 @@ namespace osu.Game.Tournament private Container screens; private TourneyVideo video; + public const float CONTROL_AREA_WIDTH = 160; + + public const float STREAM_AREA_WIDTH = 1366; + + public const double REQUIRED_WIDTH = TournamentSceneManager.CONTROL_AREA_WIDTH * 2 + TournamentSceneManager.STREAM_AREA_WIDTH; + [Cached] private TournamentMatchChatDisplay chat = new TournamentMatchChatDisplay(); @@ -51,13 +57,13 @@ namespace osu.Game.Tournament { new Container { - RelativeSizeAxes = Axes.Both, - X = 200, + RelativeSizeAxes = Axes.Y, + X = CONTROL_AREA_WIDTH, FillMode = FillMode.Fit, FillAspectRatio = 16 / 9f, Anchor = Anchor.TopLeft, Origin = Anchor.TopLeft, - Size = new Vector2(0.8f, 1), + Width = STREAM_AREA_WIDTH, //Masking = true, Children = new Drawable[] { @@ -96,7 +102,7 @@ namespace osu.Game.Tournament new Container { RelativeSizeAxes = Axes.Y, - Width = 200, + Width = CONTROL_AREA_WIDTH, Children = new Drawable[] { new Box @@ -108,8 +114,8 @@ namespace osu.Game.Tournament { RelativeSizeAxes = Axes.Both, Direction = FillDirection.Vertical, - Spacing = new Vector2(2), - Padding = new MarginPadding(2), + Spacing = new Vector2(5), + Padding = new MarginPadding(5), Children = new Drawable[] { new ScreenButton(typeof(SetupScreen)) { Text = "Setup", RequestSelection = SetScreen }, From bee855bd1d914fd17cd108fb9fb55f8b6f1519fa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Mar 2020 15:54:46 +0900 Subject: [PATCH 36/51] Remove using --- osu.Game.Tournament/Components/TournamentBeatmapPanel.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs index dc4c4b9ec6..477bf4bd63 100644 --- a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs +++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs @@ -16,7 +16,6 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Tournament.Models; -using osuTK; using osuTK.Graphics; namespace osu.Game.Tournament.Components From 7e9d28b1b13120372a7afd6599d7d905817043b8 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 13 Mar 2020 13:42:33 +0300 Subject: [PATCH 37/51] Fix slider ball colour affects follow circle --- .../Objects/Drawables/Pieces/SliderBall.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs index c871089acd..287a2d7e92 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs @@ -23,9 +23,16 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { public Func GetInitialHitAction; + public new Color4 Colour + { + get => ball.Colour; + set => ball.Colour = value; + } + private readonly Slider slider; private readonly Drawable followCircle; private readonly DrawableSlider drawableSlider; + private readonly CircularContainer ball; public SliderBall(Slider slider, DrawableSlider drawableSlider = null) { @@ -47,7 +54,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces Alpha = 0, Child = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderFollowCircle), _ => new DefaultFollowCircle()), }, - new CircularContainer + ball = new CircularContainer { Masking = true, RelativeSizeAxes = Axes.Both, From 097bd37e37c38095113bd706717a9601afc1f3c0 Mon Sep 17 00:00:00 2001 From: TheWildTree Date: Fri, 13 Mar 2020 18:27:33 +0100 Subject: [PATCH 38/51] Fix SelectorTab crashing tests after a reload For some reason, the default channel type (Public) caused the channel manager to attempt to connect to an API, which was null at that time, after hot reloading the test environment (via dynamic compilation). Changing the channel type seems to fix that. --- osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs b/osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs index d5d9a6c2ce..5fb56a3f75 100644 --- a/osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs +++ b/osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs @@ -39,6 +39,7 @@ namespace osu.Game.Overlays.Chat.Tabs public ChannelSelectorTabChannel() { Name = "+"; + Type = ChannelType.Temporary; } } } From 8991e88039b0af4e39dd7122588542f7a916ccf8 Mon Sep 17 00:00:00 2001 From: TheWildTree Date: Fri, 13 Mar 2020 18:33:06 +0100 Subject: [PATCH 39/51] Fix active tab closing behaviour --- .../Overlays/Chat/Tabs/ChannelTabControl.cs | 33 +++++++------------ 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs b/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs index 104495ae01..a72f182450 100644 --- a/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs +++ b/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs @@ -41,7 +41,7 @@ namespace osu.Game.Overlays.Chat.Tabs // performTabSort might've made selectorTab's position wonky, fix it TabContainer.SetLayoutPosition(selectorTab, float.MaxValue); - ((ChannelTabItem)item).OnRequestClose += tabCloseRequested; + ((ChannelTabItem)item).OnRequestClose += channelItem => OnRequestLeave?.Invoke(channelItem.Value); base.AddTabItem(item, addToDropdown); } @@ -74,18 +74,24 @@ namespace osu.Game.Overlays.Chat.Tabs /// /// Removes a channel from the ChannelTabControl. - /// If the selected channel is the one that is beeing removed, the next available channel will be selected. + /// If the selected channel is the one that is being removed, the next available channel will be selected. /// /// The channel that is going to be removed. public void RemoveChannel(Channel channel) { - RemoveItem(channel); - if (Current.Value == channel) { - // Prefer non-selector channels first - Current.Value = Items.FirstOrDefault(c => !(c is ChannelSelectorTabItem.ChannelSelectorTabChannel)) ?? Items.FirstOrDefault(); + var allChannels = TabContainer.AllTabItems.Select(tab => tab.Value).ToList(); + var isNextTabSelector = allChannels[allChannels.IndexOf(channel) + 1] == selectorTab.Value; + + // selectorTab is not switchable, so we have to explicitly select it if it's the only tab left + if (isNextTabSelector && allChannels.Count == 2) + SelectTab(selectorTab); + else + SwitchTab(isNextTabSelector ? -1 : 1); } + + RemoveItem(channel); } protected override void SelectTab(TabItem tab) @@ -100,21 +106,6 @@ namespace osu.Game.Overlays.Chat.Tabs selectorTab.Active.Value = false; } - private void tabCloseRequested(TabItem tab) - { - int totalTabs = TabContainer.Count - 1; // account for selectorTab - int currentIndex = Math.Clamp(TabContainer.IndexOf(tab), 1, totalTabs); - - if (tab == SelectedTab && totalTabs > 1) - // Select the tab after tab-to-be-removed's index, or the tab before if current == last - SelectTab(TabContainer[currentIndex == totalTabs ? currentIndex - 1 : currentIndex + 1]); - else if (totalTabs == 1 && !selectorTab.Active.Value) - // Open channel selection overlay if all channel tabs will be closed after removing this tab - SelectTab(selectorTab); - - OnRequestLeave?.Invoke(tab.Value); - } - protected override TabFillFlowContainer CreateTabFlow() => new ChannelTabFillFlowContainer { Direction = FillDirection.Full, From 694e56b0d13cc90e0f82b1f14661dd06fbf03f8d Mon Sep 17 00:00:00 2001 From: TheWildTree Date: Fri, 13 Mar 2020 18:33:58 +0100 Subject: [PATCH 40/51] Add non-PM chat tabs to tests --- .../Visual/Online/TestSceneChatOverlay.cs | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index 19bdaff6ff..8aa30c7fa3 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -196,12 +196,22 @@ namespace osu.Game.Tests.Visual.Online private class TestTabControl : ChannelTabControl { - protected override TabItem CreateTabItem(Channel value) => new TestChannelTabItem(value); + protected override TabItem CreateTabItem(Channel value) + { + switch (value.Type) + { + case ChannelType.PM: + return new TestPrivateChannelTabItem(value); + + default: + return new TestChannelTabItem(value); + } + } public new IReadOnlyDictionary> TabMap => base.TabMap; } - private class TestChannelTabItem : PrivateChannelTabItem + private class TestChannelTabItem : ChannelTabItem { public TestChannelTabItem(Channel channel) : base(channel) @@ -210,5 +220,15 @@ namespace osu.Game.Tests.Visual.Online public new ClickableContainer CloseButton => base.CloseButton; } + + private class TestPrivateChannelTabItem : PrivateChannelTabItem + { + public TestPrivateChannelTabItem(Channel channel) + : base(channel) + { + } + + public new ClickableContainer CloseButton => base.CloseButton; + } } } From 0bbae094ddf854d6e9d6ee3c3197df0577071231 Mon Sep 17 00:00:00 2001 From: TheWildTree Date: Fri, 13 Mar 2020 18:34:22 +0100 Subject: [PATCH 41/51] Add active tab closing behaviour tests --- .../Visual/Online/TestSceneChatOverlay.cs | 76 +++++++++++++++++-- 1 file changed, 70 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index 8aa30c7fa3..ede99c06be 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -38,8 +38,13 @@ namespace osu.Game.Tests.Visual.Online private TestChatOverlay chatOverlay; private ChannelManager channelManager; + private IEnumerable visibleChannels => chatOverlay.ChannelTabControl.VisibleItems.Where(channel => channel.Name != "+"); + private IEnumerable joinedChannels => chatOverlay.ChannelTabControl.Items.Where(channel => channel.Name != "+"); private readonly List channels; + private Channel currentChannel => channelManager.CurrentChannel.Value; + private Channel nextChannel => joinedChannels.ElementAt(joinedChannels.ToList().IndexOf(currentChannel) + 1); + private Channel previousChannel => joinedChannels.ElementAt(joinedChannels.ToList().IndexOf(currentChannel) - 1); private Channel channel1 => channels[0]; private Channel channel2 => channels[1]; @@ -91,7 +96,7 @@ namespace osu.Game.Tests.Visual.Online AddStep("Join channel 1", () => channelManager.JoinChannel(channel1)); AddStep("Switch to channel 1", () => clickDrawable(chatOverlay.TabMap[channel1])); - AddAssert("Current channel is channel 1", () => channelManager.CurrentChannel.Value == channel1); + AddAssert("Current channel is channel 1", () => currentChannel == channel1); AddAssert("Channel selector was closed", () => chatOverlay.SelectionOverlayState == Visibility.Hidden); } @@ -102,12 +107,12 @@ namespace osu.Game.Tests.Visual.Online AddStep("Join channel 2", () => channelManager.JoinChannel(channel2)); AddStep("Switch to channel 2", () => clickDrawable(chatOverlay.TabMap[channel2])); - AddStep("Close channel 2", () => clickDrawable(((TestChannelTabItem)chatOverlay.TabMap[channel2]).CloseButton.Child)); + AddStep("Close channel 2", () => clickDrawable(((TestPrivateChannelTabItem)chatOverlay.TabMap[channel2]).CloseButton.Child)); AddAssert("Selector remained closed", () => chatOverlay.SelectionOverlayState == Visibility.Hidden); - AddAssert("Current channel is channel 1", () => channelManager.CurrentChannel.Value == channel1); + AddAssert("Current channel is channel 1", () => currentChannel == channel1); - AddStep("Close channel 1", () => clickDrawable(((TestChannelTabItem)chatOverlay.TabMap[channel1]).CloseButton.Child)); + AddStep("Close channel 1", () => clickDrawable(((TestPrivateChannelTabItem)chatOverlay.TabMap[channel1]).CloseButton.Child)); AddAssert("Selector is visible", () => chatOverlay.SelectionOverlayState == Visibility.Visible); } @@ -140,10 +145,67 @@ namespace osu.Game.Tests.Visual.Online var targetNumberKey = oneBasedIndex % 10; var targetChannel = channels[zeroBasedIndex]; AddStep($"press Alt+{targetNumberKey}", () => pressChannelHotkey(targetNumberKey)); - AddAssert($"channel #{oneBasedIndex} is selected", () => channelManager.CurrentChannel.Value == targetChannel); + AddAssert($"channel #{oneBasedIndex} is selected", () => currentChannel == targetChannel); } } + private Channel expectedChannel; + + [Test] + public void TestCloseChannelWhileActive() + { + AddUntilStep("Join until dropdown has channels", () => + { + if (visibleChannels.Count() < joinedChannels.Count()) + return true; + + // Using temporary channels because they don't hide their names when not active + Channel toAdd = new Channel { Name = $"test channel {joinedChannels.Count()}", Type = ChannelType.Temporary }; + channelManager.JoinChannel(toAdd); + + return false; + }); + + AddStep("Switch to last tab", () => clickDrawable(chatOverlay.TabMap[visibleChannels.Last()])); + AddAssert("Last visible selected", () => currentChannel == visibleChannels.Last()); + + // Closing the last channel before dropdown + AddStep("Close current channel", () => + { + expectedChannel = nextChannel; + chatOverlay.ChannelTabControl.RemoveChannel(currentChannel); + }); + AddAssert("Next channel selected", () => currentChannel == expectedChannel); + + // Depending on the window size, one more channel might need to be closed for the selectorTab to appear + AddUntilStep("Close channels until selector visible", () => + { + if (chatOverlay.ChannelTabControl.VisibleItems.Last().Name == "+") + return true; + + chatOverlay.ChannelTabControl.RemoveChannel(visibleChannels.Last()); + return false; + }); + AddAssert("Last visible selected", () => currentChannel == visibleChannels.Last()); + + // Closing the last channel with dropdown no longer present + AddStep("Close last when selector next", () => + { + expectedChannel = previousChannel; + chatOverlay.ChannelTabControl.RemoveChannel(currentChannel); + }); + AddAssert("Channel changed to previous", () => currentChannel == expectedChannel); + + // Standard channel closing + AddStep("Switch to previous channel", () => chatOverlay.ChannelTabControl.SwitchTab(-1)); + AddStep("Close current channel", () => + { + expectedChannel = nextChannel; + chatOverlay.ChannelTabControl.RemoveChannel(currentChannel); + }); + AddAssert("Channel changed to next", () => currentChannel == expectedChannel); + } + private void pressChannelHotkey(int number) { var channelKey = Key.Number0 + number; @@ -187,6 +249,8 @@ namespace osu.Game.Tests.Visual.Online { public Visibility SelectionOverlayState => ChannelSelectionOverlay.State.Value; + public new ChannelTabControl ChannelTabControl => base.ChannelTabControl; + public new ChannelSelectionOverlay ChannelSelectionOverlay => base.ChannelSelectionOverlay; protected override ChannelTabControl CreateChannelTabControl() => new TestTabControl(); From 8d3cab0e1696e62c3c018cab91a1ee797b8bcc03 Mon Sep 17 00:00:00 2001 From: TheWildTree Date: Fri, 13 Mar 2020 18:58:32 +0100 Subject: [PATCH 42/51] Trim whitespace --- osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index ede99c06be..297a51c4a5 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -107,12 +107,12 @@ namespace osu.Game.Tests.Visual.Online AddStep("Join channel 2", () => channelManager.JoinChannel(channel2)); AddStep("Switch to channel 2", () => clickDrawable(chatOverlay.TabMap[channel2])); - AddStep("Close channel 2", () => clickDrawable(((TestPrivateChannelTabItem)chatOverlay.TabMap[channel2]).CloseButton.Child)); + AddStep("Close channel 2", () => clickDrawable(((TestChannelTabItem)chatOverlay.TabMap[channel2]).CloseButton.Child)); AddAssert("Selector remained closed", () => chatOverlay.SelectionOverlayState == Visibility.Hidden); AddAssert("Current channel is channel 1", () => currentChannel == channel1); - AddStep("Close channel 1", () => clickDrawable(((TestPrivateChannelTabItem)chatOverlay.TabMap[channel1]).CloseButton.Child)); + AddStep("Close channel 1", () => clickDrawable(((TestChannelTabItem)chatOverlay.TabMap[channel1]).CloseButton.Child)); AddAssert("Selector is visible", () => chatOverlay.SelectionOverlayState == Visibility.Visible); } From 38d00c7f0ae6abb0283a366fadfca6cabb048948 Mon Sep 17 00:00:00 2001 From: TheWildTree Date: Fri, 13 Mar 2020 21:29:10 +0100 Subject: [PATCH 43/51] Revert unnecessary changes and actually trim the whitespace --- osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index 297a51c4a5..736bfd8e7d 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -107,12 +107,12 @@ namespace osu.Game.Tests.Visual.Online AddStep("Join channel 2", () => channelManager.JoinChannel(channel2)); AddStep("Switch to channel 2", () => clickDrawable(chatOverlay.TabMap[channel2])); - AddStep("Close channel 2", () => clickDrawable(((TestChannelTabItem)chatOverlay.TabMap[channel2]).CloseButton.Child)); + AddStep("Close channel 2", () => clickDrawable(((TestPrivateChannelTabItem)chatOverlay.TabMap[channel2]).CloseButton.Child)); AddAssert("Selector remained closed", () => chatOverlay.SelectionOverlayState == Visibility.Hidden); AddAssert("Current channel is channel 1", () => currentChannel == channel1); - AddStep("Close channel 1", () => clickDrawable(((TestChannelTabItem)chatOverlay.TabMap[channel1]).CloseButton.Child)); + AddStep("Close channel 1", () => clickDrawable(((TestPrivateChannelTabItem)chatOverlay.TabMap[channel1]).CloseButton.Child)); AddAssert("Selector is visible", () => chatOverlay.SelectionOverlayState == Visibility.Visible); } @@ -189,7 +189,7 @@ namespace osu.Game.Tests.Visual.Online AddAssert("Last visible selected", () => currentChannel == visibleChannels.Last()); // Closing the last channel with dropdown no longer present - AddStep("Close last when selector next", () => + AddStep("Close last when selector next", () => { expectedChannel = previousChannel; chatOverlay.ChannelTabControl.RemoveChannel(currentChannel); @@ -198,7 +198,7 @@ namespace osu.Game.Tests.Visual.Online // Standard channel closing AddStep("Switch to previous channel", () => chatOverlay.ChannelTabControl.SwitchTab(-1)); - AddStep("Close current channel", () => + AddStep("Close current channel", () => { expectedChannel = nextChannel; chatOverlay.ChannelTabControl.RemoveChannel(currentChannel); From 202c8cdad8ef0cf223b48f6a0590622004fa2efd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 14 Mar 2020 15:35:59 +0900 Subject: [PATCH 44/51] Add braces to satisfy codefactor --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 2c8b080ee3..a3dc58bc19 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -348,8 +348,8 @@ namespace osu.Game.Rulesets.Catch.UI UpdatePosition((float)(X + direction * Clock.ElapsedFrameTime * speed)); // Correct overshooting. - if (hyperDashDirection > 0 && hyperDashTargetPosition < X || - hyperDashDirection < 0 && hyperDashTargetPosition > X) + if ((hyperDashDirection > 0 && hyperDashTargetPosition < X) || + (hyperDashDirection < 0 && hyperDashTargetPosition > X)) { X = hyperDashTargetPosition; SetHyperDashState(); From 74c9d5fc93f85780df7da15124e92b386e4ea517 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 14 Mar 2020 13:45:55 +0300 Subject: [PATCH 45/51] Use AccentColour --- .../Objects/Drawables/DrawableSlider.cs | 2 +- .../Objects/Drawables/Pieces/SliderBall.cs | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 7403649184..ccc731779d 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -185,7 +185,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables base.ApplySkin(skin, allowFallback); bool allowBallTint = skin.GetConfig(OsuSkinConfiguration.AllowSliderBallTint)?.Value ?? false; - Ball.Colour = allowBallTint ? AccentColour.Value : Color4.White; + Ball.AccentColour = allowBallTint ? AccentColour.Value : Color4.White; } protected override void CheckForResult(bool userTriggered, double timeOffset) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs index 287a2d7e92..0c046604c0 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs @@ -16,17 +16,24 @@ using osu.Game.Rulesets.Osu.Skinning; using osuTK.Graphics; using osu.Game.Skinning; using osuTK; +using osu.Game.Graphics; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { - public class SliderBall : CircularContainer, ISliderProgress, IRequireHighFrequencyMousePosition + public class SliderBall : CircularContainer, ISliderProgress, IRequireHighFrequencyMousePosition, IHasAccentColour { public Func GetInitialHitAction; - public new Color4 Colour + private Color4 accentColour; + + public Color4 AccentColour { - get => ball.Colour; - set => ball.Colour = value; + get => accentColour; + set + { + accentColour = value; + ball.Colour = value; + } } private readonly Slider slider; From c271d1755715822bd0dd5d47040a91da74ab23f6 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 14 Mar 2020 14:07:52 +0300 Subject: [PATCH 46/51] Remove useless field --- .../Objects/Drawables/Pieces/SliderBall.cs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs index 0c046604c0..5a6dd49c44 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs @@ -24,16 +24,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { public Func GetInitialHitAction; - private Color4 accentColour; - public Color4 AccentColour { - get => accentColour; - set - { - accentColour = value; - ball.Colour = value; - } + get => ball.Colour; + set => ball.Colour = value; } private readonly Slider slider; From 62ce5031269446adbc8c107fa16f832678a5d441 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 15 Mar 2020 00:36:21 +0900 Subject: [PATCH 47/51] Fix changelog alignment and italics usage --- osu.Game/Overlays/Changelog/ChangelogBuild.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogBuild.cs b/osu.Game/Overlays/Changelog/ChangelogBuild.cs index 8aee76cb08..48bf6c2ddd 100644 --- a/osu.Game/Overlays/Changelog/ChangelogBuild.cs +++ b/osu.Game/Overlays/Changelog/ChangelogBuild.cs @@ -93,6 +93,7 @@ namespace osu.Game.Overlays.Changelog Direction = FillDirection.Full, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, + TextAnchor = Anchor.BottomLeft, } } }; @@ -125,7 +126,7 @@ namespace osu.Game.Overlays.Changelog title.AddText("by ", t => { - t.Font = fontMedium.With(italics: true); + t.Font = fontMedium; t.Colour = entryColour; t.Padding = new MarginPadding { Left = 10 }; }); @@ -138,7 +139,7 @@ namespace osu.Game.Overlays.Changelog Id = entry.GithubUser.UserId.Value }, t => { - t.Font = fontMedium.With(italics: true); + t.Font = fontMedium; t.Colour = entryColour; }); } @@ -146,7 +147,7 @@ namespace osu.Game.Overlays.Changelog { title.AddLink(entry.GithubUser.DisplayName, entry.GithubUser.GithubUrl, t => { - t.Font = fontMedium.With(italics: true); + t.Font = fontMedium; t.Colour = entryColour; }); } @@ -154,7 +155,7 @@ namespace osu.Game.Overlays.Changelog { title.AddText(entry.GithubUser.DisplayName, t => { - t.Font = fontMedium.With(italics: true); + t.Font = fontMedium; t.Colour = entryColour; }); } From cd604785a87e14dfec1bf2317762f44325d1c196 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 15 Mar 2020 00:38:27 +0900 Subject: [PATCH 48/51] Ignore italics specification for now --- osu.Game/Graphics/OsuFont.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/OsuFont.cs b/osu.Game/Graphics/OsuFont.cs index 076cc241ed..7c78141b4d 100644 --- a/osu.Game/Graphics/OsuFont.cs +++ b/osu.Game/Graphics/OsuFont.cs @@ -31,7 +31,14 @@ namespace osu.Game.Graphics /// Whether all characters should be spaced the same distance apart. /// The . public static FontUsage GetFont(Typeface typeface = Typeface.Torus, float size = DEFAULT_FONT_SIZE, FontWeight weight = FontWeight.Medium, bool italics = false, bool fixedWidth = false) - => new FontUsage(GetFamilyString(typeface), size, GetWeightString(typeface, weight), italics, fixedWidth); + => new FontUsage(GetFamilyString(typeface), size, GetWeightString(typeface, weight), getItalics(italics), fixedWidth); + + private static bool getItalics(in bool italicsRequested) + { + // right now none of our fonts support italics. + // should add exceptions to this rule if they come up. + return false; + } /// /// Retrieves the string representation of a . From 0b788065b46a0a579e849bfe893ba0b3b57d301a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 15 Mar 2020 01:00:25 +0900 Subject: [PATCH 49/51] Update resources package --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index f623a92ade..1a63b893a1 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index ba6f0e2251..95d09e84eb 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -22,7 +22,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 54cd400d51..ce7ff38988 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + From 1a6056637b9918f66ad0dc5282d660665c17ebdc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 15 Mar 2020 01:43:29 +0900 Subject: [PATCH 50/51] Turn off italics test for now (may come back if we switch chat to content font) --- osu.Game.Tests/Visual/Online/TestSceneChatLink.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs index c76d4fd5b8..7a257a1603 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs @@ -12,7 +12,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; using osu.Game.Online.Chat; using osu.Game.Overlays; using osu.Game.Overlays.Chat; @@ -78,7 +77,7 @@ namespace osu.Game.Tests.Visual.Online AddAssert($"msg #{index} has {linkAmount} link(s)", () => newLine.Message.Links.Count == linkAmount); AddAssert($"msg #{index} has the right action", hasExpectedActions); - AddAssert($"msg #{index} is " + (isAction ? "italic" : "not italic"), () => newLine.ContentFlow.Any() && isAction == isItalic()); + //AddAssert($"msg #{index} is " + (isAction ? "italic" : "not italic"), () => newLine.ContentFlow.Any() && isAction == isItalic()); AddAssert($"msg #{index} shows {linkAmount} link(s)", isShowingLinks); bool hasExpectedActions() @@ -97,7 +96,7 @@ namespace osu.Game.Tests.Visual.Online return true; } - bool isItalic() => newLine.ContentFlow.Where(d => d is OsuSpriteText).Cast().All(sprite => sprite.Font.Italics); + //bool isItalic() => newLine.ContentFlow.Where(d => d is OsuSpriteText).Cast().All(sprite => sprite.Font.Italics); bool isShowingLinks() { From acd280c85552ec22073606807e75eeda663ebe11 Mon Sep 17 00:00:00 2001 From: TheWildTree Date: Sun, 15 Mar 2020 22:13:26 +0100 Subject: [PATCH 51/51] Add System channel type and use it for the ChannelSelectorTab --- osu.Game/Online/Chat/ChannelType.cs | 1 + osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Chat/ChannelType.cs b/osu.Game/Online/Chat/ChannelType.cs index 7d2b661164..151efc4645 100644 --- a/osu.Game/Online/Chat/ChannelType.cs +++ b/osu.Game/Online/Chat/ChannelType.cs @@ -12,5 +12,6 @@ namespace osu.Game.Online.Chat Temporary, PM, Group, + System, } } diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs b/osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs index 5fb56a3f75..e3ede04edd 100644 --- a/osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs +++ b/osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs @@ -39,7 +39,7 @@ namespace osu.Game.Overlays.Chat.Tabs public ChannelSelectorTabChannel() { Name = "+"; - Type = ChannelType.Temporary; + Type = ChannelType.System; } } }