2022-03-23 09:37:53 +08:00
|
|
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
|
|
|
// See the LICENCE file in the repository root for full licence text.
|
|
|
|
|
2022-06-17 15:37:17 +08:00
|
|
|
#nullable disable
|
|
|
|
|
2022-03-23 09:37:53 +08:00
|
|
|
using System;
|
|
|
|
using System.Linq;
|
|
|
|
using JetBrains.Annotations;
|
|
|
|
using osu.Framework.Allocation;
|
2022-04-01 20:33:57 +08:00
|
|
|
using osu.Framework.Audio;
|
|
|
|
using osu.Framework.Audio.Sample;
|
2022-03-23 09:37:53 +08:00
|
|
|
using osu.Framework.Localisation;
|
2022-03-23 09:50:05 +08:00
|
|
|
using osu.Framework.Threading;
|
2022-03-23 09:37:53 +08:00
|
|
|
using osu.Game.Graphics;
|
|
|
|
using osu.Game.Online.Multiplayer;
|
2022-03-24 13:28:38 +08:00
|
|
|
using osu.Game.Screens.OnlinePlay.Components;
|
2022-03-23 09:37:53 +08:00
|
|
|
|
|
|
|
namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
|
|
|
{
|
2022-11-24 13:32:20 +08:00
|
|
|
public partial class MultiplayerReadyButton : ReadyButton
|
2022-03-23 09:37:53 +08:00
|
|
|
{
|
|
|
|
[Resolved]
|
|
|
|
private MultiplayerClient multiplayerClient { get; set; }
|
|
|
|
|
|
|
|
[Resolved]
|
|
|
|
private OsuColour colours { get; set; }
|
|
|
|
|
|
|
|
[CanBeNull]
|
|
|
|
private MultiplayerRoom room => multiplayerClient.Room;
|
|
|
|
|
2022-04-01 20:33:57 +08:00
|
|
|
private Sample countdownTickSample;
|
2022-04-07 12:12:33 +08:00
|
|
|
private Sample countdownWarnSample;
|
|
|
|
private Sample countdownWarnFinalSample;
|
2022-04-01 20:33:57 +08:00
|
|
|
|
|
|
|
[BackgroundDependencyLoader]
|
|
|
|
private void load(AudioManager audio)
|
|
|
|
{
|
|
|
|
countdownTickSample = audio.Samples.Get(@"Multiplayer/countdown-tick");
|
2022-04-07 12:12:33 +08:00
|
|
|
countdownWarnSample = audio.Samples.Get(@"Multiplayer/countdown-warn");
|
|
|
|
countdownWarnFinalSample = audio.Samples.Get(@"Multiplayer/countdown-warn-final");
|
2022-04-01 20:33:57 +08:00
|
|
|
}
|
|
|
|
|
2022-03-23 09:37:53 +08:00
|
|
|
protected override void LoadComplete()
|
|
|
|
{
|
|
|
|
base.LoadComplete();
|
|
|
|
|
2022-03-24 16:11:08 +08:00
|
|
|
multiplayerClient.RoomUpdated += onRoomUpdated;
|
2022-03-23 09:37:53 +08:00
|
|
|
onRoomUpdated();
|
|
|
|
}
|
|
|
|
|
2022-03-23 14:19:43 +08:00
|
|
|
private MultiplayerCountdown countdown;
|
2022-04-05 10:49:57 +08:00
|
|
|
private double countdownChangeTime;
|
2022-03-23 09:50:05 +08:00
|
|
|
private ScheduledDelegate countdownUpdateDelegate;
|
2022-03-23 09:37:53 +08:00
|
|
|
|
2022-03-24 16:11:08 +08:00
|
|
|
private void onRoomUpdated() => Scheduler.AddOnce(() =>
|
2022-03-23 09:37:53 +08:00
|
|
|
{
|
2022-09-01 17:53:35 +08:00
|
|
|
MultiplayerCountdown newCountdown = room?.ActiveCountdowns.SingleOrDefault(c => c is MatchStartCountdown);
|
2022-04-21 22:01:16 +08:00
|
|
|
|
|
|
|
if (newCountdown != countdown)
|
2022-03-29 10:24:26 +08:00
|
|
|
{
|
2022-09-01 17:53:35 +08:00
|
|
|
countdown = newCountdown;
|
2022-04-05 10:49:57 +08:00
|
|
|
countdownChangeTime = Time.Current;
|
2022-03-29 10:24:26 +08:00
|
|
|
}
|
2022-03-23 09:50:05 +08:00
|
|
|
|
2022-04-04 13:36:03 +08:00
|
|
|
scheduleNextCountdownUpdate();
|
|
|
|
|
|
|
|
updateButtonText();
|
|
|
|
updateButtonColour();
|
|
|
|
});
|
|
|
|
|
|
|
|
private void scheduleNextCountdownUpdate()
|
|
|
|
{
|
2022-04-05 14:48:18 +08:00
|
|
|
countdownUpdateDelegate?.Cancel();
|
|
|
|
|
2022-03-29 10:24:26 +08:00
|
|
|
if (countdown != null)
|
2022-04-04 13:36:03 +08:00
|
|
|
{
|
2022-04-05 14:48:18 +08:00
|
|
|
// The remaining time on a countdown may be at a fractional portion between two seconds.
|
|
|
|
// We want to align certain audio/visual cues to the point at which integer seconds change.
|
2022-04-05 14:51:04 +08:00
|
|
|
// To do so, we schedule to the next whole second. Note that scheduler invocation isn't
|
|
|
|
// guaranteed to be accurate, so this may still occur slightly late, but even in such a case
|
|
|
|
// the next invocation will be roughly correct.
|
2022-04-04 13:36:03 +08:00
|
|
|
double timeToNextSecond = countdownTimeRemaining.TotalMilliseconds % 1000;
|
|
|
|
|
|
|
|
countdownUpdateDelegate = Scheduler.AddDelayed(onCountdownTick, timeToNextSecond);
|
|
|
|
}
|
2022-03-23 09:50:05 +08:00
|
|
|
else
|
|
|
|
{
|
|
|
|
countdownUpdateDelegate?.Cancel();
|
|
|
|
countdownUpdateDelegate = null;
|
|
|
|
}
|
2022-03-23 14:19:43 +08:00
|
|
|
|
2022-04-04 13:36:03 +08:00
|
|
|
void onCountdownTick()
|
|
|
|
{
|
|
|
|
updateButtonText();
|
2022-04-05 00:04:57 +08:00
|
|
|
|
2022-05-02 21:46:14 +08:00
|
|
|
int secondsRemaining = (int)countdownTimeRemaining.TotalSeconds;
|
2022-04-05 00:04:57 +08:00
|
|
|
|
2022-04-05 10:49:57 +08:00
|
|
|
playTickSound(secondsRemaining);
|
2022-04-05 00:04:57 +08:00
|
|
|
|
2022-04-05 10:49:57 +08:00
|
|
|
if (secondsRemaining > 0)
|
|
|
|
scheduleNextCountdownUpdate();
|
2022-04-04 13:36:03 +08:00
|
|
|
}
|
|
|
|
}
|
2022-03-23 09:37:53 +08:00
|
|
|
|
2022-04-05 10:49:57 +08:00
|
|
|
private void playTickSound(int secondsRemaining)
|
|
|
|
{
|
|
|
|
if (secondsRemaining < 10) countdownTickSample?.Play();
|
2022-04-07 12:12:33 +08:00
|
|
|
|
|
|
|
if (secondsRemaining <= 3)
|
|
|
|
{
|
|
|
|
if (secondsRemaining > 0)
|
|
|
|
countdownWarnSample?.Play();
|
|
|
|
else
|
|
|
|
countdownWarnFinalSample?.Play();
|
|
|
|
}
|
2022-04-05 10:49:57 +08:00
|
|
|
}
|
|
|
|
|
2022-03-23 09:37:53 +08:00
|
|
|
private void updateButtonText()
|
|
|
|
{
|
|
|
|
if (room == null)
|
|
|
|
{
|
|
|
|
Text = "Ready";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var localUser = multiplayerClient.LocalUser;
|
|
|
|
|
|
|
|
int countReady = room.Users.Count(u => u.State == MultiplayerUserState.Ready);
|
|
|
|
int countTotal = room.Users.Count(u => u.State != MultiplayerUserState.Spectating);
|
|
|
|
string countText = $"({countReady} / {countTotal} ready)";
|
|
|
|
|
2022-03-23 14:19:43 +08:00
|
|
|
if (countdown != null)
|
2022-03-23 09:37:53 +08:00
|
|
|
{
|
2022-04-04 13:36:03 +08:00
|
|
|
string countdownText = $"Starting in {countdownTimeRemaining:mm\\:ss}";
|
2022-03-23 09:37:53 +08:00
|
|
|
|
|
|
|
switch (localUser?.State)
|
|
|
|
{
|
|
|
|
default:
|
|
|
|
Text = $"Ready ({countdownText.ToLowerInvariant()})";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MultiplayerUserState.Spectating:
|
|
|
|
case MultiplayerUserState.Ready:
|
|
|
|
Text = $"{countdownText} {countText}";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
switch (localUser?.State)
|
|
|
|
{
|
|
|
|
case MultiplayerUserState.Spectating:
|
|
|
|
case MultiplayerUserState.Ready:
|
2023-12-01 18:02:27 +08:00
|
|
|
Text = multiplayerClient.IsHost
|
2022-03-23 09:37:53 +08:00
|
|
|
? $"Start match {countText}"
|
|
|
|
: $"Waiting for host... {countText}";
|
2023-12-01 14:31:06 +08:00
|
|
|
break;
|
|
|
|
|
2023-12-05 15:58:16 +08:00
|
|
|
default:
|
|
|
|
// Show the abort button for the host as long as gameplay is in progress.
|
|
|
|
if (multiplayerClient.IsHost && room.State != MultiplayerRoomState.Open)
|
|
|
|
Text = "Abort the match";
|
|
|
|
else
|
|
|
|
Text = "Ready";
|
2022-03-23 09:37:53 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-04 13:36:03 +08:00
|
|
|
private TimeSpan countdownTimeRemaining
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
2022-04-05 10:49:57 +08:00
|
|
|
double timeElapsed = Time.Current - countdownChangeTime;
|
2022-04-04 13:36:03 +08:00
|
|
|
TimeSpan remaining;
|
|
|
|
|
2022-04-05 10:49:57 +08:00
|
|
|
if (timeElapsed > countdown.TimeRemaining.TotalMilliseconds)
|
2022-04-04 13:36:03 +08:00
|
|
|
remaining = TimeSpan.Zero;
|
|
|
|
else
|
2022-04-05 10:49:57 +08:00
|
|
|
remaining = countdown.TimeRemaining - TimeSpan.FromMilliseconds(timeElapsed);
|
2022-04-04 13:36:03 +08:00
|
|
|
|
|
|
|
return remaining;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-23 09:37:53 +08:00
|
|
|
private void updateButtonColour()
|
|
|
|
{
|
|
|
|
if (room == null)
|
|
|
|
{
|
|
|
|
setGreen();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var localUser = multiplayerClient.LocalUser;
|
|
|
|
|
|
|
|
switch (localUser?.State)
|
|
|
|
{
|
|
|
|
default:
|
2023-12-05 15:58:16 +08:00
|
|
|
// Show the abort button for the host as long as gameplay is in progress.
|
|
|
|
if (multiplayerClient.IsHost && room.State != MultiplayerRoomState.Open)
|
|
|
|
setRed();
|
|
|
|
else
|
|
|
|
setGreen();
|
2022-03-23 09:37:53 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case MultiplayerUserState.Spectating:
|
|
|
|
case MultiplayerUserState.Ready:
|
2023-12-01 18:02:27 +08:00
|
|
|
if (multiplayerClient.IsHost && !room.ActiveCountdowns.Any(c => c is MatchStartCountdown))
|
2022-03-23 09:37:53 +08:00
|
|
|
setGreen();
|
|
|
|
else
|
|
|
|
setYellow();
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
2023-12-01 14:31:06 +08:00
|
|
|
|
|
|
|
void setYellow() => BackgroundColour = colours.YellowDark;
|
|
|
|
|
|
|
|
void setGreen() => BackgroundColour = colours.Green;
|
|
|
|
|
|
|
|
void setRed() => BackgroundColour = colours.Red;
|
2022-03-23 09:37:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
protected override void Dispose(bool isDisposing)
|
|
|
|
{
|
|
|
|
base.Dispose(isDisposing);
|
|
|
|
|
|
|
|
if (multiplayerClient != null)
|
|
|
|
multiplayerClient.RoomUpdated -= onRoomUpdated;
|
|
|
|
}
|
|
|
|
|
|
|
|
public override LocalisableString TooltipText
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
2022-09-01 17:53:35 +08:00
|
|
|
if (room?.ActiveCountdowns.Any(c => c is MatchStartCountdown) == true
|
|
|
|
&& multiplayerClient.IsHost
|
|
|
|
&& multiplayerClient.LocalUser?.State == MultiplayerUserState.Ready
|
|
|
|
&& !room.Settings.AutoStartEnabled)
|
|
|
|
{
|
2022-03-23 09:37:53 +08:00
|
|
|
return "Cancel countdown";
|
2022-09-01 17:53:35 +08:00
|
|
|
}
|
2022-03-23 09:37:53 +08:00
|
|
|
|
|
|
|
return base.TooltipText;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|