// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System.Linq; 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.Cursor; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Online.Multiplayer; using osu.Game.Rulesets; using osu.Game.Screens.Play.HUD; using osu.Game.Users; using osu.Game.Users.Drawables; using osuTK; using osuTK.Graphics; namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants { public class ParticipantPanel : MultiplayerRoomComposite, IHasContextMenu { public readonly MultiplayerRoomUser User; [Resolved] private IAPIProvider api { get; set; } [Resolved] private RulesetStore rulesets { get; set; } private ModDisplay userModsDisplay; private StateDisplay userStateDisplay; private SpriteIcon crown; public ParticipantPanel(MultiplayerRoomUser user) { User = user; RelativeSizeAxes = Axes.X; Height = 40; } [BackgroundDependencyLoader] private void load() { var user = User.User; var backgroundColour = Color4Extensions.FromHex("#33413C"); InternalChildren = new Drawable[] { crown = new SpriteIcon { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, Icon = FontAwesome.Solid.Crown, Size = new Vector2(14), Colour = Color4Extensions.FromHex("#F7E65D"), Alpha = 0 }, new Container { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Left = 24 }, Child = new Container { RelativeSizeAxes = Axes.Both, Masking = true, CornerRadius = 5, Children = new Drawable[] { new Box { RelativeSizeAxes = Axes.Both, Colour = backgroundColour }, new UserCoverBackground { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, RelativeSizeAxes = Axes.Both, Width = 0.75f, User = user, Colour = ColourInfo.GradientHorizontal(Color4.White.Opacity(0), Color4.White.Opacity(0.25f)) }, new FillFlowContainer { RelativeSizeAxes = Axes.Both, Spacing = new Vector2(10), Direction = FillDirection.Horizontal, Children = new Drawable[] { new UpdateableAvatar { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, RelativeSizeAxes = Axes.Both, FillMode = FillMode.Fit, User = user }, new UpdateableFlag { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, Size = new Vector2(30, 20), Country = user?.Country }, new OsuSpriteText { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 18), Text = user?.Username }, new OsuSpriteText { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, Font = OsuFont.GetFont(size: 14), Text = user?.CurrentModeRank != null ? $"#{user.CurrentModeRank}" : string.Empty } } }, new Container { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, AutoSizeAxes = Axes.Both, Margin = new MarginPadding { Right = 70 }, Child = userModsDisplay = new ModDisplay { Scale = new Vector2(0.5f), ExpansionMode = ExpansionMode.AlwaysContracted, DisplayUnrankedText = false, } }, userStateDisplay = new StateDisplay { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, Margin = new MarginPadding { Right = 10 }, } } } } }; } protected override void OnRoomUpdated() { base.OnRoomUpdated(); if (Room == null) return; const double fade_time = 50; userStateDisplay.UpdateStatus(User.State, User.BeatmapAvailability); if (Room.Host?.Equals(User) == true) crown.FadeIn(fade_time); else crown.FadeOut(fade_time); // If the mods are updated at the end of the frame, the flow container will skip a reflow cycle: https://github.com/ppy/osu-framework/issues/4187 // This looks particularly jarring here, so re-schedule the update to that start of our frame as a fix. Schedule(() => { var ruleset = rulesets.GetRuleset(Room.Settings.RulesetID).CreateInstance(); userModsDisplay.Current.Value = User.Mods.Select(m => m.ToMod(ruleset)).ToList(); }); } public MenuItem[] ContextMenuItems { get { if (Room == null) return null; // If the local user is targetted. if (User.UserID == api.LocalUser.Value.Id) return null; // If the local user is not the host of the room. if (Room.Host?.UserID != api.LocalUser.Value.Id) return null; int targetUser = User.UserID; return new MenuItem[] { new OsuMenuItem("Give host", MenuItemType.Standard, () => { // Ensure the local user is still host. if (Room.Host?.UserID != api.LocalUser.Value.Id) return; Client.TransferHost(targetUser); }) }; } } } }