Adjust matchmaking naming, namespaces, xmldoc (#35123)

* Adjust matchmaking naming, namespaces, xmldoc

* Change partial filenames to use `.` instead of `_` separator
This commit is contained in:
Dean Herbert
2025-09-26 12:17:28 +09:00
committed by GitHub
parent 12e29f0bcc
commit e1ba1b45b0
77 changed files with 1244 additions and 1312 deletions

View File

@@ -1,28 +0,0 @@
// 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.
using osu.Framework.Graphics;
using osu.Game.Screens.OnlinePlay.Matchmaking.Screens.Pick;
using osu.Game.Tests.Visual.Multiplayer;
using osuTK;
namespace osu.Game.Tests.Visual.Matchmaking
{
public partial class TestSceneBeatmapPanel : MultiplayerTestScene
{
public override void SetUpSteps()
{
base.SetUpSteps();
AddStep("add beatmap panel", () =>
{
Child = new BeatmapPanel(CreateAPIBeatmap())
{
Size = new Vector2(300, 70),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
};
});
}
}
}

View File

@@ -1,68 +0,0 @@
// 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.
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Testing;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Screens.OnlinePlay.Matchmaking.Screens.Pick;
using osuTK;
namespace osu.Game.Tests.Visual.Matchmaking
{
public partial class TestSceneBeatmapSelectionOverlay : OsuTestScene
{
private BeatmapSelectionOverlay selectionOverlay = null!;
[SetUpSteps]
public void SetupSteps()
{
AddStep("add drawable", () => Child = new Container
{
Width = 100,
AutoSizeAxes = Axes.Y,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Scale = new Vector2(2),
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0.1f,
},
selectionOverlay = new BeatmapSelectionOverlay
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
}
}
});
}
[Test]
public void TestSelectionOverlay()
{
AddStep("add maarvin", () => selectionOverlay.AddUser(new APIUser
{
Id = 6411631,
Username = "Maarvin",
}, isOwnUser: true));
AddStep("add peppy", () => selectionOverlay.AddUser(new APIUser
{
Id = 2,
Username = "peppy",
}, false));
AddStep("add smogipoo", () => selectionOverlay.AddUser(new APIUser
{
Id = 1040328,
Username = "smoogipoo",
}, false));
AddStep("remove smogipoo", () => selectionOverlay.RemoveUser(1040328));
AddStep("remove peppy", () => selectionOverlay.RemoveUser(2));
AddStep("remove maarvin", () => selectionOverlay.RemoveUser(6411631));
}
}
}

View File

@@ -11,7 +11,7 @@ using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Multiplayer.MatchTypes.Matchmaking;
using osu.Game.Online.Rooms;
using osu.Game.Screens.OnlinePlay.Matchmaking.Screens.Idle;
using osu.Game.Screens.OnlinePlay.Matchmaking.Match.RoundWarmup;
using osu.Game.Tests.Visual.Multiplayer;
using osuTK;
@@ -45,7 +45,7 @@ namespace osu.Game.Tests.Visual.Matchmaking
return (user, 0);
}).ToArray();
Child = new ScreenStack(new IdleScreen())
Child = new ScreenStack(new SubScreenRoundWarmup())
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,

View File

@@ -6,20 +6,20 @@ using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Utils;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Screens.OnlinePlay.Matchmaking;
using osu.Game.Screens.OnlinePlay.Matchmaking.Queue;
using osu.Game.Users;
namespace osu.Game.Tests.Visual.Matchmaking
{
public partial class TestSceneMatchmakingCloud : OsuTestScene
{
private MatchmakingCloud cloud = null!;
private CloudVisualisation cloud = null!;
protected override void LoadComplete()
{
base.LoadComplete();
Child = cloud = new MatchmakingCloud
Child = cloud = new CloudVisualisation
{
RelativeSizeAxes = Axes.Both,
};

View File

@@ -3,7 +3,7 @@
using osu.Framework.Graphics;
using osu.Game.Online.Matchmaking;
using osu.Game.Screens.OnlinePlay.Matchmaking;
using osu.Game.Screens.OnlinePlay.Matchmaking.Queue;
using osu.Game.Tests.Visual.Multiplayer;
namespace osu.Game.Tests.Visual.Matchmaking
@@ -14,7 +14,7 @@ namespace osu.Game.Tests.Visual.Matchmaking
{
base.SetUpSteps();
AddStep("add selector", () => Child = new MatchmakingPoolSelector
AddStep("add selector", () => Child = new PoolSelector
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,

View File

@@ -7,8 +7,8 @@ using osu.Framework.Allocation;
using osu.Framework.Testing;
using osu.Framework.Utils;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Screens.OnlinePlay.Matchmaking;
using osu.Game.Screens.OnlinePlay.Matchmaking.Screens;
using osu.Game.Screens.OnlinePlay.Matchmaking.Intro;
using osu.Game.Screens.OnlinePlay.Matchmaking.Queue;
using osu.Game.Tests.Visual.Multiplayer;
using osu.Game.Users;
@@ -17,16 +17,16 @@ namespace osu.Game.Tests.Visual.Matchmaking
public partial class TestSceneMatchmakingQueueScreen : MultiplayerTestScene
{
[Cached]
private readonly MatchmakingController controller = new MatchmakingController();
private readonly QueueController controller = new QueueController();
private MatchmakingQueueScreen? queueScreen => Stack.CurrentScreen as MatchmakingQueueScreen;
private ScreenQueue? queueScreen => Stack.CurrentScreen as ScreenQueue;
[SetUpSteps]
public override void SetUpSteps()
{
base.SetUpSteps();
AddStep("load screen", () => LoadScreen(new MatchmakingIntroScreen()));
AddStep("load screen", () => LoadScreen(new IntroScreen()));
}
[Test]
@@ -44,15 +44,15 @@ namespace osu.Game.Tests.Visual.Matchmaking
}).ToArray();
});
AddStep("change state to idle", () => queueScreen!.SetState(MatchmakingQueueScreen.MatchmakingScreenState.Idle));
AddStep("change state to idle", () => queueScreen!.SetState(ScreenQueue.MatchmakingScreenState.Idle));
AddStep("change state to queueing", () => queueScreen!.SetState(MatchmakingQueueScreen.MatchmakingScreenState.Queueing));
AddStep("change state to queueing", () => queueScreen!.SetState(ScreenQueue.MatchmakingScreenState.Queueing));
AddStep("change state to found match", () => queueScreen!.SetState(MatchmakingQueueScreen.MatchmakingScreenState.PendingAccept));
AddStep("change state to found match", () => queueScreen!.SetState(ScreenQueue.MatchmakingScreenState.PendingAccept));
AddStep("change state to waiting for room", () => queueScreen!.SetState(MatchmakingQueueScreen.MatchmakingScreenState.AcceptedWaitingForRoom));
AddStep("change state to waiting for room", () => queueScreen!.SetState(ScreenQueue.MatchmakingScreenState.AcceptedWaitingForRoom));
AddStep("change state to in room", () => queueScreen!.SetState(MatchmakingQueueScreen.MatchmakingScreenState.InRoom));
AddStep("change state to in room", () => queueScreen!.SetState(ScreenQueue.MatchmakingScreenState.InRoom));
}
}
}

View File

@@ -16,7 +16,7 @@ using osu.Game.Online.Multiplayer.MatchTypes.Matchmaking;
using osu.Game.Online.Rooms;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using osu.Game.Screens.OnlinePlay.Matchmaking;
using osu.Game.Screens.OnlinePlay.Matchmaking.Match;
using osu.Game.Tests.Visual.Multiplayer;
namespace osu.Game.Tests.Visual.Matchmaking
@@ -27,7 +27,7 @@ namespace osu.Game.Tests.Visual.Matchmaking
private const int beatmap_count = 50;
private MultiplayerRoomUser[] users = null!;
private MatchmakingScreen screen = null!;
private ScreenMatchmaking screen = null!;
public override void SetUpSteps()
{
@@ -80,7 +80,7 @@ namespace osu.Game.Tests.Visual.Matchmaking
StarRating = i / 10.0
}).ToArray();
LoadScreen(screen = new MatchmakingScreen(new MultiplayerRoom(0)
LoadScreen(screen = new ScreenMatchmaking(new MultiplayerRoom(0)
{
Users = users,
Playlist = beatmaps

View File

@@ -11,7 +11,7 @@ using osu.Game.Online.Multiplayer;
using osu.Game.Online.Multiplayer.MatchTypes.Matchmaking;
using osu.Game.Online.Rooms;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.OnlinePlay.Matchmaking.Screens;
using osu.Game.Screens.OnlinePlay.Matchmaking.Match;
using osu.Game.Tests.Visual.Multiplayer;
namespace osu.Game.Tests.Visual.Matchmaking
@@ -41,7 +41,7 @@ namespace osu.Game.Tests.Visual.Matchmaking
AddStep("add carousel", () =>
{
Child = new MatchmakingScreenStack
Child = new ScreenMatchmaking.ScreenStack
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,

View File

@@ -2,18 +2,18 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics;
using osu.Game.Screens.OnlinePlay.Matchmaking.Screens.Results;
using osu.Game.Screens.OnlinePlay.Matchmaking.Match.Results;
using osu.Game.Tests.Visual.Multiplayer;
namespace osu.Game.Tests.Visual.Matchmaking
{
public partial class TestSceneRoomStatisticPanel : MultiplayerTestScene
public partial class TestScenePanelRoomAward : MultiplayerTestScene
{
public override void SetUpSteps()
{
base.SetUpSteps();
AddStep("add statistic", () => Child = new RoomStatisticPanel("Statistic description", 1)
AddStep("add statistic", () => Child = new PanelRoomAward("Statistic description", 1)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre

View File

@@ -10,7 +10,7 @@ using osu.Framework.Utils;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Rooms;
using osu.Game.Screens.OnlinePlay.Matchmaking.Screens.Pick;
using osu.Game.Screens.OnlinePlay.Matchmaking.Match.BeatmapSelect;
using osu.Game.Tests.Visual.Multiplayer;
namespace osu.Game.Tests.Visual.Matchmaking
@@ -79,9 +79,9 @@ namespace osu.Game.Tests.Visual.Matchmaking
{
var selectedItems = new List<long>();
PickScreen screen = null!;
SubScreenBeatmapSelect screen = null!;
AddStep("add screen", () => Child = new ScreenStack(screen = new PickScreen()));
AddStep("add screen", () => Child = new ScreenStack(screen = new SubScreenBeatmapSelect()));
AddStep("select maps", () =>
{

View File

@@ -8,7 +8,7 @@ using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Multiplayer.MatchTypes.Matchmaking;
using osu.Game.Online.Rooms;
using osu.Game.Screens.OnlinePlay.Matchmaking;
using osu.Game.Screens.OnlinePlay.Matchmaking.Match;
using osu.Game.Tests.Visual.Multiplayer;
using osu.Game.Users;
@@ -16,7 +16,7 @@ namespace osu.Game.Tests.Visual.Matchmaking
{
public partial class TestScenePlayerPanel : MultiplayerTestScene
{
private PlayerPanel panel = null!;
private MatchmakingUserPanel panel = null!;
public override void SetUpSteps()
{
@@ -25,7 +25,7 @@ namespace osu.Game.Tests.Visual.Matchmaking
AddStep("join room", () => JoinRoom(CreateDefaultRoom(MatchType.Matchmaking)));
WaitForJoined();
AddStep("add panel", () => Child = panel = new PlayerPanel(new MultiplayerRoomUser(1)
AddStep("add panel", () => Child = panel = new MatchmakingUserPanel(new MultiplayerRoomUser(1)
{
User = new APIUser
{

View File

@@ -10,7 +10,7 @@ using osu.Game.Online.Multiplayer;
using osu.Game.Online.Multiplayer.MatchTypes.Matchmaking;
using osu.Game.Online.Rooms;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.OnlinePlay.Matchmaking.Screens.Results;
using osu.Game.Screens.OnlinePlay.Matchmaking.Match.Results;
using osu.Game.Tests.Visual.Multiplayer;
using osuTK;
@@ -29,7 +29,7 @@ namespace osu.Game.Tests.Visual.Matchmaking
AddStep("add results screen", () =>
{
Child = new ScreenStack(new ResultsScreen())
Child = new ScreenStack(new SubScreenResults())
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,

View File

@@ -13,7 +13,7 @@ using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Rooms;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using osu.Game.Screens.OnlinePlay.Matchmaking.Screens.RoundResults;
using osu.Game.Screens.OnlinePlay.Matchmaking.Match.RoundResults;
using osu.Game.Tests.Visual.Multiplayer;
using osuTK;
@@ -32,7 +32,7 @@ namespace osu.Game.Tests.Visual.Matchmaking
AddStep("load screen", () =>
{
Child = new ScreenStack(new RoundResultsScreen())
Child = new ScreenStack(new SubScreenRoundResults())
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,

View File

@@ -11,17 +11,17 @@ using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.Rooms;
using osu.Game.Screens.OnlinePlay.Matchmaking.Screens.Pick;
using osu.Game.Screens.OnlinePlay.Matchmaking.Match.BeatmapSelect;
using osu.Game.Tests.Visual.OnlinePlay;
using osuTK;
namespace osu.Game.Tests.Visual.Matchmaking
{
public partial class TestSceneBeatmapSelectionGrid : OnlinePlayTestScene
public partial class TestSceneSelectionGrid : OnlinePlayTestScene
{
private MultiplayerPlaylistItem[] items = null!;
private BeatmapSelectionGrid grid = null!;
private SelectionGrid grid = null!;
[Resolved]
private BeatmapManager beatmapManager { get; set; } = null!;
@@ -58,7 +58,7 @@ namespace osu.Game.Tests.Visual.Matchmaking
{
base.SetUpSteps();
AddStep("add grid", () => Child = grid = new BeatmapSelectionGrid
AddStep("add grid", () => Child = grid = new SelectionGrid
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
@@ -154,7 +154,7 @@ namespace osu.Game.Tests.Visual.Matchmaking
var (candidateItems, _) = pickRandomItems(count);
grid.TransferCandidatePanelsToRollContainer(candidateItems);
grid.Delay(BeatmapSelectionGrid.ARRANGE_DELAY)
grid.Delay(SelectionGrid.ARRANGE_DELAY)
.Schedule(() => grid.ArrangeItemsForRollAnimation());
});
@@ -162,7 +162,7 @@ namespace osu.Game.Tests.Visual.Matchmaking
AddStep("display roll order", () =>
{
var panels = grid.ChildrenOfType<BeatmapSelectionPanel>().ToArray();
var panels = grid.ChildrenOfType<SelectionPanel>().ToArray();
for (int i = 0; i < panels.Length; i++)
{

View File

@@ -7,12 +7,12 @@ using osu.Framework.Graphics;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Rooms;
using osu.Game.Overlays;
using osu.Game.Screens.OnlinePlay.Matchmaking.Screens.Pick;
using osu.Game.Screens.OnlinePlay.Matchmaking.Match.BeatmapSelect;
using osu.Game.Tests.Visual.Multiplayer;
namespace osu.Game.Tests.Visual.Matchmaking
{
public partial class TestSceneBeatmapSelectionPanel : MultiplayerTestScene
public partial class TestSceneSelectionPanel : MultiplayerTestScene
{
[Cached]
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple);
@@ -20,9 +20,9 @@ namespace osu.Game.Tests.Visual.Matchmaking
[Test]
public void TestBeatmapPanel()
{
BeatmapSelectionPanel? panel = null;
SelectionPanel? panel = null;
AddStep("add panel", () => Child = panel = new BeatmapSelectionPanel(new MultiplayerPlaylistItem())
AddStep("add panel", () => Child = panel = new SelectionPanel(new MultiplayerPlaylistItem())
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,

View File

@@ -8,7 +8,7 @@ using osu.Framework.Graphics;
using osu.Game.Online.Multiplayer.MatchTypes.Matchmaking;
using osu.Game.Online.Rooms;
using osu.Game.Overlays;
using osu.Game.Screens.OnlinePlay.Matchmaking;
using osu.Game.Screens.OnlinePlay.Matchmaking.Match;
using osu.Game.Tests.Visual.Multiplayer;
namespace osu.Game.Tests.Visual.Matchmaking

View File

@@ -9,12 +9,12 @@ using osu.Game.Online.Matchmaking;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Multiplayer.MatchTypes.Matchmaking;
using osu.Game.Online.Rooms;
using osu.Game.Screens.OnlinePlay.Matchmaking;
using osu.Game.Screens.OnlinePlay.Matchmaking.Match;
using osu.Game.Tests.Visual.Multiplayer;
namespace osu.Game.Tests.Visual.Matchmaking
{
public partial class TestSceneStageBubble : MultiplayerTestScene
public partial class TestSceneStageSegment : MultiplayerTestScene
{
public override void SetUpSteps()
{
@@ -23,7 +23,7 @@ namespace osu.Game.Tests.Visual.Matchmaking
AddStep("join room", () => JoinRoom(CreateDefaultRoom(MatchType.Matchmaking)));
WaitForJoined();
AddStep("add bubble", () => Child = new StageBubble(null, MatchmakingStage.RoundWarmupTime, "Next Round")
AddStep("add bubble", () => Child = new StageDisplay.StageSegment(null, MatchmakingStage.RoundWarmupTime, "Next Round")
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,

View File

@@ -7,12 +7,12 @@ using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Game.Online.Multiplayer.MatchTypes.Matchmaking;
using osu.Game.Online.Rooms;
using osu.Game.Screens.OnlinePlay.Matchmaking;
using osu.Game.Screens.OnlinePlay.Matchmaking.Match;
using osu.Game.Tests.Visual.Multiplayer;
namespace osu.Game.Tests.Visual.Matchmaking
{
public partial class TestSceneStageText : MultiplayerTestScene
public partial class TestSceneStatusText : MultiplayerTestScene
{
public override void SetUpSteps()
{
@@ -21,7 +21,7 @@ namespace osu.Game.Tests.Visual.Matchmaking
AddStep("join room", () => JoinRoom(CreateDefaultRoom(MatchType.Matchmaking)));
WaitForJoined();
AddStep("create display", () => Child = new StageText
AddStep("create display", () => Child = new StageDisplay.StatusText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre

View File

@@ -11,15 +11,15 @@ using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Multiplayer.MatchTypes.Matchmaking;
using osu.Game.Online.Rooms;
using osu.Game.Screens.OnlinePlay.Matchmaking;
using osu.Game.Screens.OnlinePlay.Matchmaking.Match;
using osu.Game.Tests.Visual.Multiplayer;
using osuTK;
namespace osu.Game.Tests.Visual.Matchmaking
{
public partial class TestScenePlayerPanelList : MultiplayerTestScene
public partial class TestSceneUserPanelOverlay : MultiplayerTestScene
{
private PlayerPanelList list = null!;
private UserPanelOverlay list = null!;
public override void SetUpSteps()
{
@@ -34,7 +34,7 @@ namespace osu.Game.Tests.Visual.Matchmaking
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Size = new Vector2(0.8f),
Child = list = new PlayerPanelList()
Child = list = new UserPanelOverlay()
});
}
@@ -55,15 +55,15 @@ namespace osu.Game.Tests.Visual.Matchmaking
}
});
AddStep("change to split mode", () => list.DisplayStyle = PlayerPanelList.PanelDisplayStyle.Split);
AddStep("change to grid mode", () => list.DisplayStyle = PlayerPanelList.PanelDisplayStyle.Grid);
AddStep("change to hidden mode", () => list.DisplayStyle = PlayerPanelList.PanelDisplayStyle.Hidden);
AddStep("change to split mode", () => list.DisplayStyle = PanelDisplayStyle.Split);
AddStep("change to grid mode", () => list.DisplayStyle = PanelDisplayStyle.Grid);
AddStep("change to hidden mode", () => list.DisplayStyle = PanelDisplayStyle.Hidden);
}
[Test]
public void AddPanelsGrid()
{
AddStep("change to grid mode", () => list.DisplayStyle = PlayerPanelList.PanelDisplayStyle.Grid);
AddStep("change to grid mode", () => list.DisplayStyle = PanelDisplayStyle.Grid);
int userId = 0;
@@ -84,7 +84,7 @@ namespace osu.Game.Tests.Visual.Matchmaking
[Test]
public void AddPanelsSplit()
{
AddStep("change to split mode", () => list.DisplayStyle = PlayerPanelList.PanelDisplayStyle.Split);
AddStep("change to split mode", () => list.DisplayStyle = PanelDisplayStyle.Split);
int userId = 0;

View File

@@ -65,7 +65,7 @@ using osu.Game.Screens.Edit;
using osu.Game.Screens.Footer;
using osu.Game.Screens.Menu;
using osu.Game.Screens.OnlinePlay.DailyChallenge;
using osu.Game.Screens.OnlinePlay.Matchmaking;
using osu.Game.Screens.OnlinePlay.Matchmaking.Queue;
using osu.Game.Screens.OnlinePlay.Multiplayer;
using osu.Game.Screens.OnlinePlay.Playlists;
using osu.Game.Screens.Play;
@@ -80,6 +80,7 @@ using osu.Game.Utils;
using osuTK;
using osuTK.Graphics;
using Sentry;
using IntroScreen = osu.Game.Screens.Menu.IntroScreen;
using MatchType = osu.Game.Online.Rooms.MatchType;
namespace osu.Game
@@ -1271,7 +1272,7 @@ namespace osu.Game
loadComponentSingleFile(new BackgroundDataStoreProcessor(), Add);
loadComponentSingleFile<BeatmapStore>(detachedBeatmapStore = new RealmDetachedBeatmapStore(), Add, true);
loadComponentSingleFile(new MatchmakingController(), Add, true);
loadComponentSingleFile(new QueueController(), Add, true);
Add(externalLinkOpener = new ExternalLinkOpener());
Add(new MusicKeyBindingHandler());

View File

@@ -37,7 +37,6 @@ using osu.Game.Rulesets;
using osu.Game.Screens.Backgrounds;
using osu.Game.Screens.Edit;
using osu.Game.Screens.OnlinePlay.DailyChallenge;
using osu.Game.Screens.OnlinePlay.Matchmaking.Screens;
using osu.Game.Screens.OnlinePlay.Multiplayer;
using osu.Game.Screens.OnlinePlay.Playlists;
using osu.Game.Screens.SelectV2;
@@ -483,7 +482,7 @@ namespace osu.Game.Screens.Menu
private void loadSongSelect() => this.Push(new SoloSongSelect());
private void joinOrLeaveMatchmakingQueue() => this.Push(new MatchmakingIntroScreen());
private void joinOrLeaveMatchmakingQueue() => this.Push(new OnlinePlay.Matchmaking.Intro.IntroScreen());
private partial class MobileDisclaimerDialog : PopupDialog
{

View File

@@ -14,10 +14,14 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Overlays;
using osu.Game.Screens.OnlinePlay.Match;
using osu.Game.Screens.OnlinePlay.Matchmaking.Queue;
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Screens
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Intro
{
public partial class MatchmakingIntroScreen : OsuScreen
/// <summary>
/// A brief intro animation that introduces matchmaking to the user.
/// </summary>
public partial class IntroScreen : OsuScreen
{
public override bool DisallowExternalBeatmapRulesetChanges => false;
@@ -51,7 +55,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Screens
protected override BackgroundScreen CreateBackground() => new MatchmakingIntroBackgroundScreen(colourProvider);
public MatchmakingIntroScreen()
public IntroScreen()
{
ValidForResume = false;
}
@@ -191,7 +195,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Screens
Schedule(() =>
{
if (this.IsCurrentScreen())
this.Push(new MatchmakingQueueScreen());
this.Push(new ScreenQueue());
});
}
}

View File

@@ -20,9 +20,9 @@ using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Rooms;
using osuTK;
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Screens.Pick
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.BeatmapSelect
{
public partial class BeatmapSelectionGrid : CompositeDrawable
public partial class SelectionGrid : CompositeDrawable
{
public const double ARRANGE_DELAY = 200;
@@ -37,10 +37,10 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Screens.Pick
[Resolved]
private IAPIProvider api { get; set; } = null!;
private readonly Dictionary<long, BeatmapSelectionPanel> panelLookup = new Dictionary<long, BeatmapSelectionPanel>();
private readonly Dictionary<long, SelectionPanel> panelLookup = new Dictionary<long, SelectionPanel>();
private readonly PanelGridContainer panelGridContainer;
private readonly Container<BeatmapSelectionPanel> rollContainer;
private readonly Container<SelectionPanel> rollContainer;
private readonly OsuScrollContainer scroll;
private bool allowSelection = true;
@@ -51,7 +51,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Screens.Pick
private Sample? swooshSample;
private double? lastSamplePlayback;
public BeatmapSelectionGrid()
public SelectionGrid()
{
InternalChildren = new Drawable[]
{
@@ -67,7 +67,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Screens.Pick
Spacing = new Vector2(panel_spacing)
},
},
rollContainer = new Container<BeatmapSelectionPanel>
rollContainer = new Container<SelectionPanel>
{
RelativeSizeAxes = Axes.Both,
Masking = true,
@@ -108,7 +108,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Screens.Pick
public void AddItem(MultiplayerPlaylistItem item)
{
var panel = panelLookup[item.ID] = new BeatmapSelectionPanel(item)
var panel = panelLookup[item.ID] = new SelectionPanel(item)
{
Size = new Vector2(300, 70),
AllowSelection = allowSelection,
@@ -176,7 +176,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Screens.Pick
var rng = new Random();
var remainingPanels = new List<BeatmapSelectionPanel>();
var remainingPanels = new List<SelectionPanel>();
foreach (var panel in panelGridContainer.Children.ToArray())
{
@@ -216,7 +216,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Screens.Pick
{
var panel = rollContainer.Children[i];
var position = positions[i] * (BeatmapPanel.SIZE + new Vector2(panel_spacing));
var position = positions[i] * (SelectionPanel.SIZE + new Vector2(panel_spacing));
panel.MoveTo(position, duration + stagger * i, new SplitEasingFunction(Easing.InCubic, Easing.OutExpo, 0.3f));
@@ -285,7 +285,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Screens.Pick
while ((numSteps - 1) % rollContainer.Children.Count != finalItemIndex)
numSteps++;
BeatmapSelectionPanel? lastPanel = null;
SelectionPanel? lastPanel = null;
for (int i = 0; i < numSteps; i++)
{
@@ -346,7 +346,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Screens.Pick
PresentRolledBeatmap(finalItem);
}
private partial class PanelGridContainer : FillFlowContainer<BeatmapSelectionPanel>
private partial class PanelGridContainer : FillFlowContainer<SelectionPanel>
{
public bool LayoutDisabled;

View File

@@ -0,0 +1,502 @@
// 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.
using System;
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Extensions;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events;
using osu.Framework.Localisation;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Database;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Rooms;
using osu.Game.Overlays;
using osuTK;
using osuTK.Input;
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.BeatmapSelect
{
public partial class SelectionPanel : Container
{
public static readonly Vector2 SIZE = new Vector2(300, 70);
private const float corner_radius = 6;
private const float border_width = 3;
public readonly MultiplayerPlaylistItem Item;
private readonly Container scaleContainer;
private readonly BeatmapPanel beatmapPanel;
private readonly AvatarOverlay selectionOverlay;
private readonly Container border;
private readonly Box flash;
public bool AllowSelection;
public Action<MultiplayerPlaylistItem>? Action;
[Resolved]
private BeatmapLookupCache beatmapLookupCache { get; set; } = null!;
public override bool PropagatePositionalInputSubTree => AllowSelection;
public SelectionPanel(MultiplayerPlaylistItem item)
{
Item = item;
Size = SIZE;
InternalChildren = new Drawable[]
{
scaleContainer = new Container
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding(-border_width),
Child = border = new Container
{
RelativeSizeAxes = Axes.Both,
Masking = true,
CornerRadius = corner_radius + border_width,
Alpha = 0,
Child = new Box { RelativeSizeAxes = Axes.Both },
}
},
beatmapPanel = new BeatmapPanel
{
RelativeSizeAxes = Axes.Both,
OverlayLayer =
{
Children = new[]
{
flash = new Box
{
Blending = BlendingParameters.Additive,
RelativeSizeAxes = Axes.Both,
Alpha = 0,
},
}
}
},
selectionOverlay = new AvatarOverlay
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding { Horizontal = 10 },
Origin = Anchor.CentreLeft,
},
}
},
new HoverClickSounds(),
};
}
protected override void LoadComplete()
{
base.LoadComplete();
beatmapLookupCache.GetBeatmapAsync(Item.BeatmapID).ContinueWith(b => Schedule(() =>
{
var beatmap = b.GetResultSafely()!;
beatmap.StarRating = Item.StarRating;
beatmapPanel.Beatmap = beatmap;
}));
}
public bool AddUser(APIUser user, bool isOwnUser = false) => selectionOverlay.AddUser(user, isOwnUser);
public bool RemoveUser(int userId) => selectionOverlay.RemoveUser(userId);
public bool RemoveUser(APIUser user) => RemoveUser(user.Id);
protected override bool OnHover(HoverEvent e)
{
flash.FadeTo(0.2f, 50)
.Then()
.FadeTo(0.1f, 300);
return true;
}
protected override void OnHoverLost(HoverLostEvent e)
{
base.OnHoverLost(e);
flash.FadeOut(200);
}
protected override bool OnMouseDown(MouseDownEvent e)
{
if (e.Button == MouseButton.Left)
{
scaleContainer.ScaleTo(0.95f, 400, Easing.OutExpo);
return true;
}
return base.OnMouseDown(e);
}
protected override void OnMouseUp(MouseUpEvent e)
{
base.OnMouseUp(e);
if (e.Button == MouseButton.Left)
{
scaleContainer.ScaleTo(1f, 500, Easing.OutElasticHalf);
}
}
protected override bool OnClick(ClickEvent e)
{
Action?.Invoke(Item);
flash.FadeTo(0.5f, 50)
.Then()
.FadeTo(0.1f, 400);
return true;
}
public void ShowBorder() => border.Show();
public void HideBorder() => border.Hide();
public void FadeInAndEnterFromBelow(double duration = 500, double delay = 0, float distance = 200)
{
scaleContainer
.FadeOut()
.MoveToY(distance)
.Delay(delay)
.FadeIn(duration / 2)
.MoveToY(0, duration, Easing.OutExpo);
}
public void PopOutAndExpire(double duration = 400, double delay = 0, Easing easing = Easing.InCubic)
{
AllowSelection = false;
scaleContainer.Delay(delay)
.ScaleTo(0, duration, easing)
.FadeOut(duration);
this.Delay(delay + duration).FadeOut().Expire();
}
// TODO: combine following two classes with above implementation for simplicity?
private partial class BeatmapPanel : CompositeDrawable
{
public readonly Container OverlayLayer = new Container { RelativeSizeAxes = Axes.Both };
public APIBeatmap? Beatmap
{
get => beatmap;
set
{
if (beatmap?.OnlineID == value?.OnlineID)
return;
beatmap = value;
if (IsLoaded)
updateContent();
}
}
private APIBeatmap? beatmap;
private Container content = null!;
private UpdateableOnlineBeatmapSetCover cover = null!;
public BeatmapPanel(APIBeatmap? beatmap = null)
{
this.beatmap = beatmap;
}
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
{
Masking = true;
CornerRadius = 6;
InternalChildren = new Drawable[]
{
cover = new UpdateableOnlineBeatmapSetCover(BeatmapSetCoverType.Card, timeBeforeLoad: 0, timeBeforeUnload: 10000)
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = ColourInfo.GradientHorizontal(
colourProvider.Background4.Opacity(0.7f),
colourProvider.Background4.Opacity(0.4f)
)
},
content = new Container
{
RelativeSizeAxes = Axes.Both,
},
OverlayLayer,
};
}
protected override void LoadComplete()
{
base.LoadComplete();
updateContent();
FinishTransforms(true);
}
private void updateContent()
{
foreach (var child in content.Children)
child.FadeOut(300).Expire();
cover.OnlineInfo = beatmap?.BeatmapSet;
if (beatmap != null)
{
var panelContent = new BeatmapPanelContent(beatmap)
{
RelativeSizeAxes = Axes.Both,
};
content.Add(panelContent);
panelContent.FadeInFromZero(300);
}
}
private partial class BeatmapPanelContent : CompositeDrawable
{
private readonly APIBeatmap beatmap;
public BeatmapPanelContent(APIBeatmap beatmap)
{
this.beatmap = beatmap;
}
[BackgroundDependencyLoader]
private void load()
{
InternalChild = new FillFlowContainer
{
Direction = FillDirection.Vertical,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Padding = new MarginPadding { Horizontal = 12 },
Children = new Drawable[]
{
new TruncatingSpriteText
{
Text = new RomanisableString(beatmap.Metadata.TitleUnicode, beatmap.Metadata.TitleUnicode),
Font = OsuFont.Default.With(size: 19, weight: FontWeight.SemiBold),
RelativeSizeAxes = Axes.X,
},
new TextFlowContainer(s =>
{
s.Font = OsuFont.GetFont(size: 16, weight: FontWeight.SemiBold);
}).With(d =>
{
d.RelativeSizeAxes = Axes.X;
d.AutoSizeAxes = Axes.Y;
d.AddText("by ");
d.AddText(new RomanisableString(beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist));
}),
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Horizontal,
Margin = new MarginPadding { Top = 6 },
Spacing = new Vector2(4),
Children = new Drawable[]
{
new StarRatingDisplay(new StarDifficulty(beatmap.StarRating, 0), StarRatingDisplaySize.Small)
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
},
new TruncatingSpriteText
{
Text = beatmap.DifficultyName,
Font = OsuFont.Default.With(size: 16, weight: FontWeight.SemiBold),
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
},
}
},
},
};
}
}
}
private partial class AvatarOverlay : CompositeDrawable
{
private readonly Dictionary<int, SelectionAvatar> avatars = new Dictionary<int, SelectionAvatar>();
private readonly Container<SelectionAvatar> avatarContainer;
private Sample? userAddedSample;
private double? lastSamplePlayback;
public new Axes AutoSizeAxes
{
get => base.AutoSizeAxes;
set => base.AutoSizeAxes = value;
}
public new MarginPadding Padding
{
get => base.Padding;
set => base.Padding = value;
}
public AvatarOverlay()
{
InternalChild = avatarContainer = new Container<SelectionAvatar>();
}
protected override void LoadComplete()
{
base.LoadComplete();
avatarContainer.AutoSizeAxes = AutoSizeAxes;
avatarContainer.RelativeSizeAxes = RelativeSizeAxes;
}
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
userAddedSample = audio.Samples.Get(@"Multiplayer/player-ready");
}
public bool AddUser(APIUser user, bool isOwnUser)
{
if (avatars.ContainsKey(user.Id))
return false;
var avatar = new SelectionAvatar(user, isOwnUser)
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
};
avatarContainer.Add(avatars[user.Id] = avatar);
if (lastSamplePlayback == null || Time.Current - lastSamplePlayback > OsuGameBase.SAMPLE_DEBOUNCE_TIME)
{
userAddedSample?.Play();
lastSamplePlayback = Time.Current;
}
updateLayout();
avatar.FinishTransforms();
return true;
}
public bool RemoveUser(int id)
{
if (!avatars.Remove(id, out var avatar))
return false;
avatar.PopOutAndExpire();
avatarContainer.ChangeChildDepth(avatar, float.MaxValue);
updateLayout();
return true;
}
private void updateLayout()
{
const double stagger = 30;
const float spacing = 4;
double delay = 0;
float x = 0;
for (int i = avatarContainer.Count - 1; i >= 0; i--)
{
var avatar = avatarContainer[i];
if (avatar.Expired)
continue;
avatar.Delay(delay).MoveToX(x, 500, Easing.OutElasticQuarter);
x -= avatar.LayoutSize.X + spacing;
delay += stagger;
}
}
public partial class SelectionAvatar : CompositeDrawable
{
public bool Expired { get; private set; }
private readonly Container content;
public SelectionAvatar(APIUser user, bool isOwnUser)
{
Size = new Vector2(30);
InternalChildren = new Drawable[]
{
content = new Container
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Child = new MatchmakingAvatar(user, isOwnUser)
}
};
}
protected override void LoadComplete()
{
base.LoadComplete();
content.ScaleTo(0)
.ScaleTo(1, 500, Easing.OutElasticHalf)
.FadeIn(200);
}
public void PopOutAndExpire()
{
content.ScaleTo(0, 400, Easing.OutExpo);
this.FadeOut(100).Expire();
Expired = true;
}
}
}
}
}

View File

@@ -9,19 +9,19 @@ using osu.Framework.Graphics.Containers;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Rooms;
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Screens.Pick
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.BeatmapSelect
{
public partial class PickScreen : MatchmakingSubScreen
public partial class SubScreenBeatmapSelect : MatchmakingSubScreen
{
public override PlayerPanelList.PanelDisplayStyle PlayersDisplayStyle => PlayerPanelList.PanelDisplayStyle.Split;
public override PanelDisplayStyle PlayersDisplayStyle => PanelDisplayStyle.Split;
public override Drawable PlayersDisplayArea { get; }
private readonly BeatmapSelectionGrid selectionGrid;
private readonly SelectionGrid selectionGrid;
[Resolved]
private MultiplayerClient client { get; set; } = null!;
public PickScreen()
public SubScreenBeatmapSelect()
{
InternalChildren = new Drawable[]
{
@@ -29,7 +29,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Screens.Pick
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Horizontal = 200 },
Child = selectionGrid = new BeatmapSelectionGrid
Child = selectionGrid = new SelectionGrid
{
RelativeSizeAxes = Axes.Both,
},

View File

@@ -8,11 +8,11 @@ using osu.Game.Online.Rooms;
using osu.Game.Scoring;
using osu.Game.Screens.OnlinePlay.Multiplayer;
namespace osu.Game.Screens.OnlinePlay.Matchmaking
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.Gameplay
{
public partial class MatchmakingPlayer : MultiplayerPlayer
public partial class ScreenGameplay : MultiplayerPlayer
{
public MatchmakingPlayer(Room room, PlaylistItem playlistItem, MultiplayerRoomUser[] users)
public ScreenGameplay(Room room, PlaylistItem playlistItem, MultiplayerRoomUser[] users)
: base(room, playlistItem, users)
{
}

View File

@@ -11,8 +11,12 @@ using osu.Game.Users.Drawables;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Screens.OnlinePlay.Matchmaking
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match
{
/// <summary>
/// A circular player avatar used in matchmaking displays.
/// Is part of a <see cref="MatchmakingUserPanel"/> but can also be used in isolation for a more ambient/decorative user display.
/// </summary>
public partial class MatchmakingAvatar : CompositeDrawable
{
public static readonly Vector2 SIZE = new Vector2(30);

View File

@@ -4,11 +4,11 @@
using osu.Framework.Graphics;
using osu.Framework.Screens;
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Screens
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match
{
public abstract partial class MatchmakingSubScreen : Screen
{
public abstract PlayerPanelList.PanelDisplayStyle PlayersDisplayStyle { get; }
public abstract PanelDisplayStyle PlayersDisplayStyle { get; }
public abstract Drawable? PlayersDisplayArea { get; }
protected MatchmakingSubScreen()

View File

@@ -14,9 +14,13 @@ using osu.Game.Online.Multiplayer.MatchTypes.Matchmaking;
using osu.Game.Users;
using osuTK;
namespace osu.Game.Screens.OnlinePlay.Matchmaking
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match
{
public partial class PlayerPanel : UserPanel
/// <summary>
/// A panel used throughout matchmaking to represent a user, including local information like their
/// rank and high level statistics in the matchmaking system.
/// </summary>
public partial class MatchmakingUserPanel : UserPanel
{
public static readonly Vector2 SIZE_HORIZONTAL = new Vector2(250, 100);
public static readonly Vector2 SIZE_VERTICAL = new Vector2(150, 200);
@@ -51,7 +55,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking
private bool horizontal;
public PlayerPanel(MultiplayerRoomUser user)
public MatchmakingUserPanel(MultiplayerRoomUser user)
: base(user.User!)
{
RoomUser = user;

View File

@@ -11,16 +11,16 @@ using osu.Game.Graphics.Sprites;
using osu.Game.Online.API.Requests.Responses;
using osuTK.Graphics;
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Screens.Results
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.Results
{
public partial class RoomStatisticPanel : CompositeDrawable
public partial class PanelRoomAward : CompositeDrawable
{
private readonly Color4 backgroundColour = Color4.SaddleBrown;
private readonly string text;
private readonly int userId;
public RoomStatisticPanel(string text, int userId)
public PanelRoomAward(string text, int userId)
{
this.text = text;
this.userId = userId;

View File

@@ -8,15 +8,15 @@ using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.Sprites;
using osuTK.Graphics;
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Screens.Results
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.Results
{
public partial class UserStatisticPanel : CompositeDrawable
public partial class PanelUserStatistic : CompositeDrawable
{
private readonly Color4 backgroundColour = Color4.SaddleBrown;
private readonly string text;
public UserStatisticPanel(string text)
public PanelUserStatistic(string text)
{
this.text = text;

View File

@@ -14,23 +14,26 @@ using osu.Game.Rulesets.Scoring;
using osu.Game.Utils;
using osuTK;
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Screens.Results
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.Results
{
public partial class ResultsScreen : MatchmakingSubScreen
/// <summary>
/// Final room results, during <see cref="MatchmakingStage.Ended"/>
/// </summary>
public partial class SubScreenResults : MatchmakingSubScreen
{
private const float grid_spacing = 5;
public override PlayerPanelList.PanelDisplayStyle PlayersDisplayStyle => PlayerPanelList.PanelDisplayStyle.Grid;
public override PanelDisplayStyle PlayersDisplayStyle => PanelDisplayStyle.Grid;
public override Drawable PlayersDisplayArea { get; }
[Resolved]
private MultiplayerClient client { get; set; } = null!;
private readonly OsuSpriteText placementText;
private readonly FillFlowContainer<UserStatisticPanel> userStatistics;
private readonly FillFlowContainer<RoomStatisticPanel> roomStatistics;
private readonly FillFlowContainer<PanelUserStatistic> userStatistics;
private readonly FillFlowContainer<PanelRoomAward> roomStatistics;
public ResultsScreen()
public SubScreenResults()
{
InternalChild = new GridContainer
{
@@ -103,7 +106,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Screens.Results
Text = "Breakdown",
Font = OsuFont.Default.With(size: 12)
},
userStatistics = new FillFlowContainer<UserStatisticPanel>
userStatistics = new FillFlowContainer<PanelUserStatistic>
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
@@ -139,7 +142,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Screens.Results
Text = "Statistics",
Font = OsuFont.Default.With(size: 12)
},
roomStatistics = new FillFlowContainer<RoomStatisticPanel>
roomStatistics = new FillFlowContainer<PanelRoomAward>
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
@@ -196,7 +199,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Screens.Results
void addStatistic(string text)
{
userStatistics.Add(new UserStatisticPanel(text)
userStatistics.Add(new PanelUserStatistic(text)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre
@@ -321,7 +324,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Screens.Results
void addStatistic(int userId, string text)
{
roomStatistics.Add(new RoomStatisticPanel(text, userId)
roomStatistics.Add(new PanelRoomAward(text, userId)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre

View File

@@ -18,18 +18,22 @@ using osu.Game.Models;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Multiplayer.MatchTypes.Matchmaking;
using osu.Game.Online.Rooms;
using osu.Game.Rulesets;
using osu.Game.Scoring;
using osu.Game.Screens.Ranking;
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Screens.RoundResults
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.RoundResults
{
public partial class RoundResultsScreen : MatchmakingSubScreen
/// <summary>
/// Per-round results, during <see cref="MatchmakingStage.ResultsDisplaying"/>
/// </summary>
public partial class SubScreenRoundResults : MatchmakingSubScreen
{
private const int panel_spacing = 5;
public override PlayerPanelList.PanelDisplayStyle PlayersDisplayStyle => PlayerPanelList.PanelDisplayStyle.Hidden;
public override PanelDisplayStyle PlayersDisplayStyle => PanelDisplayStyle.Hidden;
public override Drawable? PlayersDisplayArea => null;
[Resolved]
@@ -153,6 +157,32 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Screens.RoundResults
}
});
private partial class RoundResultsScorePanel : CompositeDrawable
{
public RoundResultsScorePanel(ScoreInfo score)
{
AutoSizeAxes = Axes.Both;
InternalChild = new InstantSizingScorePanel(score);
}
public override bool PropagateNonPositionalInputSubTree => false;
public override bool PropagatePositionalInputSubTree => false;
private partial class InstantSizingScorePanel : ScorePanel
{
public InstantSizingScorePanel(ScoreInfo score, bool isNewLocalScore = false)
: base(score, isNewLocalScore)
{
}
protected override void LoadComplete()
{
base.LoadComplete();
FinishTransforms(true);
}
}
}
private partial class AutoScrollContainer : UserTrackingScrollContainer
{
private const float initial_offset = -0.5f;

View File

@@ -3,12 +3,16 @@
using osu.Framework.Graphics;
using osu.Framework.Screens;
using osu.Game.Online.Multiplayer.MatchTypes.Matchmaking;
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Screens.Idle
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.RoundWarmup
{
public partial class IdleScreen : MatchmakingSubScreen
/// <summary>
/// Shown during <see cref="MatchmakingStage.RoundWarmupTime"/>
/// </summary>
public partial class SubScreenRoundWarmup : MatchmakingSubScreen
{
public override PlayerPanelList.PanelDisplayStyle PlayersDisplayStyle => PlayerPanelList.PanelDisplayStyle.Grid;
public override PanelDisplayStyle PlayersDisplayStyle => PanelDisplayStyle.Grid;
public override Drawable PlayersDisplayArea => this;
public override void OnEntering(ScreenTransitionEvent e)

View File

@@ -0,0 +1,133 @@
// 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.
using System.Diagnostics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Screens;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Multiplayer.MatchTypes.Matchmaking;
using osu.Game.Screens.OnlinePlay.Matchmaking.Match.BeatmapSelect;
using osu.Game.Screens.OnlinePlay.Matchmaking.Match.Results;
using osu.Game.Screens.OnlinePlay.Matchmaking.Match.RoundResults;
using osu.Game.Screens.OnlinePlay.Matchmaking.Match.RoundWarmup;
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match
{
public partial class ScreenMatchmaking
{
public partial class ScreenStack : CompositeDrawable
{
[Resolved]
private MultiplayerClient client { get; set; } = null!;
private Framework.Screens.ScreenStack screenStack = null!;
private UserPanelOverlay playersList = null!;
[BackgroundDependencyLoader]
private void load()
{
RelativeSizeAxes = Axes.Both;
InternalChildren = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding(10)
{
Bottom = StageDisplay.HEIGHT,
},
Children = new Drawable[]
{
screenStack = new Framework.Screens.ScreenStack(),
}
},
playersList = new UserPanelOverlay
{
DisplayArea = this
},
new StageDisplay
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X
}
};
}
protected override void LoadComplete()
{
base.LoadComplete();
screenStack.ScreenPushed += onScreenPushed;
screenStack.ScreenExited += onScreenExited;
screenStack.Push(new SubScreenRoundWarmup());
client.MatchRoomStateChanged += onMatchRoomStateChanged;
onMatchRoomStateChanged(client.Room!.MatchState);
}
private void onScreenPushed(IScreen lastScreen, IScreen newScreen)
{
if (newScreen is not MatchmakingSubScreen matchmakingSubScreen)
return;
playersList.DisplayStyle = matchmakingSubScreen.PlayersDisplayStyle;
playersList.DisplayArea = matchmakingSubScreen.PlayersDisplayArea;
}
private void onScreenExited(IScreen lastScreen, IScreen newScreen)
{
if (newScreen is not MatchmakingSubScreen matchmakingSubScreen)
return;
playersList.DisplayStyle = matchmakingSubScreen.PlayersDisplayStyle;
playersList.DisplayArea = matchmakingSubScreen.PlayersDisplayArea;
}
private void onMatchRoomStateChanged(MatchRoomState? state) => Scheduler.Add(() =>
{
if (state is not MatchmakingRoomState matchmakingState)
return;
switch (matchmakingState.Stage)
{
case MatchmakingStage.WaitingForClientsJoin:
case MatchmakingStage.RoundWarmupTime:
while (screenStack.CurrentScreen is not SubScreenRoundWarmup)
screenStack.Exit();
break;
case MatchmakingStage.UserBeatmapSelect:
screenStack.Push(new SubScreenBeatmapSelect());
break;
case MatchmakingStage.ServerBeatmapFinalised:
Debug.Assert(screenStack.CurrentScreen is SubScreenBeatmapSelect);
((SubScreenBeatmapSelect)screenStack.CurrentScreen).RollFinalBeatmap(matchmakingState.CandidateItems, matchmakingState.CandidateItem);
break;
case MatchmakingStage.ResultsDisplaying:
screenStack.Push(new SubScreenRoundResults());
break;
case MatchmakingStage.Ended:
screenStack.Push(new SubScreenResults());
break;
}
});
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (client.IsNotNull())
client.MatchRoomStateChanged -= onMatchRoomStateChanged;
}
}
}
}

View File

@@ -25,13 +25,16 @@ using osu.Game.Overlays;
using osu.Game.Overlays.Dialog;
using osu.Game.Rulesets;
using osu.Game.Screens.OnlinePlay.Match.Components;
using osu.Game.Screens.OnlinePlay.Matchmaking.Screens;
using osu.Game.Screens.OnlinePlay.Matchmaking.Match.Gameplay;
using osu.Game.Screens.OnlinePlay.Multiplayer;
using osu.Game.Users;
namespace osu.Game.Screens.OnlinePlay.Matchmaking
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match
{
public partial class MatchmakingScreen : OsuScreen
/// <summary>
/// The main matchmaking screen which houses a custom <see cref="ScreenStack"/> through the life cycle of a single session.
/// </summary>
public partial class ScreenMatchmaking : OsuScreen
{
/// <summary>
/// Padding between rows of the content.
@@ -76,7 +79,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking
private MatchChatDisplay chat = null!;
public MatchmakingScreen(MultiplayerRoom room)
public ScreenMatchmaking(MultiplayerRoom room)
{
this.room = room;
@@ -128,7 +131,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking
RelativeSizeAxes = Axes.Both,
Colour = colourProvider.Background6,
},
new MatchmakingScreenStack(),
new ScreenStack(),
}
}
],
@@ -248,7 +251,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking
sampleStart?.Play();
this.Push(new MultiplayerPlayerLoader(() => new MatchmakingPlayer(new Room(room), new PlaylistItem(client.Room!.CurrentPlaylistItem), room.Users.ToArray())));
this.Push(new MultiplayerPlayerLoader(() => new ScreenGameplay(new Room(room), new PlaylistItem(client.Room!.CurrentPlaylistItem), room.Users.ToArray())));
});
private void checkForAutomaticDownload()

View File

@@ -0,0 +1,234 @@
// 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.
using System;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Localisation;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.Matchmaking;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Multiplayer.MatchTypes.Matchmaking;
using osu.Game.Overlays;
using osuTK;
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match
{
public partial class StageDisplay
{
internal partial class StageSegment : CompositeDrawable
{
[Resolved]
private MultiplayerClient client { get; set; } = null!;
public readonly int? Round;
private readonly MatchmakingStage stage;
private readonly LocalisableString displayText;
private Drawable progressBar = null!;
private DateTimeOffset countdownStartTime;
private DateTimeOffset countdownEndTime;
private SpriteIcon arrow = null!;
private Sample? countdownTickSample;
private double? lastSamplePlayback;
public bool Active { get; private set; }
public float Progress => progressBar.Width;
public StageSegment(int? round, MatchmakingStage stage, LocalisableString displayText)
{
Round = round;
this.stage = stage;
this.displayText = displayText;
AutoSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load(AudioManager audio, OverlayColourProvider colourProvider)
{
InternalChild = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(5),
Children = new Drawable[]
{
arrow = new SpriteIcon
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Alpha = 0.5f,
Size = new Vector2(16),
Icon = FontAwesome.Solid.ArrowRight,
Margin = new MarginPadding { Horizontal = 10 }
},
new Container
{
Masking = true,
CornerRadius = 5,
CornerExponent = 10,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
AutoSizeAxes = Axes.Both,
Children = new[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour =
ColourInfo.GradientVertical(
colourProvider.Dark2,
colourProvider.Dark1
),
},
progressBar = new Box
{
Blending = BlendingParameters.Additive,
EdgeSmoothness = new Vector2(1),
RelativeSizeAxes = Axes.Both,
Width = 0,
Colour = colourProvider.Dark3,
},
new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Text = displayText,
Padding = new MarginPadding(10)
}
}
}
}
};
Alpha = 0.5f;
countdownTickSample = audio.Samples.Get(@"Multiplayer/countdown-tick");
}
protected override void LoadComplete()
{
base.LoadComplete();
client.MatchRoomStateChanged += onMatchRoomStateChanged;
client.CountdownStarted += onCountdownStarted;
client.CountdownStopped += onCountdownStopped;
if (client.Room != null)
{
onMatchRoomStateChanged(client.Room.MatchState);
foreach (var countdown in client.Room.ActiveCountdowns)
onCountdownStarted(countdown);
}
}
protected override void Update()
{
base.Update();
if (!Active)
return;
TimeSpan total = countdownEndTime - countdownStartTime;
TimeSpan elapsed = DateTimeOffset.Now - countdownStartTime;
if (total.TotalMilliseconds <= 0)
{
progressBar.Width = 0;
return;
}
progressBar.Width = (float)Math.Clamp(elapsed.TotalMilliseconds / total.TotalMilliseconds, 0, 1);
int secondsRemaining = Math.Max(0, (int)Math.Ceiling((total.TotalMilliseconds - elapsed.TotalMilliseconds) / 1000));
if (total.TotalMilliseconds - elapsed.TotalMilliseconds <= 3000
&& lastSamplePlayback != secondsRemaining)
{
countdownTickSample?.Play();
lastSamplePlayback = secondsRemaining;
}
}
private void onMatchRoomStateChanged(MatchRoomState? state) => Scheduler.Add(() =>
{
bool wasActive = Active;
Active = false;
if (state is not MatchmakingRoomState roomState)
return;
if (Round != null && roomState.CurrentRound != Round)
return;
Active = stage == roomState.Stage;
if (wasActive)
progressBar.Width = 1;
bool isPreparing =
(stage == MatchmakingStage.RoundWarmupTime && roomState.Stage == MatchmakingStage.WaitingForClientsJoin) ||
(stage == MatchmakingStage.GameplayWarmupTime && roomState.Stage == MatchmakingStage.WaitingForClientsBeatmapDownload) ||
(stage == MatchmakingStage.ResultsDisplaying && roomState.Stage == MatchmakingStage.Gameplay);
if (isPreparing)
{
arrow.FadeTo(1, 500)
.Then()
.FadeTo(0.5f, 500)
.Loop();
}
});
private void onCountdownStarted(MultiplayerCountdown countdown) => Scheduler.Add(() =>
{
if (!Active)
return;
if (countdown is not MatchmakingStageCountdown)
return;
countdownStartTime = DateTimeOffset.Now;
countdownEndTime = countdownStartTime + countdown.TimeRemaining;
arrow.FadeIn(500, Easing.OutQuint);
this.FadeIn(200);
});
private void onCountdownStopped(MultiplayerCountdown countdown) => Scheduler.Add(() =>
{
if (!Active)
return;
if (countdown is not MatchmakingStageCountdown)
return;
countdownEndTime = DateTimeOffset.Now;
});
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (client.IsNotNull())
{
client.MatchRoomStateChanged -= onMatchRoomStateChanged;
client.CountdownStarted -= onCountdownStarted;
client.CountdownStopped -= onCountdownStopped;
}
}
}
}
}

View File

@@ -0,0 +1,129 @@
// 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.
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Localisation;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Multiplayer.MatchTypes.Matchmaking;
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match
{
public partial class StageDisplay
{
public partial class StatusText : CompositeDrawable
{
[Resolved]
private MultiplayerClient client { get; set; } = null!;
private OsuSpriteText text = null!;
private Sample? textChangedSample;
private double? lastSamplePlayback;
public StatusText()
{
AutoSizeAxes = Axes.X;
Height = 16;
}
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
InternalChild = text = new OsuSpriteText
{
Alpha = 0,
Height = 16,
Font = OsuFont.Style.Caption1,
AlwaysPresent = true,
};
textChangedSample = audio.Samples.Get(@"Multiplayer/Matchmaking/stage-message");
}
protected override void LoadComplete()
{
base.LoadComplete();
client.MatchRoomStateChanged += onMatchRoomStateChanged;
onMatchRoomStateChanged(client.Room!.MatchState);
}
private void onMatchRoomStateChanged(MatchRoomState? state) => Scheduler.Add(() =>
{
if (state is not MatchmakingRoomState matchmakingState)
return;
text.Text = getTextForStatus(matchmakingState.Stage);
if (text.Text == string.Empty || (lastSamplePlayback != null && Time.Current - lastSamplePlayback < OsuGameBase.SAMPLE_DEBOUNCE_TIME))
return;
textChangedSample?.Play();
lastSamplePlayback = Time.Current;
LocalisableString textForStatus = getTextForStatus(matchmakingState.Stage);
if (string.IsNullOrEmpty(textForStatus.ToString()))
{
text.FadeOut();
return;
}
text.RotateTo(2f)
.RotateTo(0, 500, Easing.OutQuint);
text.FadeInFromZero(500, Easing.OutQuint);
using (text.BeginDelayedSequence(500))
{
text
.FadeTo(0.6f, 400, Easing.In)
.Then()
.FadeTo(1, 400, Easing.Out)
.Loop();
}
text.ScaleTo(0.3f)
.ScaleTo(1, 500, Easing.OutQuint);
text.Text = textForStatus;
});
private LocalisableString getTextForStatus(MatchmakingStage status)
{
switch (status)
{
case MatchmakingStage.WaitingForClientsJoin:
return "Players are joining the match...";
case MatchmakingStage.WaitingForClientsBeatmapDownload:
return "Players are downloading the beatmap...";
case MatchmakingStage.Gameplay:
return "Game is in progress...";
case MatchmakingStage.Ended:
return "Thanks for playing! The match will close shortly.";
default:
return string.Empty;
}
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (client.IsNotNull())
client.MatchRoomStateChanged -= onMatchRoomStateChanged;
}
}
}
}

View File

@@ -16,8 +16,11 @@ using osu.Game.Online.Multiplayer.MatchTypes.Matchmaking;
using osu.Game.Overlays;
using osuTK;
namespace osu.Game.Screens.OnlinePlay.Matchmaking
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match
{
/// <summary>
/// A "global" footer staple element in matchmaking which shows the current progression of the room, from start to finish.
/// </summary>
public partial class StageDisplay : CompositeDrawable
{
public const float HEIGHT = 96;
@@ -68,7 +71,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking
Direction = FillDirection.Horizontal,
},
},
new StageText
new StatusText
{
Y = 32,
Anchor = Anchor.Centre,
@@ -93,23 +96,23 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking
},
};
flow.Add(new StageBubble(null, MatchmakingStage.WaitingForClientsJoin, "Waiting for other users"));
flow.Add(new StageSegment(null, MatchmakingStage.WaitingForClientsJoin, "Waiting for other users"));
for (int i = 1; i <= round_count; i++)
{
flow.Add(new StageBubble(i, MatchmakingStage.RoundWarmupTime, "Next Round"));
flow.Add(new StageBubble(i, MatchmakingStage.UserBeatmapSelect, "Beatmap Selection"));
flow.Add(new StageBubble(i, MatchmakingStage.GameplayWarmupTime, "Get Ready"));
flow.Add(new StageBubble(i, MatchmakingStage.ResultsDisplaying, "Results"));
flow.Add(new StageSegment(i, MatchmakingStage.RoundWarmupTime, "Next Round"));
flow.Add(new StageSegment(i, MatchmakingStage.UserBeatmapSelect, "Beatmap Selection"));
flow.Add(new StageSegment(i, MatchmakingStage.GameplayWarmupTime, "Get Ready"));
flow.Add(new StageSegment(i, MatchmakingStage.ResultsDisplaying, "Results"));
}
flow.Add(new StageBubble(null, MatchmakingStage.Ended, "Match End"));
flow.Add(new StageSegment(null, MatchmakingStage.Ended, "Match End"));
}
protected override void Update()
{
base.Update();
var bubble = flow.OfType<StageBubble>().FirstOrDefault(b => b.Active);
var bubble = flow.OfType<StageSegment>().FirstOrDefault(b => b.Active);
if (bubble != null)
{

View File

@@ -12,14 +12,18 @@ using osu.Game.Online.Multiplayer;
using osu.Game.Online.Multiplayer.MatchTypes.Matchmaking;
using osuTK;
namespace osu.Game.Screens.OnlinePlay.Matchmaking
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match
{
public partial class PlayerPanelList : CompositeDrawable
/// <summary>
/// A component which maintains the layout of the players in a matchmaking room.
/// Can be controlled to display the panels in a certain location and in multiple styles.
/// </summary>
public partial class UserPanelOverlay : CompositeDrawable
{
[Resolved]
private MultiplayerClient client { get; set; } = null!;
private Container<PlayerPanel> panels = null!;
private Container<MatchmakingUserPanel> panels = null!;
private PlayerPanelCellContainer gridLayout = null!;
private PlayerPanelCellContainer splitLayoutLeft = null!;
private PlayerPanelCellContainer splitLayoutRight = null!;
@@ -56,7 +60,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking
Direction = FillDirection.Vertical,
Spacing = new Vector2(20, 5),
},
panels = new Container<PlayerPanel>
panels = new Container<MatchmakingUserPanel>
{
RelativeSizeAxes = Axes.Both
}
@@ -106,7 +110,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking
private void onUserJoined(MultiplayerRoomUser user) => Scheduler.Add(() =>
{
panels.Add(new PlayerPanel(user)
panels.Add(new MatchmakingUserPanel(user)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@@ -211,7 +215,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking
[Resolved]
private MultiplayerClient client { get; set; } = null!;
public void AcquirePanels(PlayerPanel[] panels)
public void AcquirePanels(MatchmakingUserPanel[] panels)
{
while (Count < panels.Length)
{
@@ -255,10 +259,10 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking
private partial class PlayerPanelCell : Drawable
{
private PlayerPanel? panel;
private MatchmakingUserPanel? panel;
private bool isAnimating;
public void AcquirePanel(PlayerPanel panel)
public void AcquirePanel(MatchmakingUserPanel panel)
{
this.panel = panel;
isAnimating = true;
@@ -276,7 +280,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking
if (panel == null)
return;
Size = panel.Horizontal ? PlayerPanel.SIZE_HORIZONTAL : PlayerPanel.SIZE_VERTICAL;
Size = panel.Horizontal ? MatchmakingUserPanel.SIZE_HORIZONTAL : MatchmakingUserPanel.SIZE_VERTICAL;
Size *= panel.Scale;
var targetPos = getFinalPosition();
@@ -298,12 +302,12 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking
=> panel.Parent!.ToLocalSpace(ScreenSpaceDrawQuad.Centre) - panel.AnchorPosition;
}
}
}
public enum PanelDisplayStyle
{
Grid,
Split,
Hidden
}
public enum PanelDisplayStyle
{
Grid,
Split,
Hidden
}
}

View File

@@ -11,12 +11,17 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Utils;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Screens.OnlinePlay.Matchmaking.Match;
using osu.Game.Screens.Ranking;
using osuTK;
namespace osu.Game.Screens.OnlinePlay.Matchmaking
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Queue
{
public partial class MatchmakingCloud : CompositeDrawable
/// <summary>
/// A visualisation at the top level of matchmaking which shows the overall system status.
/// This is intended to be something which users can watch while idle, for fun or otherwise.
/// </summary>
public partial class CloudVisualisation : CompositeDrawable
{
private APIUser[] users = [];
private Container usersContainer = null!;

View File

@@ -16,9 +16,9 @@ using osu.Game.Rulesets;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Screens.OnlinePlay.Matchmaking
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Queue
{
public partial class MatchmakingPoolSelector : CompositeDrawable
public partial class PoolSelector : CompositeDrawable
{
private const float icon_size = 48;
@@ -27,7 +27,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking
private FillFlowContainer<SelectorButton> poolFlow = null!;
public MatchmakingPoolSelector()
public PoolSelector()
{
AutoSizeAxes = Axes.Both;
}

View File

@@ -10,13 +10,21 @@ using osu.Game.Online.Multiplayer;
using osu.Game.Online.Rooms;
using osu.Game.Overlays;
using osu.Game.Overlays.Notifications;
using osu.Game.Screens.OnlinePlay.Matchmaking.Screens;
using osu.Game.Screens.OnlinePlay.Matchmaking.Intro;
namespace osu.Game.Screens.OnlinePlay.Matchmaking
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Queue
{
public partial class MatchmakingController : Component
/// <summary>
/// A component which acts as a bridge between the online component (ie <see cref="MultiplayerClient"/>)
/// and the visual representations and flow of queueing for matchmaking.
///
/// Includes support for deferring to background.
/// </summary>
/// <remarks>
/// This is initialised and cached in the <see cref="ScreenQueue"/> but can be used throughout the system via DI.</remarks>
public partial class QueueController : Component
{
public readonly Bindable<MatchmakingQueueScreen.MatchmakingScreenState> CurrentState = new Bindable<MatchmakingQueueScreen.MatchmakingScreenState>();
public readonly Bindable<ScreenQueue.MatchmakingScreenState> CurrentState = new Bindable<ScreenQueue.MatchmakingScreenState>();
[Resolved]
private MultiplayerClient client { get; set; } = null!;
@@ -63,12 +71,12 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking
private void onRoomUpdated() => Scheduler.Add(() =>
{
if (client.Room == null)
CurrentState.Value = MatchmakingQueueScreen.MatchmakingScreenState.Idle;
CurrentState.Value = ScreenQueue.MatchmakingScreenState.Idle;
});
private void onMatchmakingQueueJoined() => Scheduler.Add(() =>
{
CurrentState.Value = MatchmakingQueueScreen.MatchmakingScreenState.Queueing;
CurrentState.Value = ScreenQueue.MatchmakingScreenState.Queueing;
if (isBackgrounded)
{
@@ -79,15 +87,15 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking
private void onMatchmakingQueueLeft() => Scheduler.Add(() =>
{
if (CurrentState.Value != MatchmakingQueueScreen.MatchmakingScreenState.InRoom)
CurrentState.Value = MatchmakingQueueScreen.MatchmakingScreenState.Idle;
if (CurrentState.Value != ScreenQueue.MatchmakingScreenState.InRoom)
CurrentState.Value = ScreenQueue.MatchmakingScreenState.Idle;
closeNotifications();
});
private void onMatchmakingRoomInvited() => Scheduler.Add(() =>
{
CurrentState.Value = MatchmakingQueueScreen.MatchmakingScreenState.PendingAccept;
CurrentState.Value = ScreenQueue.MatchmakingScreenState.PendingAccept;
if (backgroundNotification != null)
{
@@ -101,7 +109,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking
client.JoinRoom(new Room { RoomID = roomId }, password)
.FireAndForget(() => Scheduler.Add(() =>
{
CurrentState.Value = MatchmakingQueueScreen.MatchmakingScreenState.InRoom;
CurrentState.Value = ScreenQueue.MatchmakingScreenState.InRoom;
}));
});
@@ -118,7 +126,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking
CompletionClickAction = () =>
{
client.MatchmakingAcceptInvitation().FireAndForget();
performer?.PerformFromScreen(s => s.Push(new MatchmakingIntroScreen()));
performer?.PerformFromScreen(s => s.Push(new IntroScreen()));
closeNotifications();
return true;

View File

@@ -27,18 +27,22 @@ using osu.Game.Online.Multiplayer;
using osu.Game.Overlays;
using osu.Game.Overlays.Dialog;
using osu.Game.Rulesets;
using osu.Game.Screens.OnlinePlay.Matchmaking.Match;
using osuTK;
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Screens
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Queue
{
public partial class MatchmakingQueueScreen : OsuScreen
/// <summary>
/// The initial screen that users arrive at when preparing for a quick play session.
/// </summary>
public partial class ScreenQueue : OsuScreen
{
public override bool ShowFooter => true;
private Container mainContent = null!;
private MatchmakingScreenState state;
private MatchmakingCloud cloud = null!;
private CloudVisualisation cloud = null!;
[Resolved]
private IAPIProvider api { get; set; } = null!;
@@ -56,7 +60,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Screens
private IDialogOverlay dialogOverlay { get; set; } = null!;
[Resolved]
private MatchmakingController controller { get; set; } = null!;
private QueueController controller { get; set; } = null!;
[Resolved]
private UserLookupCache userLookupCache { get; set; } = null!;
@@ -79,7 +83,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Screens
InternalChildren = new Drawable[]
{
cloud = new MatchmakingCloud
cloud = new CloudVisualisation
{
Y = -100,
Anchor = Anchor.Centre,
@@ -252,7 +256,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Screens
Spacing = new Vector2(10),
Children = new Drawable[]
{
new MatchmakingPoolSelector
new PoolSelector
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
@@ -411,7 +415,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Screens
};
using (BeginDelayedSequence(2000))
Schedule(() => this.Push(new MatchmakingScreen(client.Room!)));
Schedule(() => this.Push(new ScreenMatchmaking(client.Room!)));
break;
default:

View File

@@ -1,130 +0,0 @@
// 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.
using System.Diagnostics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Screens;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Multiplayer.MatchTypes.Matchmaking;
using osu.Game.Screens.OnlinePlay.Matchmaking.Screens.Idle;
using osu.Game.Screens.OnlinePlay.Matchmaking.Screens.Pick;
using osu.Game.Screens.OnlinePlay.Matchmaking.Screens.Results;
using osu.Game.Screens.OnlinePlay.Matchmaking.Screens.RoundResults;
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Screens
{
public partial class MatchmakingScreenStack : CompositeDrawable
{
[Resolved]
private MultiplayerClient client { get; set; } = null!;
private ScreenStack screenStack = null!;
private PlayerPanelList playersList = null!;
[BackgroundDependencyLoader]
private void load()
{
RelativeSizeAxes = Axes.Both;
InternalChildren = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding(10)
{
Bottom = StageDisplay.HEIGHT,
},
Children = new Drawable[]
{
screenStack = new ScreenStack(),
}
},
playersList = new PlayerPanelList
{
DisplayArea = this
},
new StageDisplay
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X
}
};
}
protected override void LoadComplete()
{
base.LoadComplete();
screenStack.ScreenPushed += onScreenPushed;
screenStack.ScreenExited += onScreenExited;
screenStack.Push(new IdleScreen());
client.MatchRoomStateChanged += onMatchRoomStateChanged;
onMatchRoomStateChanged(client.Room!.MatchState);
}
private void onScreenPushed(IScreen lastScreen, IScreen newScreen)
{
if (newScreen is not MatchmakingSubScreen matchmakingSubScreen)
return;
playersList.DisplayStyle = matchmakingSubScreen.PlayersDisplayStyle;
playersList.DisplayArea = matchmakingSubScreen.PlayersDisplayArea;
}
private void onScreenExited(IScreen lastScreen, IScreen newScreen)
{
if (newScreen is not MatchmakingSubScreen matchmakingSubScreen)
return;
playersList.DisplayStyle = matchmakingSubScreen.PlayersDisplayStyle;
playersList.DisplayArea = matchmakingSubScreen.PlayersDisplayArea;
}
private void onMatchRoomStateChanged(MatchRoomState? state) => Scheduler.Add(() =>
{
if (state is not MatchmakingRoomState matchmakingState)
return;
switch (matchmakingState.Stage)
{
case MatchmakingStage.WaitingForClientsJoin:
case MatchmakingStage.RoundWarmupTime:
while (screenStack.CurrentScreen is not IdleScreen)
screenStack.Exit();
break;
case MatchmakingStage.UserBeatmapSelect:
screenStack.Push(new PickScreen());
break;
case MatchmakingStage.ServerBeatmapFinalised:
Debug.Assert(screenStack.CurrentScreen is PickScreen);
((PickScreen)screenStack.CurrentScreen).RollFinalBeatmap(matchmakingState.CandidateItems, matchmakingState.CandidateItem);
break;
case MatchmakingStage.ResultsDisplaying:
screenStack.Push(new RoundResultsScreen());
break;
case MatchmakingStage.Ended:
screenStack.Push(new ResultsScreen());
break;
}
});
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (client.IsNotNull())
client.MatchRoomStateChanged -= onMatchRoomStateChanged;
}
}
}

View File

@@ -1,176 +0,0 @@
// 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.
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Localisation;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays;
using osuTK;
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Screens.Pick
{
public partial class BeatmapPanel : CompositeDrawable
{
public static readonly Vector2 SIZE = new Vector2(300, 70);
public readonly Container OverlayLayer = new Container { RelativeSizeAxes = Axes.Both };
public APIBeatmap? Beatmap
{
get => beatmap;
set
{
if (beatmap?.OnlineID == value?.OnlineID)
return;
beatmap = value;
if (IsLoaded)
updateContent();
}
}
private APIBeatmap? beatmap;
private Container content = null!;
private UpdateableOnlineBeatmapSetCover cover = null!;
public BeatmapPanel(APIBeatmap? beatmap = null)
{
this.beatmap = beatmap;
}
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
{
Masking = true;
CornerRadius = 6;
InternalChildren = new Drawable[]
{
cover = new UpdateableOnlineBeatmapSetCover(BeatmapSetCoverType.Card, timeBeforeLoad: 0, timeBeforeUnload: 10000)
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = ColourInfo.GradientHorizontal(
colourProvider.Background4.Opacity(0.7f),
colourProvider.Background4.Opacity(0.4f)
)
},
content = new Container
{
RelativeSizeAxes = Axes.Both,
},
OverlayLayer,
};
}
protected override void LoadComplete()
{
base.LoadComplete();
updateContent();
FinishTransforms(true);
}
private void updateContent()
{
foreach (var child in content.Children)
child.FadeOut(300).Expire();
cover.OnlineInfo = beatmap?.BeatmapSet;
if (beatmap != null)
{
var panelContent = new BeatmapPanelContent(beatmap)
{
RelativeSizeAxes = Axes.Both,
};
content.Add(panelContent);
panelContent.FadeInFromZero(300);
}
}
private partial class BeatmapPanelContent : CompositeDrawable
{
private readonly APIBeatmap beatmap;
public BeatmapPanelContent(APIBeatmap beatmap)
{
this.beatmap = beatmap;
}
[BackgroundDependencyLoader]
private void load()
{
InternalChild = new FillFlowContainer
{
Direction = FillDirection.Vertical,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Padding = new MarginPadding { Horizontal = 12 },
Children = new Drawable[]
{
new TruncatingSpriteText
{
Text = new RomanisableString(beatmap.Metadata.TitleUnicode, beatmap.Metadata.TitleUnicode),
Font = OsuFont.Default.With(size: 19, weight: FontWeight.SemiBold),
RelativeSizeAxes = Axes.X,
},
new TextFlowContainer(s =>
{
s.Font = OsuFont.GetFont(size: 16, weight: FontWeight.SemiBold);
}).With(d =>
{
d.RelativeSizeAxes = Axes.X;
d.AutoSizeAxes = Axes.Y;
d.AddText("by ");
d.AddText(new RomanisableString(beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist));
}),
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Horizontal,
Margin = new MarginPadding { Top = 6 },
Spacing = new Vector2(4),
Children = new Drawable[]
{
new StarRatingDisplay(new StarDifficulty(beatmap.StarRating, 0), StarRatingDisplaySize.Small)
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
},
new TruncatingSpriteText
{
Text = beatmap.DifficultyName,
Font = OsuFont.Default.With(size: 16, weight: FontWeight.SemiBold),
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
},
}
},
},
};
}
}
}
}

View File

@@ -1,157 +0,0 @@
// 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.
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Online.API.Requests.Responses;
using osuTK;
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Screens.Pick
{
public partial class BeatmapSelectionOverlay : CompositeDrawable
{
private readonly Dictionary<int, SelectionAvatar> avatars = new Dictionary<int, SelectionAvatar>();
private readonly Container<SelectionAvatar> avatarContainer;
private Sample? userAddedSample;
private double? lastSamplePlayback;
public new Axes AutoSizeAxes
{
get => base.AutoSizeAxes;
set => base.AutoSizeAxes = value;
}
public new MarginPadding Padding
{
get => base.Padding;
set => base.Padding = value;
}
public BeatmapSelectionOverlay()
{
InternalChild = avatarContainer = new Container<SelectionAvatar>();
}
protected override void LoadComplete()
{
base.LoadComplete();
avatarContainer.AutoSizeAxes = AutoSizeAxes;
avatarContainer.RelativeSizeAxes = RelativeSizeAxes;
}
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
userAddedSample = audio.Samples.Get(@"Multiplayer/player-ready");
}
public bool AddUser(APIUser user, bool isOwnUser)
{
if (avatars.ContainsKey(user.Id))
return false;
var avatar = new SelectionAvatar(user, isOwnUser)
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
};
avatarContainer.Add(avatars[user.Id] = avatar);
if (lastSamplePlayback == null || Time.Current - lastSamplePlayback > OsuGameBase.SAMPLE_DEBOUNCE_TIME)
{
userAddedSample?.Play();
lastSamplePlayback = Time.Current;
}
updateLayout();
avatar.FinishTransforms();
return true;
}
public bool RemoveUser(int id)
{
if (!avatars.Remove(id, out var avatar))
return false;
avatar.PopOutAndExpire();
avatarContainer.ChangeChildDepth(avatar, float.MaxValue);
updateLayout();
return true;
}
private void updateLayout()
{
const double stagger = 30;
const float spacing = 4;
double delay = 0;
float x = 0;
for (int i = avatarContainer.Count - 1; i >= 0; i--)
{
var avatar = avatarContainer[i];
if (avatar.Expired)
continue;
avatar.Delay(delay).MoveToX(x, 500, Easing.OutElasticQuarter);
x -= avatar.LayoutSize.X + spacing;
delay += stagger;
}
}
public partial class SelectionAvatar : CompositeDrawable
{
public bool Expired { get; private set; }
private readonly Container content;
public SelectionAvatar(APIUser user, bool isOwnUser)
{
Size = new Vector2(30);
InternalChildren = new Drawable[]
{
content = new Container
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Child = new MatchmakingAvatar(user, isOwnUser)
}
};
}
protected override void LoadComplete()
{
base.LoadComplete();
content.ScaleTo(0)
.ScaleTo(1, 500, Easing.OutElasticHalf)
.FadeIn(200);
}
public void PopOutAndExpire()
{
content.ScaleTo(0, 400, Easing.OutExpo);
this.FadeOut(100).Expire();
Expired = true;
}
}
}
}

View File

@@ -1,190 +0,0 @@
// 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.
using System;
using osu.Framework.Allocation;
using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events;
using osu.Game.Database;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Rooms;
using osuTK.Input;
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Screens.Pick
{
public partial class BeatmapSelectionPanel : Container
{
private const float corner_radius = 6;
private const float border_width = 3;
public readonly MultiplayerPlaylistItem Item;
private readonly Container scaleContainer;
private readonly BeatmapPanel beatmapPanel;
private readonly BeatmapSelectionOverlay selectionOverlay;
private readonly Container border;
private readonly Box flash;
public bool AllowSelection;
public Action<MultiplayerPlaylistItem>? Action;
[Resolved]
private BeatmapLookupCache beatmapLookupCache { get; set; } = null!;
public override bool PropagatePositionalInputSubTree => AllowSelection;
public BeatmapSelectionPanel(MultiplayerPlaylistItem item)
{
Item = item;
Size = BeatmapPanel.SIZE;
InternalChildren = new Drawable[]
{
scaleContainer = new Container
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding(-border_width),
Child = border = new Container
{
RelativeSizeAxes = Axes.Both,
Masking = true,
CornerRadius = corner_radius + border_width,
Alpha = 0,
Child = new Box { RelativeSizeAxes = Axes.Both },
}
},
beatmapPanel = new BeatmapPanel
{
RelativeSizeAxes = Axes.Both,
OverlayLayer =
{
Children = new[]
{
flash = new Box
{
Blending = BlendingParameters.Additive,
RelativeSizeAxes = Axes.Both,
Alpha = 0,
},
}
}
},
selectionOverlay = new BeatmapSelectionOverlay
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding { Horizontal = 10 },
Origin = Anchor.CentreLeft,
},
}
},
new HoverClickSounds(),
};
}
protected override void LoadComplete()
{
base.LoadComplete();
beatmapLookupCache.GetBeatmapAsync(Item.BeatmapID).ContinueWith(b => Schedule(() =>
{
var beatmap = b.GetResultSafely()!;
beatmap.StarRating = Item.StarRating;
beatmapPanel.Beatmap = beatmap;
}));
}
public bool AddUser(APIUser user, bool isOwnUser = false) => selectionOverlay.AddUser(user, isOwnUser);
public bool RemoveUser(int userId) => selectionOverlay.RemoveUser(userId);
public bool RemoveUser(APIUser user) => RemoveUser(user.Id);
protected override bool OnHover(HoverEvent e)
{
flash.FadeTo(0.2f, 50)
.Then()
.FadeTo(0.1f, 300);
return true;
}
protected override void OnHoverLost(HoverLostEvent e)
{
base.OnHoverLost(e);
flash.FadeOut(200);
}
protected override bool OnMouseDown(MouseDownEvent e)
{
if (e.Button == MouseButton.Left)
{
scaleContainer.ScaleTo(0.95f, 400, Easing.OutExpo);
return true;
}
return base.OnMouseDown(e);
}
protected override void OnMouseUp(MouseUpEvent e)
{
base.OnMouseUp(e);
if (e.Button == MouseButton.Left)
{
scaleContainer.ScaleTo(1f, 500, Easing.OutElasticHalf);
}
}
protected override bool OnClick(ClickEvent e)
{
Action?.Invoke(Item);
flash.FadeTo(0.5f, 50)
.Then()
.FadeTo(0.1f, 400);
return true;
}
public void ShowBorder() => border.Show();
public void HideBorder() => border.Hide();
public void FadeInAndEnterFromBelow(double duration = 500, double delay = 0, float distance = 200)
{
scaleContainer
.FadeOut()
.MoveToY(distance)
.Delay(delay)
.FadeIn(duration / 2)
.MoveToY(0, duration, Easing.OutExpo);
}
public void PopOutAndExpire(double duration = 400, double delay = 0, Easing easing = Easing.InCubic)
{
AllowSelection = false;
scaleContainer.Delay(delay)
.ScaleTo(0, duration, easing)
.FadeOut(duration);
this.Delay(delay + duration).FadeOut().Expire();
}
}
}

View File

@@ -1,36 +0,0 @@
// 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.
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Scoring;
using osu.Game.Screens.Ranking;
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Screens.RoundResults
{
internal partial class RoundResultsScorePanel : CompositeDrawable
{
public RoundResultsScorePanel(ScoreInfo score)
{
AutoSizeAxes = Axes.Both;
InternalChild = new InstantSizingScorePanel(score);
}
public override bool PropagateNonPositionalInputSubTree => false;
public override bool PropagatePositionalInputSubTree => false;
private partial class InstantSizingScorePanel : ScorePanel
{
public InstantSizingScorePanel(ScoreInfo score, bool isNewLocalScore = false)
: base(score, isNewLocalScore)
{
}
protected override void LoadComplete()
{
base.LoadComplete();
FinishTransforms(true);
}
}
}
}

View File

@@ -1,231 +0,0 @@
// 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.
using System;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Localisation;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.Matchmaking;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Multiplayer.MatchTypes.Matchmaking;
using osu.Game.Overlays;
using osuTK;
namespace osu.Game.Screens.OnlinePlay.Matchmaking
{
internal partial class StageBubble : CompositeDrawable
{
[Resolved]
private MultiplayerClient client { get; set; } = null!;
public readonly int? Round;
private readonly MatchmakingStage stage;
private readonly LocalisableString displayText;
private Drawable progressBar = null!;
private DateTimeOffset countdownStartTime;
private DateTimeOffset countdownEndTime;
private SpriteIcon arrow = null!;
private Sample? countdownTickSample;
private double? lastSamplePlayback;
public bool Active { get; private set; }
public float Progress => progressBar.Width;
public StageBubble(int? round, MatchmakingStage stage, LocalisableString displayText)
{
Round = round;
this.stage = stage;
this.displayText = displayText;
AutoSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load(AudioManager audio, OverlayColourProvider colourProvider)
{
InternalChild = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(5),
Children = new Drawable[]
{
arrow = new SpriteIcon
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Alpha = 0.5f,
Size = new Vector2(16),
Icon = FontAwesome.Solid.ArrowRight,
Margin = new MarginPadding { Horizontal = 10 }
},
new Container
{
Masking = true,
CornerRadius = 5,
CornerExponent = 10,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
AutoSizeAxes = Axes.Both,
Children = new[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour =
ColourInfo.GradientVertical(
colourProvider.Dark2,
colourProvider.Dark1
),
},
progressBar = new Box
{
Blending = BlendingParameters.Additive,
EdgeSmoothness = new Vector2(1),
RelativeSizeAxes = Axes.Both,
Width = 0,
Colour = colourProvider.Dark3,
},
new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Text = displayText,
Padding = new MarginPadding(10)
}
}
}
}
};
Alpha = 0.5f;
countdownTickSample = audio.Samples.Get(@"Multiplayer/countdown-tick");
}
protected override void LoadComplete()
{
base.LoadComplete();
client.MatchRoomStateChanged += onMatchRoomStateChanged;
client.CountdownStarted += onCountdownStarted;
client.CountdownStopped += onCountdownStopped;
if (client.Room != null)
{
onMatchRoomStateChanged(client.Room.MatchState);
foreach (var countdown in client.Room.ActiveCountdowns)
onCountdownStarted(countdown);
}
}
protected override void Update()
{
base.Update();
if (!Active)
return;
TimeSpan total = countdownEndTime - countdownStartTime;
TimeSpan elapsed = DateTimeOffset.Now - countdownStartTime;
if (total.TotalMilliseconds <= 0)
{
progressBar.Width = 0;
return;
}
progressBar.Width = (float)Math.Clamp(elapsed.TotalMilliseconds / total.TotalMilliseconds, 0, 1);
int secondsRemaining = Math.Max(0, (int)Math.Ceiling((total.TotalMilliseconds - elapsed.TotalMilliseconds) / 1000));
if (total.TotalMilliseconds - elapsed.TotalMilliseconds <= 3000
&& lastSamplePlayback != secondsRemaining)
{
countdownTickSample?.Play();
lastSamplePlayback = secondsRemaining;
}
}
private void onMatchRoomStateChanged(MatchRoomState? state) => Scheduler.Add(() =>
{
bool wasActive = Active;
Active = false;
if (state is not MatchmakingRoomState roomState)
return;
if (Round != null && roomState.CurrentRound != Round)
return;
Active = stage == roomState.Stage;
if (wasActive)
progressBar.Width = 1;
bool isPreparing =
(stage == MatchmakingStage.RoundWarmupTime && roomState.Stage == MatchmakingStage.WaitingForClientsJoin) ||
(stage == MatchmakingStage.GameplayWarmupTime && roomState.Stage == MatchmakingStage.WaitingForClientsBeatmapDownload) ||
(stage == MatchmakingStage.ResultsDisplaying && roomState.Stage == MatchmakingStage.Gameplay);
if (isPreparing)
{
arrow.FadeTo(1, 500)
.Then()
.FadeTo(0.5f, 500)
.Loop();
}
});
private void onCountdownStarted(MultiplayerCountdown countdown) => Scheduler.Add(() =>
{
if (!Active)
return;
if (countdown is not MatchmakingStageCountdown)
return;
countdownStartTime = DateTimeOffset.Now;
countdownEndTime = countdownStartTime + countdown.TimeRemaining;
arrow.FadeIn(500, Easing.OutQuint);
this.FadeIn(200);
});
private void onCountdownStopped(MultiplayerCountdown countdown) => Scheduler.Add(() =>
{
if (!Active)
return;
if (countdown is not MatchmakingStageCountdown)
return;
countdownEndTime = DateTimeOffset.Now;
});
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (client.IsNotNull())
{
client.MatchRoomStateChanged -= onMatchRoomStateChanged;
client.CountdownStarted -= onCountdownStarted;
client.CountdownStopped -= onCountdownStopped;
}
}
}
}

View File

@@ -1,126 +0,0 @@
// 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.
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Localisation;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Multiplayer.MatchTypes.Matchmaking;
namespace osu.Game.Screens.OnlinePlay.Matchmaking
{
public partial class StageText : CompositeDrawable
{
[Resolved]
private MultiplayerClient client { get; set; } = null!;
private OsuSpriteText text = null!;
private Sample? textChangedSample;
private double? lastSamplePlayback;
public StageText()
{
AutoSizeAxes = Axes.X;
Height = 16;
}
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
InternalChild = text = new OsuSpriteText
{
Alpha = 0,
Height = 16,
Font = OsuFont.Style.Caption1,
AlwaysPresent = true,
};
textChangedSample = audio.Samples.Get(@"Multiplayer/Matchmaking/stage-message");
}
protected override void LoadComplete()
{
base.LoadComplete();
client.MatchRoomStateChanged += onMatchRoomStateChanged;
onMatchRoomStateChanged(client.Room!.MatchState);
}
private void onMatchRoomStateChanged(MatchRoomState? state) => Scheduler.Add(() =>
{
if (state is not MatchmakingRoomState matchmakingState)
return;
text.Text = getTextForStatus(matchmakingState.Stage);
if (text.Text == string.Empty || (lastSamplePlayback != null && Time.Current - lastSamplePlayback < OsuGameBase.SAMPLE_DEBOUNCE_TIME))
return;
textChangedSample?.Play();
lastSamplePlayback = Time.Current;
LocalisableString textForStatus = getTextForStatus(matchmakingState.Stage);
if (string.IsNullOrEmpty(textForStatus.ToString()))
{
text.FadeOut();
return;
}
text.RotateTo(2f)
.RotateTo(0, 500, Easing.OutQuint);
text.FadeInFromZero(500, Easing.OutQuint);
using (text.BeginDelayedSequence(500))
{
text
.FadeTo(0.6f, 400, Easing.In)
.Then()
.FadeTo(1, 400, Easing.Out)
.Loop();
}
text.ScaleTo(0.3f)
.ScaleTo(1, 500, Easing.OutQuint);
text.Text = textForStatus;
});
private LocalisableString getTextForStatus(MatchmakingStage status)
{
switch (status)
{
case MatchmakingStage.WaitingForClientsJoin:
return "Players are joining the match...";
case MatchmakingStage.WaitingForClientsBeatmapDownload:
return "Players are downloading the beatmap...";
case MatchmakingStage.Gameplay:
return "Game is in progress...";
case MatchmakingStage.Ended:
return "Thanks for playing! The match will close shortly.";
default:
return string.Empty;
}
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (client.IsNotNull())
client.MatchRoomStateChanged -= onMatchRoomStateChanged;
}
}
}