mirror of
https://github.com/ppy/osu.git
synced 2026-05-18 19:50:31 +08:00
310 lines
10 KiB
C#
310 lines
10 KiB
C#
// 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.Linq;
|
|
using osu.Framework.Allocation;
|
|
using osu.Framework.Extensions.ObjectExtensions;
|
|
using osu.Framework.Graphics;
|
|
using osu.Framework.Graphics.Containers;
|
|
using osu.Framework.Utils;
|
|
using osu.Game.Online.Multiplayer;
|
|
using osu.Game.Online.Multiplayer.MatchTypes.Matchmaking;
|
|
using osuTK;
|
|
|
|
namespace osu.Game.Screens.OnlinePlay.Matchmaking
|
|
{
|
|
public partial class PlayerPanelList : CompositeDrawable
|
|
{
|
|
[Resolved]
|
|
private MultiplayerClient client { get; set; } = null!;
|
|
|
|
private Container<PlayerPanel> panels = null!;
|
|
private PlayerPanelCellContainer gridLayout = null!;
|
|
private PlayerPanelCellContainer splitLayoutLeft = null!;
|
|
private PlayerPanelCellContainer splitLayoutRight = null!;
|
|
|
|
private PanelDisplayStyle displayStyle;
|
|
private Drawable? displayArea;
|
|
private bool isAnimatingToDisplayArea;
|
|
|
|
[BackgroundDependencyLoader]
|
|
private void load()
|
|
{
|
|
InternalChildren = new Drawable[]
|
|
{
|
|
gridLayout = new PlayerPanelCellContainer
|
|
{
|
|
RelativeSizeAxes = Axes.Both,
|
|
Spacing = new Vector2(20, 5),
|
|
},
|
|
splitLayoutLeft = new PlayerPanelCellContainer
|
|
{
|
|
Anchor = Anchor.CentreLeft,
|
|
Origin = Anchor.CentreLeft,
|
|
RelativeSizeAxes = Axes.Y,
|
|
AutoSizeAxes = Axes.X,
|
|
Direction = FillDirection.Vertical,
|
|
Spacing = new Vector2(20, 5),
|
|
},
|
|
splitLayoutRight = new PlayerPanelCellContainer
|
|
{
|
|
Anchor = Anchor.CentreRight,
|
|
Origin = Anchor.CentreRight,
|
|
RelativeSizeAxes = Axes.Y,
|
|
AutoSizeAxes = Axes.X,
|
|
Direction = FillDirection.Vertical,
|
|
Spacing = new Vector2(20, 5),
|
|
},
|
|
panels = new Container<PlayerPanel>
|
|
{
|
|
RelativeSizeAxes = Axes.Both
|
|
}
|
|
};
|
|
}
|
|
|
|
protected override void LoadComplete()
|
|
{
|
|
base.LoadComplete();
|
|
|
|
// Set position/size so we don't initially animate.
|
|
Position = getFinalPosition();
|
|
Size = getFinalSize();
|
|
|
|
client.MatchRoomStateChanged += onRoomStateChanged;
|
|
client.UserJoined += onUserJoined;
|
|
client.UserLeft += onUserLeft;
|
|
|
|
if (client.Room != null)
|
|
{
|
|
onRoomStateChanged(client.Room.MatchState);
|
|
foreach (var user in client.Room.Users)
|
|
onUserJoined(user);
|
|
}
|
|
|
|
updateDisplay();
|
|
}
|
|
|
|
public PanelDisplayStyle DisplayStyle
|
|
{
|
|
set
|
|
{
|
|
displayStyle = value;
|
|
if (IsLoaded)
|
|
updateDisplay();
|
|
}
|
|
}
|
|
|
|
public Drawable? DisplayArea
|
|
{
|
|
set
|
|
{
|
|
displayArea = value;
|
|
isAnimatingToDisplayArea = true;
|
|
}
|
|
}
|
|
|
|
private void onUserJoined(MultiplayerRoomUser user) => Scheduler.Add(() =>
|
|
{
|
|
panels.Add(new PlayerPanel(user)
|
|
{
|
|
Anchor = Anchor.Centre,
|
|
Origin = Anchor.Centre,
|
|
Scale = new Vector2(0.8f)
|
|
});
|
|
|
|
updateDisplay();
|
|
});
|
|
|
|
private void onUserLeft(MultiplayerRoomUser user) => Scheduler.Add(() =>
|
|
{
|
|
panels.Single(p => p.RoomUser.Equals(user)).Expire();
|
|
updateDisplay();
|
|
});
|
|
|
|
private void onRoomStateChanged(MatchRoomState? state) => Scheduler.Add(updateDisplay);
|
|
|
|
private void updateDisplay()
|
|
{
|
|
gridLayout.ReleasePanels();
|
|
splitLayoutLeft.ReleasePanels();
|
|
splitLayoutRight.ReleasePanels();
|
|
|
|
switch (displayStyle)
|
|
{
|
|
case PanelDisplayStyle.Grid:
|
|
foreach (var panel in panels)
|
|
{
|
|
panel.FadeTo(1, 200);
|
|
panel.Horizontal = false;
|
|
}
|
|
|
|
gridLayout.AcquirePanels(panels.ToArray());
|
|
break;
|
|
|
|
case PanelDisplayStyle.Split:
|
|
foreach (var panel in panels)
|
|
{
|
|
panel.FadeTo(1, 200);
|
|
panel.Horizontal = true;
|
|
}
|
|
|
|
int leftCount = (int)Math.Ceiling(panels.Count / 2f);
|
|
|
|
splitLayoutLeft.AcquirePanels(panels.Take(leftCount).ToArray());
|
|
splitLayoutRight.AcquirePanels(panels.Skip(leftCount).ToArray());
|
|
break;
|
|
|
|
case PanelDisplayStyle.Hidden:
|
|
foreach (var panel in panels)
|
|
panel.FadeTo(0, 200);
|
|
return;
|
|
}
|
|
}
|
|
|
|
protected override void Update()
|
|
{
|
|
base.Update();
|
|
|
|
var targetPos = getFinalPosition();
|
|
var targetSize = getFinalSize();
|
|
|
|
double duration = isAnimatingToDisplayArea ? 60 : 0;
|
|
|
|
if (Time.Elapsed > 0)
|
|
{
|
|
Position = new Vector2(
|
|
(float)Interpolation.DampContinuously(Position.X, targetPos.X, duration, Time.Elapsed),
|
|
(float)Interpolation.DampContinuously(Position.Y, targetPos.Y, duration, Time.Elapsed)
|
|
);
|
|
|
|
Size = new Vector2(
|
|
(float)Interpolation.DampContinuously(Size.X, targetSize.X, duration, Time.Elapsed),
|
|
(float)Interpolation.DampContinuously(Size.Y, targetSize.Y, duration, Time.Elapsed)
|
|
);
|
|
}
|
|
|
|
// If we don't track the animating state, the animation will also occur when resizing the window.
|
|
isAnimatingToDisplayArea &= !Precision.AlmostEquals(Size, targetSize, 0.5f);
|
|
}
|
|
|
|
private Vector2 getFinalPosition()
|
|
=> displayArea == null ? Vector2.Zero : Parent!.ToLocalSpace(displayArea.ScreenSpaceDrawQuad.TopLeft);
|
|
|
|
private Vector2 getFinalSize()
|
|
=> displayArea == null ? Parent!.DrawSize : Parent!.ToLocalSpace(displayArea.ScreenSpaceDrawQuad.BottomRight) - Parent!.ToLocalSpace(displayArea.ScreenSpaceDrawQuad.TopLeft);
|
|
|
|
protected override void Dispose(bool isDisposing)
|
|
{
|
|
base.Dispose(isDisposing);
|
|
|
|
if (client.IsNotNull())
|
|
{
|
|
client.MatchRoomStateChanged -= onRoomStateChanged;
|
|
client.UserJoined -= onUserJoined;
|
|
client.UserLeft -= onUserLeft;
|
|
}
|
|
}
|
|
|
|
private partial class PlayerPanelCellContainer : FillFlowContainer<PlayerPanelCell>
|
|
{
|
|
[Resolved]
|
|
private MultiplayerClient client { get; set; } = null!;
|
|
|
|
public void AcquirePanels(PlayerPanel[] panels)
|
|
{
|
|
while (Count < panels.Length)
|
|
{
|
|
Add(new PlayerPanelCell
|
|
{
|
|
Anchor = Anchor.Centre,
|
|
Origin = Anchor.Centre
|
|
});
|
|
}
|
|
|
|
while (Count > panels.Length)
|
|
Remove(Children[^1], true);
|
|
|
|
for (int i = 0; i < panels.Length; i++)
|
|
{
|
|
// We'll invalidate the layout position to represent the new placements and the re-flow will happen in UpdateAfterChildren().
|
|
// But the cells expect their positions to be valid as they're updated, which won't be the case until the re-flow happens.
|
|
int i2 = i;
|
|
ScheduleAfterChildren(() => Children[i2].AcquirePanel(panels[i2]));
|
|
|
|
if (client.Room?.MatchState is not MatchmakingRoomState matchmakingState)
|
|
continue;
|
|
|
|
if (matchmakingState.Users.UserDictionary.TryGetValue(panels[i].User.Id, out MatchmakingUser? user))
|
|
SetLayoutPosition(Children[i], user.Placement);
|
|
else
|
|
SetLayoutPosition(Children[i], float.MaxValue);
|
|
}
|
|
}
|
|
|
|
public void ReleasePanels()
|
|
{
|
|
// Matches the schedule in AcquirePanels.
|
|
ScheduleAfterChildren(() =>
|
|
{
|
|
foreach (var panel in Children)
|
|
panel.ReleasePanel();
|
|
});
|
|
}
|
|
}
|
|
|
|
private partial class PlayerPanelCell : Drawable
|
|
{
|
|
private PlayerPanel? panel;
|
|
private bool isAnimating;
|
|
|
|
public void AcquirePanel(PlayerPanel panel)
|
|
{
|
|
this.panel = panel;
|
|
isAnimating = true;
|
|
}
|
|
|
|
public void ReleasePanel()
|
|
{
|
|
panel = null;
|
|
}
|
|
|
|
protected override void Update()
|
|
{
|
|
base.Update();
|
|
|
|
if (panel == null)
|
|
return;
|
|
|
|
Size = panel.Horizontal ? PlayerPanel.SIZE_HORIZONTAL : PlayerPanel.SIZE_VERTICAL;
|
|
Size *= panel.Scale;
|
|
|
|
var targetPos = getFinalPosition();
|
|
|
|
double duration = isAnimating ? 60 : 0;
|
|
|
|
if (Time.Elapsed > 0)
|
|
{
|
|
panel.Position = new Vector2(
|
|
(float)Interpolation.DampContinuously(panel.Position.X, targetPos.X, duration, Time.Elapsed),
|
|
(float)Interpolation.DampContinuously(panel.Position.Y, targetPos.Y, duration, Time.Elapsed)
|
|
);
|
|
}
|
|
|
|
// If we don't track the animating state, the animation will also occur when resizing the window.
|
|
isAnimating &= !Precision.AlmostEquals(panel.Position, targetPos, 0.5f);
|
|
|
|
Vector2 getFinalPosition()
|
|
=> panel.Parent!.ToLocalSpace(ScreenSpaceDrawQuad.Centre) - panel.AnchorPosition;
|
|
}
|
|
}
|
|
|
|
public enum PanelDisplayStyle
|
|
{
|
|
Grid,
|
|
Split,
|
|
Hidden
|
|
}
|
|
}
|
|
}
|