diff --git a/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/Components/RankedPlayStageDisplay.cs b/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/Components/RankedPlayStageDisplay.cs index 8bcd66292a..3e387f8405 100644 --- a/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/Components/RankedPlayStageDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/Components/RankedPlayStageDisplay.cs @@ -14,6 +14,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Sprites; using osu.Game.Online.Multiplayer; +using osu.Game.Online.Multiplayer.MatchTypes.RankedPlay; using osu.Game.Online.RankedPlay; using osuTK; using osuTK.Graphics; @@ -37,6 +38,40 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Components private DateTimeOffset countdownStartTime; private DateTimeOffset countdownEndTime; + private RankedPlayStage? activeStage; + + private LocalisableString heading; + + /// + /// Heading text to be displayed indicating the purpose of the current stage. + /// + public LocalisableString Heading + { + get => heading; + set + { + heading = value; + if (headingText != null) + headingText.Text = value; + } + } + + private LocalisableString caption; + + /// + /// Subtitle text to be displayed indicating the action a user should take in the current stage. + /// + public LocalisableString Caption + { + get => caption; + set + { + caption = value; + if (captionText != null) + captionText.Text = value; + } + } + public RankedPlayStageDisplay(RankedPlayColourScheme colourScheme) { this.colourScheme = colourScheme; @@ -152,7 +187,6 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Components Left = 10 }, UseFullGlyphHeight = false, - Text = "00:27:123", Font = OsuFont.TorusAlternate.With(size: 16, fixedWidth: true, weight: FontWeight.SemiBold) } ] @@ -164,61 +198,12 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Components Top = 80, Left = 20 }, - Colour = CaptionColour ?? colourScheme.Primary, Text = Caption, Font = OsuFont.TorusAlternate.With(size: 24, weight: FontWeight.SemiBold) } }; } - private LocalisableString heading; - - /// - /// Heading text to be displayed indicating the purpose of the current stage. - /// - public LocalisableString Heading - { - get => heading; - set - { - heading = value; - if (headingText != null) - headingText.Text = value; - } - } - - private LocalisableString caption; - - /// - /// Subtitle text to be displayed indicating the action a user should take in the current stage. - /// - public LocalisableString Caption - { - get => caption; - set - { - caption = value; - if (captionText != null) - captionText.Text = value; - } - } - - private Color4? captionColour; - - /// - /// Overrides the default caption colour from the colour scheme with a custom one. - /// - public Color4? CaptionColour - { - get => captionColour; - set - { - captionColour = value; - if (captionText != null) - captionText.Colour = value ?? colourScheme.Primary; - } - } - protected override void LoadComplete() { base.LoadComplete(); @@ -254,18 +239,37 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Components private void onCountdownStarted(MultiplayerCountdown countdown) => Scheduler.Add(() => { - if (countdown is not RankedPlayStageCountdown) + if (countdown is not RankedPlayStageCountdown stageCountdown) return; + switch (stageCountdown.Stage) + { + case RankedPlayStage.CardDiscard: + // Discard stage ends when both players have discarded, but adds a 3 second delay before completing. + // Showing this in the countdown just creates visual noise, so let's handle internally. + if (activeStage == stageCountdown.Stage) + return; + + break; + } + + activeStage = stageCountdown.Stage; countdownStartTime = DateTimeOffset.Now; countdownEndTime = DateTimeOffset.Now + countdown.TimeRemaining; }); private void onCountdownStopped(MultiplayerCountdown countdown) => Scheduler.Add(() => { - if (countdown is not RankedPlayStageCountdown) + if (countdown is not RankedPlayStageCountdown stageCountdown) return; + switch (stageCountdown.Stage) + { + // See above special case handling. + case RankedPlayStage.CardDiscard: + return; + } + countdownEndTime = DateTimeOffset.Now; }); diff --git a/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/DiscardScreen.cs b/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/DiscardScreen.cs index 42f1dd94fb..bc6a725fc8 100644 --- a/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/DiscardScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/DiscardScreen.cs @@ -12,6 +12,7 @@ using osu.Framework.Audio.Sample; using osu.Framework.Graphics; using osu.Framework.Graphics.Primitives; using osu.Framework.Localisation; +using osu.Framework.Threading; using osu.Game.Audio; using osu.Game.Graphics; using osu.Game.Graphics.Containers; @@ -23,7 +24,6 @@ using osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Card; using osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Components; using osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Hand; using osuTK; -using osuTK.Graphics; namespace osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay { @@ -36,7 +36,6 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay public override bool ShowStageOverlay => true; public override LocalisableString StageHeading => "Discard Phase"; - protected override LocalisableString StageCaption => "Replace cards from your hand"; private PlayerHandOfCards playerHand = null!; private ShearedButton discardButton = null!; @@ -62,9 +61,11 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay private DateTimeOffset stageEndTime; private TimeSpan stageDuration; + private ScheduledDelegate? waitingOpponentTextUpdate; + public DiscardScreen() { - StageDisplay.CaptionColour = Color4.White; + StageCaption = "Replace cards from your hand"; } [BackgroundDependencyLoader] @@ -233,6 +234,12 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay playerHand.SelectionMode = HandSelectionMode.Disabled; hasDiscardedCards = true; + + StageCaption = string.Empty; + + // A bit awkward, but we're delaying this until we're mostly sure the opponent is still discarding. + // See the countdown reset logic for DiscardStage which gives 3 seconds for animation. + waitingOpponentTextUpdate = Scheduler.AddDelayed(() => StageCaption = "Waiting for your opponent...", 3200); } private readonly List discardedCards = new List(); @@ -313,6 +320,9 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay double presentationTime = Math.Max(earliestPresentationTime, Time.Current); Scheduler.AddDelayed(presentRemainingCards, presentationTime - Time.Current); + + waitingOpponentTextUpdate?.Cancel(); + StageCaption = string.Empty; } private void presentRemainingCards() diff --git a/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/EndedScreen.cs b/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/EndedScreen.cs index 8dfc8ebb60..46798343c6 100644 --- a/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/EndedScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/EndedScreen.cs @@ -28,7 +28,6 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay public Action? ExitRequested { get; init; } public override LocalisableString StageHeading => "Results"; - protected override LocalisableString StageCaption => string.Empty; [Resolved] private RankedPlayMatchInfo matchInfo { get; set; } = null!; diff --git a/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/GameplayScreen.cs b/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/GameplayScreen.cs index a1ef73d965..71fbaeacbd 100644 --- a/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/GameplayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/GameplayScreen.cs @@ -14,7 +14,6 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay public partial class GameplayScreen : RankedPlaySubScreen { public override LocalisableString StageHeading => "Gameplay"; - protected override LocalisableString StageCaption => string.Empty; [BackgroundDependencyLoader] private void load() diff --git a/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/GameplayWarmupScreen.cs b/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/GameplayWarmupScreen.cs index 854dcc0a6b..2ea154eeae 100644 --- a/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/GameplayWarmupScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/GameplayWarmupScreen.cs @@ -33,7 +33,6 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay public override bool ShowBeatmapBackground => true; public override LocalisableString StageHeading => "Gameplay"; - protected override LocalisableString StageCaption => string.Empty; [Cached(typeof(IBindable))] private readonly Bindable lastLookupResult = new Bindable(); diff --git a/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/Intro/IntroScreen.cs b/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/Intro/IntroScreen.cs index 2ec5621c23..017dc30f59 100644 --- a/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/Intro/IntroScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/Intro/IntroScreen.cs @@ -20,7 +20,6 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Intro public partial class IntroScreen : RankedPlaySubScreen { public override LocalisableString StageHeading => string.Empty; - protected override LocalisableString StageCaption => string.Empty; public IntroScreen() { diff --git a/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/OpponentPickScreen.cs b/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/OpponentPickScreen.cs index a9e743e8f9..cd5d2406a7 100644 --- a/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/OpponentPickScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/OpponentPickScreen.cs @@ -25,7 +25,6 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay public override bool ShowStageOverlay => true; public override LocalisableString StageHeading => "Pick Phase"; - protected override LocalisableString StageCaption => "Waiting for your opponent..."; protected override RankedPlayColourScheme ColourScheme => RankedPlayColourScheme.RED; @@ -40,6 +39,11 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay private const int card_play_samples = 2; private Sample?[]? cardPlaySamples; + public OpponentPickScreen() + { + StageCaption = "Waiting for your opponent..."; + } + [BackgroundDependencyLoader] private void load(AudioManager audio) { diff --git a/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/PickScreen.cs b/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/PickScreen.cs index e0e83b6624..bc96fd5877 100644 --- a/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/PickScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/PickScreen.cs @@ -31,7 +31,6 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay public override bool ShowStageOverlay => true; public override LocalisableString StageHeading => "Pick Phase"; - protected override LocalisableString StageCaption => "It's your turn to play a card!"; private PlayerHandOfCards playerHand = null!; private OpponentHandOfCards opponentHand = null!; @@ -46,7 +45,6 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay private Sample? timeRunningOutSample; private SampleChannel? timeRunningOutSampleChannel; - private Sample? timeUpBuzzerSample; private DateTimeOffset stageEndTime; private TimeSpan stageDuration; @@ -56,6 +54,11 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay /// private bool hasPlayedCard; + public PickScreen() + { + StageCaption = "It's your turn to play a card!"; + } + [BackgroundDependencyLoader] private void load(AudioManager audio) { @@ -103,7 +106,6 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay cardPlaySamples[i] = audio.Samples.Get($@"Multiplayer/Matchmaking/Ranked/card-play-{1 + i}"); timeRunningOutSample = audio.Samples.Get(@"Multiplayer/Matchmaking/Ranked/time-running-out"); - timeUpBuzzerSample = audio.Samples.Get(@"Multiplayer/Matchmaking/Ranked/time-up"); } protected override void LoadComplete() @@ -209,14 +211,11 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay private void onCountdownStopped(MultiplayerCountdown countdown) => Scheduler.Add(() => { - if (countdown is not RankedPlayStageCountdown stageCountdown) + if (countdown is not RankedPlayStageCountdown) return; stageEndTime = DateTimeOffset.Now; stageDuration = TimeSpan.Zero; - - if (stageCountdown.Stage == RankedPlayStage.CardPlay && !hasPlayedCard) - timeUpBuzzerSample?.Play(); }); private void onPlayButtonClicked() diff --git a/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/RankedPlaySubScreen.cs b/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/RankedPlaySubScreen.cs index 3d49aaac4d..464e99d1b6 100644 --- a/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/RankedPlaySubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/RankedPlaySubScreen.cs @@ -37,7 +37,11 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay /// /// Subtitle text to be displayed indicating the action a user should take in the current stage. /// - protected abstract LocalisableString StageCaption { get; } + protected LocalisableString StageCaption + { + get => StageDisplay.Caption; + set => StageDisplay.Caption = value; + } /// /// The colour scheme commonly used for components of this screen. @@ -79,7 +83,6 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay StageDisplay = new RankedPlayStageDisplay(ColourScheme) { Heading = StageHeading, - Caption = StageCaption, Margin = new MarginPadding { Top = 60 }, State = { BindTarget = CountdownVisibility } }, diff --git a/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/ResultsScreen.cs b/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/ResultsScreen.cs index 2a16040268..70c501943e 100644 --- a/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/ResultsScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/ResultsScreen.cs @@ -40,7 +40,6 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay public partial class ResultsScreen : RankedPlaySubScreen { public override LocalisableString StageHeading => "Results"; - protected override LocalisableString StageCaption => string.Empty; public override bool ShowBeatmapBackground => true;