1
0
mirror of https://github.com/ppy/osu.git synced 2026-06-10 00:51:34 +08:00
Files
osu-lazer/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerSkipOverlay.cs
T
2025-12-15 03:54:09 -05:00

326 lines
12 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.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
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.Input.Events;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.Multiplayer;
using osu.Game.Screens.Play;
using osu.Game.Screens.Ranking;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Screens.OnlinePlay.Multiplayer
{
public partial class MultiplayerSkipOverlay : SkipOverlay
{
[Resolved]
private MultiplayerClient client { get; set; } = null!;
[Resolved]
private OsuColour colours { get; set; } = null!;
private Button skipButton = null!;
public MultiplayerSkipOverlay(double startTime)
: base(startTime)
{
}
protected override OsuClickableContainer CreateButton(IBindable<bool> inSkipPeriod) => skipButton = new Button
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
InSkipPeriod = { BindTarget = inSkipPeriod },
};
protected override void LoadComplete()
{
base.LoadComplete();
skipButton.Enabled.BindValueChanged(e =>
{
RemainingTimeBox.Colour = e.NewValue ? colours.Orange3 : Button.COLOUR_GRAY;
}, true);
client.UserLeft += onUserLeft;
client.UserStateChanged += onUserStateChanged;
client.UserVotedToSkipIntro += onUserVotedToSkipIntro;
updateCount();
}
private void onUserLeft(MultiplayerRoomUser user) => Schedule(updateCount);
private void onUserStateChanged(MultiplayerRoomUser user, MultiplayerUserState state) => Schedule(updateCount);
private void onUserVotedToSkipIntro(int userId, bool voted) => Schedule(() =>
{
FadingContent.TriggerShow();
updateCount();
});
private void updateCount()
{
if (client.Room == null || client.Room.Settings.AutoSkip)
return;
int countTotal = client.Room.Users.Count(u => u.State == MultiplayerUserState.Playing);
int countSkipped = client.Room.Users.Count(u => u.State == MultiplayerUserState.Playing && u.VotedToSkipIntro);
int countRequired = countTotal / 2 + 1;
skipButton.SkippedCount.Value = Math.Min(countRequired, countSkipped);
skipButton.RequiredCount.Value = countRequired;
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (client.IsNotNull())
{
client.UserLeft -= onUserLeft;
client.UserStateChanged -= onUserStateChanged;
client.UserVotedToSkipIntro -= onUserVotedToSkipIntro;
}
}
public partial class Button : OsuClickableContainer
{
private const float chevron_y = 0.4f;
private const float secondary_y = 0.7f;
public static readonly Color4 COLOUR_GRAY = OsuColour.Gray(0.4f);
private Box background = null!;
private Box box = null!;
private TrianglesV2 triangles = null!;
private OsuSpriteText countText = null!;
private OsuSpriteText skipText = null!;
private AspectContainer aspect = null!;
private FillFlowContainer chevrons = null!;
private Sample sampleConfirm = null!;
public readonly BindableInt SkippedCount = new BindableInt();
public readonly BindableInt RequiredCount = new BindableInt();
public readonly BindableBool InSkipPeriod = new BindableBool();
private readonly BindableBool clicked = new BindableBool();
[Resolved]
private OsuColour colours { get; set; } = null!;
public Button()
{
RelativeSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
sampleConfirm = audio.Samples.Get(@"UI/submit-select");
Children = new Drawable[]
{
background = new Box
{
Alpha = 0.2f,
Colour = Color4.Black,
RelativeSizeAxes = Axes.Both,
},
aspect = new AspectContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Y,
Height = 0.6f,
Masking = true,
CornerRadius = 15,
Children = new Drawable[]
{
box = new Box
{
RelativeSizeAxes = Axes.Both,
},
triangles = new TrianglesV2
{
RelativeSizeAxes = Axes.Both,
},
countText = new OsuSpriteText
{
Anchor = Anchor.TopCentre,
RelativePositionAxes = Axes.Y,
Y = 0.35f,
Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 24),
Origin = Anchor.Centre,
},
chevrons = new FillFlowContainer
{
Anchor = Anchor.TopCentre,
RelativePositionAxes = Axes.Y,
AutoSizeAxes = Axes.Both,
Origin = Anchor.Centre,
Direction = FillDirection.Horizontal,
Children = new[]
{
new SpriteIcon { Size = new Vector2(15), Shadow = true, Icon = FontAwesome.Solid.ChevronRight },
new SpriteIcon { Size = new Vector2(15), Shadow = true, Icon = FontAwesome.Solid.ChevronRight },
new SpriteIcon { Size = new Vector2(15), Shadow = true, Icon = FontAwesome.Solid.ChevronRight },
}
},
skipText = new OsuSpriteText
{
Anchor = Anchor.TopCentre,
RelativePositionAxes = Axes.Y,
Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 12),
Origin = Anchor.Centre,
Text = @"SKIP",
Y = secondary_y,
},
}
}
};
}
protected override void LoadComplete()
{
base.LoadComplete();
SkippedCount.BindValueChanged(_ => updateCount());
RequiredCount.BindValueChanged(_ => updateCount(), true);
InSkipPeriod.BindValueChanged(_ => updateEnabledState());
clicked.BindValueChanged(_ => updateEnabledState(), true);
Enabled.BindValueChanged(_ => updateColours(), true);
FinishTransforms(true);
}
private void updateEnabledState() => Enabled.Value = InSkipPeriod.Value && !clicked.Value;
private void updateChevronsSpacing()
{
if (SkippedCount.Value > 0 && RequiredCount.Value > 1)
chevrons.TransformSpacingTo(new Vector2(-5f), 500, Easing.OutQuint);
else
chevrons.TransformSpacingTo(IsHovered ? new Vector2(5f) : new Vector2(0f), 500, Easing.OutQuint);
}
private void updateCount()
{
if (SkippedCount.Value > 0 && RequiredCount.Value > 1)
{
countText.FadeIn(300, Easing.OutQuint);
countText.Text = $"{SkippedCount.Value} / {RequiredCount.Value}";
chevrons.ScaleTo(0.5f, 300, Easing.OutQuint)
.MoveTo(new Vector2(-11, secondary_y), 300, Easing.OutQuint);
skipText.MoveToX(11f, 300, Easing.OutQuint);
}
else
{
countText.FadeOut(300, Easing.OutQuint);
chevrons.ScaleTo(1f, 300, Easing.OutQuint)
.MoveTo(new Vector2(0, chevron_y), 300, Easing.OutQuint);
skipText.MoveToX(0f, 300, Easing.OutQuint);
}
updateChevronsSpacing();
updateColours();
}
private void updateColours()
{
if (!Enabled.Value)
{
box.FadeColour(COLOUR_GRAY, 500, Easing.OutQuint);
triangles.FadeColour(ColourInfo.GradientVertical(COLOUR_GRAY.Lighten(0.2f), COLOUR_GRAY), 500, Easing.OutQuint);
}
else
{
box.FadeColour(IsHovered ? colours.Orange3.Lighten(0.2f) : colours.Orange3, 500, Easing.OutQuint);
triangles.FadeColour(ColourInfo.GradientVertical(colours.Orange3.Lighten(0.2f), colours.Orange3), 500, Easing.OutQuint);
}
}
protected override bool OnHover(HoverEvent e)
{
if (Enabled.Value)
{
updateChevronsSpacing();
updateColours();
background.FadeTo(0.4f, 500, Easing.OutQuint);
}
return true;
}
protected override void OnHoverLost(HoverLostEvent e)
{
updateChevronsSpacing();
updateColours();
background.FadeTo(0.2f, 500, Easing.OutQuint);
base.OnHoverLost(e);
}
protected override bool OnMouseDown(MouseDownEvent e)
{
if (Enabled.Value)
aspect.ScaleTo(0.75f, 2000, Easing.OutQuint);
return base.OnMouseDown(e);
}
protected override void OnMouseUp(MouseUpEvent e)
{
if (Enabled.Value)
aspect.ScaleTo(1, 1000, Easing.OutElastic);
base.OnMouseUp(e);
}
protected override bool OnClick(ClickEvent e)
{
if (!Enabled.Value)
return false;
sampleConfirm.Play();
box.FlashColour(Color4.White, 500, Easing.OutQuint);
aspect.ScaleTo(1.2f, 2000, Easing.OutQuint);
base.OnClick(e);
clicked.Value = true;
return true;
}
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
countText.Scale = new Vector2(Math.Min(0.85f * aspect.DrawWidth / countText.DrawWidth, 1));
}
}
}
}