mirror of
https://github.com/ppy/osu
synced 2025-02-20 20:47:09 +00:00
Merge branch 'master' into multi-polling-request-refactor
This commit is contained in:
commit
08d347f10b
@ -40,6 +40,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
});
|
||||
|
||||
AddStep("add local player", () => createLeaderboardScore(playerScore, new User { Username = "You", Id = 3 }, true));
|
||||
AddStep("toggle expanded", () => leaderboard.Expanded.Value = !leaderboard.Expanded.Value);
|
||||
AddSliderStep("set player score", 50, 5000000, 1222333, v => playerScore.Value = v);
|
||||
}
|
||||
|
||||
@ -83,19 +84,38 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddStep("add frenzibyte", () => createRandomScore(new User { Username = "frenzibyte", Id = 14210502 }));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMaxHeight()
|
||||
{
|
||||
int playerNumber = 1;
|
||||
AddRepeatStep("add 3 other players", () => createRandomScore(new User { Username = $"Player {playerNumber++}" }), 3);
|
||||
checkHeight(4);
|
||||
|
||||
AddRepeatStep("add 4 other players", () => createRandomScore(new User { Username = $"Player {playerNumber++}" }), 4);
|
||||
checkHeight(8);
|
||||
|
||||
AddRepeatStep("add 4 other players", () => createRandomScore(new User { Username = $"Player {playerNumber++}" }), 4);
|
||||
checkHeight(8);
|
||||
|
||||
void checkHeight(int panelCount)
|
||||
=> AddAssert($"leaderboard height is {panelCount} panels high", () => leaderboard.DrawHeight == (GameplayLeaderboardScore.PANEL_HEIGHT + leaderboard.Spacing) * panelCount);
|
||||
}
|
||||
|
||||
private void createRandomScore(User user) => createLeaderboardScore(new BindableDouble(RNG.Next(0, 5_000_000)), user);
|
||||
|
||||
private void createLeaderboardScore(BindableDouble score, User user, bool isTracked = false)
|
||||
{
|
||||
var leaderboardScore = leaderboard.AddPlayer(user, isTracked);
|
||||
var leaderboardScore = leaderboard.Add(user, isTracked);
|
||||
leaderboardScore.TotalScore.BindTo(score);
|
||||
}
|
||||
|
||||
private class TestGameplayLeaderboard : GameplayLeaderboard
|
||||
{
|
||||
public float Spacing => Flow.Spacing.Y;
|
||||
|
||||
public bool CheckPositionByUsername(string username, int? expectedPosition)
|
||||
{
|
||||
var scoreItem = this.FirstOrDefault(i => i.User?.Username == username);
|
||||
var scoreItem = Flow.FirstOrDefault(i => i.User?.Username == username);
|
||||
|
||||
return scoreItem != null && scoreItem.ScorePosition == expectedPosition;
|
||||
}
|
||||
|
@ -0,0 +1,51 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public class TestSceneMultiplayerResults : ScreenTestScene
|
||||
{
|
||||
[Test]
|
||||
public void TestDisplayResults()
|
||||
{
|
||||
MultiplayerResultsScreen screen = null;
|
||||
|
||||
AddStep("show results screen", () =>
|
||||
{
|
||||
var rulesetInfo = new OsuRuleset().RulesetInfo;
|
||||
var beatmapInfo = CreateBeatmap(rulesetInfo).BeatmapInfo;
|
||||
|
||||
var score = new ScoreInfo
|
||||
{
|
||||
Rank = ScoreRank.B,
|
||||
TotalScore = 987654,
|
||||
Accuracy = 0.8,
|
||||
MaxCombo = 500,
|
||||
Combo = 250,
|
||||
Beatmap = beatmapInfo,
|
||||
User = new User { Username = "Test user" },
|
||||
Date = DateTimeOffset.Now,
|
||||
OnlineScoreID = 12345,
|
||||
Ruleset = rulesetInfo,
|
||||
};
|
||||
|
||||
PlaylistItem playlistItem = new PlaylistItem
|
||||
{
|
||||
BeatmapID = beatmapInfo.ID,
|
||||
};
|
||||
|
||||
Stack.Push(screen = new MultiplayerResultsScreen(score, 1, playlistItem));
|
||||
});
|
||||
|
||||
AddUntilStep("wait for loaded", () => screen.IsLoaded);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public class TestSceneMultiplayerTeamResults : ScreenTestScene
|
||||
{
|
||||
[TestCase(7483253, 1048576)]
|
||||
[TestCase(1048576, 7483253)]
|
||||
[TestCase(1048576, 1048576)]
|
||||
public void TestDisplayTeamResults(int team1Score, int team2Score)
|
||||
{
|
||||
MultiplayerResultsScreen screen = null;
|
||||
|
||||
AddStep("show results screen", () =>
|
||||
{
|
||||
var rulesetInfo = new OsuRuleset().RulesetInfo;
|
||||
var beatmapInfo = CreateBeatmap(rulesetInfo).BeatmapInfo;
|
||||
|
||||
var score = new ScoreInfo
|
||||
{
|
||||
Rank = ScoreRank.B,
|
||||
TotalScore = 987654,
|
||||
Accuracy = 0.8,
|
||||
MaxCombo = 500,
|
||||
Combo = 250,
|
||||
Beatmap = beatmapInfo,
|
||||
User = new User { Username = "Test user" },
|
||||
Date = DateTimeOffset.Now,
|
||||
OnlineScoreID = 12345,
|
||||
Ruleset = rulesetInfo,
|
||||
};
|
||||
|
||||
PlaylistItem playlistItem = new PlaylistItem
|
||||
{
|
||||
BeatmapID = beatmapInfo.ID,
|
||||
};
|
||||
|
||||
SortedDictionary<int, BindableInt> teamScores = new SortedDictionary<int, BindableInt>
|
||||
{
|
||||
{ 0, new BindableInt(team1Score) },
|
||||
{ 1, new BindableInt(team2Score) }
|
||||
};
|
||||
|
||||
Stack.Push(screen = new MultiplayerTeamResultsScreen(score, 1, playlistItem, teamScores));
|
||||
});
|
||||
|
||||
AddUntilStep("wait for loaded", () => screen.IsLoaded);
|
||||
}
|
||||
}
|
||||
}
|
@ -101,7 +101,7 @@ namespace osu.Game.Configuration
|
||||
SetDefault(OsuSetting.HitLighting, true);
|
||||
|
||||
SetDefault(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Always);
|
||||
SetDefault(OsuSetting.ShowProgressGraph, true);
|
||||
SetDefault(OsuSetting.ShowDifficultyGraph, true);
|
||||
SetDefault(OsuSetting.ShowHealthDisplayWhenCantFail, true);
|
||||
SetDefault(OsuSetting.FadePlayfieldWhenHealthLow, true);
|
||||
SetDefault(OsuSetting.KeyOverlay, false);
|
||||
@ -217,7 +217,7 @@ namespace osu.Game.Configuration
|
||||
AlwaysPlayFirstComboBreak,
|
||||
FloatingComments,
|
||||
HUDVisibilityMode,
|
||||
ShowProgressGraph,
|
||||
ShowDifficultyGraph,
|
||||
ShowHealthDisplayWhenCantFail,
|
||||
FadePlayfieldWhenHealthLow,
|
||||
MouseDisableButtons,
|
||||
|
24
osu.Game/Localisation/MultiplayerTeamResultsScreenStrings.cs
Normal file
24
osu.Game/Localisation/MultiplayerTeamResultsScreenStrings.cs
Normal file
@ -0,0 +1,24 @@
|
||||
// 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.Localisation;
|
||||
|
||||
namespace osu.Game.Localisation
|
||||
{
|
||||
public static class MultiplayerTeamResultsScreenStrings
|
||||
{
|
||||
private const string prefix = @"osu.Game.Resources.Localisation.MultiplayerTeamResultsScreen";
|
||||
|
||||
/// <summary>
|
||||
/// "Team {0} wins!"
|
||||
/// </summary>
|
||||
public static LocalisableString TeamWins(string winner) => new TranslatableString(getKey(@"team_wins"), @"Team {0} wins!", winner);
|
||||
|
||||
/// <summary>
|
||||
/// "The teams are tied!"
|
||||
/// </summary>
|
||||
public static LocalisableString TheTeamsAreTied => new TranslatableString(getKey(@"the_teams_are_tied"), @"The teams are tied!");
|
||||
|
||||
private static string getKey(string key) => $@"{prefix}:{key}";
|
||||
}
|
||||
}
|
@ -31,6 +31,15 @@ namespace osu.Game.Online.Multiplayer
|
||||
/// <param name="user">The user.</param>
|
||||
Task UserLeft(MultiplayerRoomUser user);
|
||||
|
||||
/// <summary>
|
||||
/// Signals that a user has been kicked from the room.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This will also be sent to the user that was kicked.
|
||||
/// </remarks>
|
||||
/// <param name="user">The user.</param>
|
||||
Task UserKicked(MultiplayerRoomUser user);
|
||||
|
||||
/// <summary>
|
||||
/// Signal that the host of the room has changed.
|
||||
/// </summary>
|
||||
|
@ -389,6 +389,18 @@ namespace osu.Game.Online.Multiplayer
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
Task IMultiplayerClient.UserKicked(MultiplayerRoomUser user)
|
||||
{
|
||||
if (LocalUser == null)
|
||||
return Task.CompletedTask;
|
||||
|
||||
if (user.Equals(LocalUser))
|
||||
LeaveRoom();
|
||||
|
||||
// TODO: also inform users of the kick operation.
|
||||
return ((IMultiplayerClient)this).UserLeft(user);
|
||||
}
|
||||
|
||||
Task IMultiplayerClient.HostChanged(int userId)
|
||||
{
|
||||
if (Room == null)
|
||||
|
@ -50,6 +50,7 @@ namespace osu.Game.Online.Multiplayer
|
||||
connection.On<MultiplayerRoomState>(nameof(IMultiplayerClient.RoomStateChanged), ((IMultiplayerClient)this).RoomStateChanged);
|
||||
connection.On<MultiplayerRoomUser>(nameof(IMultiplayerClient.UserJoined), ((IMultiplayerClient)this).UserJoined);
|
||||
connection.On<MultiplayerRoomUser>(nameof(IMultiplayerClient.UserLeft), ((IMultiplayerClient)this).UserLeft);
|
||||
connection.On<MultiplayerRoomUser>(nameof(IMultiplayerClient.UserKicked), ((IMultiplayerClient)this).UserKicked);
|
||||
connection.On<int>(nameof(IMultiplayerClient.HostChanged), ((IMultiplayerClient)this).HostChanged);
|
||||
connection.On<MultiplayerRoomSettings>(nameof(IMultiplayerClient.SettingsChanged), ((IMultiplayerClient)this).SettingsChanged);
|
||||
connection.On<int, MultiplayerUserState>(nameof(IMultiplayerClient.UserStateChanged), ((IMultiplayerClient)this).UserStateChanged);
|
||||
|
@ -78,10 +78,10 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
Direction = FillDirection.Horizontal,
|
||||
Children = new[]
|
||||
{
|
||||
length = new Statistic(FontAwesome.Regular.Clock, "Length") { Width = 0.25f },
|
||||
bpm = new Statistic(FontAwesome.Regular.Circle, "BPM") { Width = 0.25f },
|
||||
circleCount = new Statistic(FontAwesome.Regular.Circle, "Circle Count") { Width = 0.25f },
|
||||
sliderCount = new Statistic(FontAwesome.Regular.Circle, "Slider Count") { Width = 0.25f },
|
||||
length = new Statistic(BeatmapStatisticsIconType.Length, "Length") { Width = 0.25f },
|
||||
bpm = new Statistic(BeatmapStatisticsIconType.Bpm, "BPM") { Width = 0.25f },
|
||||
circleCount = new Statistic(BeatmapStatisticsIconType.Circles, "Circle Count") { Width = 0.25f },
|
||||
sliderCount = new Statistic(BeatmapStatisticsIconType.Sliders, "Slider Count") { Width = 0.25f },
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -104,7 +104,7 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
set => this.value.Text = value;
|
||||
}
|
||||
|
||||
public Statistic(IconUsage icon, string name)
|
||||
public Statistic(BeatmapStatisticsIconType icon, string name)
|
||||
{
|
||||
TooltipText = name;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
@ -133,8 +133,16 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.Centre,
|
||||
Icon = icon,
|
||||
Size = new Vector2(12),
|
||||
Icon = FontAwesome.Regular.Circle,
|
||||
Size = new Vector2(10),
|
||||
Rotation = 0,
|
||||
Colour = Color4Extensions.FromHex(@"f7dd55"),
|
||||
},
|
||||
new BeatmapStatisticIcon(icon)
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(10),
|
||||
Colour = Color4Extensions.FromHex(@"f7dd55"),
|
||||
Scale = new Vector2(0.8f),
|
||||
},
|
||||
|
@ -46,7 +46,7 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
|
||||
new SettingsCheckbox
|
||||
{
|
||||
LabelText = "Show difficulty graph on progress bar",
|
||||
Current = config.GetBindable<bool>(OsuSetting.ShowProgressGraph)
|
||||
Current = config.GetBindable<bool>(OsuSetting.ShowDifficultyGraph)
|
||||
},
|
||||
new SettingsCheckbox
|
||||
{
|
||||
|
@ -277,7 +277,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
isConnected.BindValueChanged(connected =>
|
||||
{
|
||||
if (!connected.NewValue)
|
||||
Schedule(this.Exit);
|
||||
handleRoomLost();
|
||||
}, true);
|
||||
|
||||
currentRoom.BindValueChanged(room =>
|
||||
@ -287,7 +287,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
// the room has gone away.
|
||||
// this could mean something happened during the join process, or an external connection issue occurred.
|
||||
// one specific scenario is where the underlying room is created, but the signalr server returns an error during the join process. this triggers a PartRoom operation (see https://github.com/ppy/osu/blob/7654df94f6f37b8382be7dfcb4f674e03bd35427/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs#L97)
|
||||
Schedule(this.Exit);
|
||||
handleRoomLost();
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
@ -451,9 +451,24 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
|
||||
private void onRoomUpdated()
|
||||
{
|
||||
// may happen if the client is kicked or otherwise removed from the room.
|
||||
if (client.Room == null)
|
||||
{
|
||||
handleRoomLost();
|
||||
return;
|
||||
}
|
||||
|
||||
Scheduler.AddOnce(UpdateMods);
|
||||
}
|
||||
|
||||
private void handleRoomLost() => Schedule(() =>
|
||||
{
|
||||
if (this.IsCurrentScreen())
|
||||
this.Exit();
|
||||
else
|
||||
ValidForResume = false;
|
||||
});
|
||||
|
||||
private void onLoadRequested()
|
||||
{
|
||||
if (BeatmapAvailability.Value.State != DownloadState.LocallyAvailable)
|
||||
|
@ -181,7 +181,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
protected override ResultsScreen CreateResults(ScoreInfo score)
|
||||
{
|
||||
Debug.Assert(RoomId.Value != null);
|
||||
return new MultiplayerResultsScreen(score, RoomId.Value.Value, PlaylistItem);
|
||||
return leaderboard.TeamScores.Count == 2
|
||||
? new MultiplayerTeamResultsScreen(score, RoomId.Value.Value, PlaylistItem, leaderboard.TeamScores)
|
||||
: new MultiplayerResultsScreen(score, RoomId.Value.Value, PlaylistItem);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
|
@ -0,0 +1,152 @@
|
||||
// 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 System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Effects;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Localisation;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
{
|
||||
public class MultiplayerTeamResultsScreen : MultiplayerResultsScreen
|
||||
{
|
||||
private readonly SortedDictionary<int, BindableInt> teamScores;
|
||||
|
||||
private Container winnerBackground;
|
||||
private Drawable winnerText;
|
||||
|
||||
public MultiplayerTeamResultsScreen(ScoreInfo score, long roomId, PlaylistItem playlistItem, SortedDictionary<int, BindableInt> teamScores)
|
||||
: base(score, roomId, playlistItem)
|
||||
{
|
||||
if (teamScores.Count != 2)
|
||||
throw new NotSupportedException(@"This screen currently only supports 2 teams");
|
||||
|
||||
this.teamScores = teamScores;
|
||||
}
|
||||
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; }
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
const float winner_background_half_height = 250;
|
||||
|
||||
VerticalScrollContent.Anchor = VerticalScrollContent.Origin = Anchor.TopCentre;
|
||||
VerticalScrollContent.Scale = new Vector2(0.9f);
|
||||
VerticalScrollContent.Y = 75;
|
||||
|
||||
var redScore = teamScores.First().Value;
|
||||
var blueScore = teamScores.Last().Value;
|
||||
|
||||
LocalisableString winner;
|
||||
Colour4 winnerColour;
|
||||
|
||||
int comparison = redScore.Value.CompareTo(blueScore.Value);
|
||||
|
||||
if (comparison < 0)
|
||||
{
|
||||
// team name should eventually be coming from the multiplayer match state.
|
||||
winner = MultiplayerTeamResultsScreenStrings.TeamWins(@"Blue");
|
||||
winnerColour = colours.TeamColourBlue;
|
||||
}
|
||||
else if (comparison > 0)
|
||||
{
|
||||
// team name should eventually be coming from the multiplayer match state.
|
||||
winner = MultiplayerTeamResultsScreenStrings.TeamWins(@"Red");
|
||||
winnerColour = colours.TeamColourRed;
|
||||
}
|
||||
else
|
||||
{
|
||||
winner = MultiplayerTeamResultsScreenStrings.TheTeamsAreTied;
|
||||
winnerColour = Colour4.White.Opacity(0.5f);
|
||||
}
|
||||
|
||||
AddRangeInternal(new Drawable[]
|
||||
{
|
||||
new MatchScoreDisplay
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Team1Score = { BindTarget = redScore },
|
||||
Team2Score = { BindTarget = blueScore },
|
||||
},
|
||||
winnerBackground = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Alpha = 0,
|
||||
Children = new[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = winner_background_half_height,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
Colour = ColourInfo.GradientVertical(Colour4.Black.Opacity(0), Colour4.Black.Opacity(0.4f))
|
||||
},
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = winner_background_half_height,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Colour = ColourInfo.GradientVertical(Colour4.Black.Opacity(0.4f), Colour4.Black.Opacity(0))
|
||||
}
|
||||
}
|
||||
},
|
||||
(winnerText = new OsuSpriteText
|
||||
{
|
||||
Alpha = 0,
|
||||
Font = OsuFont.Torus.With(size: 80, weight: FontWeight.Bold),
|
||||
Text = winner,
|
||||
Blending = BlendingParameters.Additive
|
||||
}).WithEffect(new GlowEffect
|
||||
{
|
||||
Colour = winnerColour,
|
||||
}).With(e =>
|
||||
{
|
||||
e.Anchor = Anchor.Centre;
|
||||
e.Origin = Anchor.Centre;
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
using (BeginDelayedSequence(300))
|
||||
{
|
||||
const double fade_in_duration = 600;
|
||||
|
||||
winnerText.FadeInFromZero(fade_in_duration, Easing.InQuint);
|
||||
winnerBackground.FadeInFromZero(fade_in_duration, Easing.InQuint);
|
||||
|
||||
winnerText
|
||||
.ScaleTo(10)
|
||||
.ScaleTo(1, 600, Easing.InQuad)
|
||||
.Then()
|
||||
.ScaleTo(1.02f, 1600, Easing.OutQuint)
|
||||
.FadeOut(5000, Easing.InQuad);
|
||||
winnerBackground.Delay(2200).FadeOut(2000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -6,29 +6,58 @@ using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Caching;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Users;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Play.HUD
|
||||
{
|
||||
public class GameplayLeaderboard : FillFlowContainer<GameplayLeaderboardScore>
|
||||
public class GameplayLeaderboard : CompositeDrawable
|
||||
{
|
||||
private readonly int maxPanels;
|
||||
private readonly Cached sorting = new Cached();
|
||||
|
||||
public Bindable<bool> Expanded = new Bindable<bool>();
|
||||
|
||||
public GameplayLeaderboard()
|
||||
protected readonly FillFlowContainer<GameplayLeaderboardScore> Flow;
|
||||
|
||||
private bool requiresScroll;
|
||||
private readonly OsuScrollContainer scroll;
|
||||
|
||||
private GameplayLeaderboardScore trackedScore;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new leaderboard.
|
||||
/// </summary>
|
||||
/// <param name="maxPanels">The maximum panels to show at once. Defines the maximum height of this component.</param>
|
||||
public GameplayLeaderboard(int maxPanels = 8)
|
||||
{
|
||||
this.maxPanels = maxPanels;
|
||||
|
||||
Width = GameplayLeaderboardScore.EXTENDED_WIDTH + GameplayLeaderboardScore.SHEAR_WIDTH;
|
||||
|
||||
Direction = FillDirection.Vertical;
|
||||
|
||||
Spacing = new Vector2(2.5f);
|
||||
|
||||
LayoutDuration = 250;
|
||||
LayoutEasing = Easing.OutQuint;
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
scroll = new InputDisabledScrollContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = Flow = new FillFlowContainer<GameplayLeaderboardScore>
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
X = GameplayLeaderboardScore.SHEAR_WIDTH,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(2.5f),
|
||||
LayoutDuration = 450,
|
||||
LayoutEasing = Easing.OutQuint,
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
@ -46,26 +75,87 @@ namespace osu.Game.Screens.Play.HUD
|
||||
/// Whether the player should be tracked on the leaderboard.
|
||||
/// Set to <c>true</c> for the local player or a player whose replay is currently being played.
|
||||
/// </param>
|
||||
public ILeaderboardScore AddPlayer([CanBeNull] User user, bool isTracked)
|
||||
public ILeaderboardScore Add([CanBeNull] User user, bool isTracked)
|
||||
{
|
||||
var drawable = CreateLeaderboardScoreDrawable(user, isTracked);
|
||||
|
||||
if (isTracked)
|
||||
{
|
||||
if (trackedScore != null)
|
||||
throw new InvalidOperationException("Cannot track more than one score.");
|
||||
|
||||
trackedScore = drawable;
|
||||
}
|
||||
|
||||
drawable.Expanded.BindTo(Expanded);
|
||||
|
||||
base.Add(drawable);
|
||||
Flow.Add(drawable);
|
||||
drawable.TotalScore.BindValueChanged(_ => sorting.Invalidate(), true);
|
||||
|
||||
Height = Count * (GameplayLeaderboardScore.PANEL_HEIGHT + Spacing.Y);
|
||||
int displayCount = Math.Min(Flow.Count, maxPanels);
|
||||
Height = displayCount * (GameplayLeaderboardScore.PANEL_HEIGHT + Flow.Spacing.Y);
|
||||
requiresScroll = displayCount != Flow.Count;
|
||||
|
||||
return drawable;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
Flow.Clear();
|
||||
trackedScore = null;
|
||||
scroll.ScrollToStart(false);
|
||||
}
|
||||
|
||||
protected virtual GameplayLeaderboardScore CreateLeaderboardScoreDrawable(User user, bool isTracked) =>
|
||||
new GameplayLeaderboardScore(user, isTracked);
|
||||
|
||||
public sealed override void Add(GameplayLeaderboardScore drawable)
|
||||
protected override void Update()
|
||||
{
|
||||
throw new NotSupportedException($"Use {nameof(AddPlayer)} instead.");
|
||||
base.Update();
|
||||
|
||||
if (requiresScroll && trackedScore != null)
|
||||
{
|
||||
float scrollTarget = scroll.GetChildPosInContent(trackedScore) + trackedScore.DrawHeight / 2 - scroll.DrawHeight / 2;
|
||||
scroll.ScrollTo(scrollTarget, false);
|
||||
}
|
||||
|
||||
const float panel_height = GameplayLeaderboardScore.PANEL_HEIGHT;
|
||||
|
||||
float fadeBottom = scroll.Current + scroll.DrawHeight;
|
||||
float fadeTop = scroll.Current + panel_height;
|
||||
|
||||
if (scroll.Current <= 0) fadeTop -= panel_height;
|
||||
if (!scroll.IsScrolledToEnd()) fadeBottom -= panel_height;
|
||||
|
||||
// logic is mostly shared with Leaderboard, copied here for simplicity.
|
||||
foreach (var c in Flow.Children)
|
||||
{
|
||||
float topY = c.ToSpaceOfOtherDrawable(Vector2.Zero, Flow).Y;
|
||||
float bottomY = topY + panel_height;
|
||||
|
||||
bool requireTopFade = requiresScroll && topY <= fadeTop;
|
||||
bool requireBottomFade = requiresScroll && bottomY >= fadeBottom;
|
||||
|
||||
if (!requireTopFade && !requireBottomFade)
|
||||
c.Colour = Color4.White;
|
||||
else if (topY > fadeBottom + panel_height || bottomY < fadeTop - panel_height)
|
||||
c.Colour = Color4.Transparent;
|
||||
else
|
||||
{
|
||||
if (requireBottomFade)
|
||||
{
|
||||
c.Colour = ColourInfo.GradientVertical(
|
||||
Color4.White.Opacity(Math.Min(1 - (topY - fadeBottom) / panel_height, 1)),
|
||||
Color4.White.Opacity(Math.Min(1 - (bottomY - fadeBottom) / panel_height, 1)));
|
||||
}
|
||||
else if (requiresScroll)
|
||||
{
|
||||
c.Colour = ColourInfo.GradientVertical(
|
||||
Color4.White.Opacity(Math.Min(1 - (fadeTop - topY) / panel_height, 1)),
|
||||
Color4.White.Opacity(Math.Min(1 - (fadeTop - bottomY) / panel_height, 1)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void sort()
|
||||
@ -73,15 +163,26 @@ namespace osu.Game.Screens.Play.HUD
|
||||
if (sorting.IsValid)
|
||||
return;
|
||||
|
||||
var orderedByScore = this.OrderByDescending(i => i.TotalScore.Value).ToList();
|
||||
var orderedByScore = Flow.OrderByDescending(i => i.TotalScore.Value).ToList();
|
||||
|
||||
for (int i = 0; i < Count; i++)
|
||||
for (int i = 0; i < Flow.Count; i++)
|
||||
{
|
||||
SetLayoutPosition(orderedByScore[i], i);
|
||||
Flow.SetLayoutPosition(orderedByScore[i], i);
|
||||
orderedByScore[i].ScorePosition = i + 1;
|
||||
}
|
||||
|
||||
sorting.Validate();
|
||||
}
|
||||
|
||||
private class InputDisabledScrollContainer : OsuScrollContainer
|
||||
{
|
||||
public InputDisabledScrollContainer()
|
||||
{
|
||||
ScrollbarVisible = false;
|
||||
}
|
||||
|
||||
public override bool HandlePositionalInput => false;
|
||||
public override bool HandleNonPositionalInput => false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -81,7 +81,10 @@ namespace osu.Game.Screens.Play.HUD
|
||||
[CanBeNull]
|
||||
public User User { get; }
|
||||
|
||||
private readonly bool trackedPlayer;
|
||||
/// <summary>
|
||||
/// Whether this score is the local user or a replay player (and should be focused / always visible).
|
||||
/// </summary>
|
||||
public readonly bool Tracked;
|
||||
|
||||
private Container mainFillContainer;
|
||||
|
||||
@ -97,11 +100,11 @@ namespace osu.Game.Screens.Play.HUD
|
||||
/// Creates a new <see cref="GameplayLeaderboardScore"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The score's player.</param>
|
||||
/// <param name="trackedPlayer">Whether the player is the local user or a replay player.</param>
|
||||
public GameplayLeaderboardScore([CanBeNull] User user, bool trackedPlayer)
|
||||
/// <param name="tracked">Whether the player is the local user or a replay player.</param>
|
||||
public GameplayLeaderboardScore([CanBeNull] User user, bool tracked)
|
||||
{
|
||||
User = user;
|
||||
this.trackedPlayer = trackedPlayer;
|
||||
Tracked = tracked;
|
||||
|
||||
AutoSizeAxes = Axes.X;
|
||||
Height = PANEL_HEIGHT;
|
||||
@ -338,7 +341,7 @@ namespace osu.Game.Screens.Play.HUD
|
||||
panelColour = BackgroundColour ?? Color4Extensions.FromHex("7fcc33");
|
||||
textColour = TextColour ?? Color4.White;
|
||||
}
|
||||
else if (trackedPlayer)
|
||||
else if (Tracked)
|
||||
{
|
||||
widthExtension = true;
|
||||
panelColour = BackgroundColour ?? Color4Extensions.FromHex("ffd966");
|
||||
|
@ -7,6 +7,7 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
@ -104,7 +105,7 @@ namespace osu.Game.Screens.Play.HUD
|
||||
base.LoadComplete();
|
||||
|
||||
Team1Score.BindValueChanged(_ => updateScores());
|
||||
Team2Score.BindValueChanged(_ => updateScores());
|
||||
Team2Score.BindValueChanged(_ => updateScores(), true);
|
||||
}
|
||||
|
||||
private void updateScores()
|
||||
@ -171,6 +172,8 @@ namespace osu.Game.Screens.Play.HUD
|
||||
=> displayedSpriteText.Font = winning
|
||||
? OsuFont.Torus.With(weight: FontWeight.Bold, size: font_size, fixedWidth: true)
|
||||
: OsuFont.Torus.With(weight: FontWeight.Regular, size: font_size * 0.8f, fixedWidth: true);
|
||||
|
||||
protected override LocalisableString FormatCount(double count) => count.ToLocalisableString(@"N0");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -85,7 +85,7 @@ namespace osu.Game.Screens.Play.HUD
|
||||
|
||||
var trackedUser = UserScores[user.Id];
|
||||
|
||||
var leaderboardScore = AddPlayer(user, user.Id == api.LocalUser.Value.Id);
|
||||
var leaderboardScore = Add(user, user.Id == api.LocalUser.Value.Id);
|
||||
leaderboardScore.Accuracy.BindTo(trackedUser.Accuracy);
|
||||
leaderboardScore.TotalScore.BindTo(trackedUser.Score);
|
||||
leaderboardScore.Combo.BindTo(trackedUser.CurrentCombo);
|
||||
@ -184,7 +184,7 @@ namespace osu.Game.Screens.Play.HUD
|
||||
continue;
|
||||
|
||||
if (TeamScores.TryGetValue(u.Team.Value, out var team))
|
||||
team.Value += (int)u.Score.Value;
|
||||
team.Value += (int)Math.Round(u.Score.Value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -125,7 +125,7 @@ namespace osu.Game.Screens.Play
|
||||
Objects = drawableRuleset.Objects;
|
||||
}
|
||||
|
||||
config.BindWith(OsuSetting.ShowProgressGraph, ShowGraph);
|
||||
config.BindWith(OsuSetting.ShowDifficultyGraph, ShowGraph);
|
||||
|
||||
graph.FillColour = bar.FillColour = colours.BlueLighter;
|
||||
}
|
||||
|
@ -40,6 +40,8 @@ namespace osu.Game.Screens.Ranking
|
||||
|
||||
protected ScorePanelList ScorePanelList { get; private set; }
|
||||
|
||||
protected VerticalScrollContainer VerticalScrollContent { get; private set; }
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
private Player player { get; set; }
|
||||
|
||||
@ -77,7 +79,7 @@ namespace osu.Game.Screens.Ranking
|
||||
{
|
||||
new Drawable[]
|
||||
{
|
||||
new VerticalScrollContainer
|
||||
VerticalScrollContent = new VerticalScrollContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
ScrollbarVisible = false,
|
||||
@ -343,7 +345,7 @@ namespace osu.Game.Screens.Ranking
|
||||
{
|
||||
}
|
||||
|
||||
private class VerticalScrollContainer : OsuScrollContainer
|
||||
protected class VerticalScrollContainer : OsuScrollContainer
|
||||
{
|
||||
protected override Container<Drawable> Content => content;
|
||||
|
||||
@ -351,6 +353,8 @@ namespace osu.Game.Screens.Ranking
|
||||
|
||||
public VerticalScrollContainer()
|
||||
{
|
||||
Masking = false;
|
||||
|
||||
base.Content.Add(content = new Container { RelativeSizeAxes = Axes.X });
|
||||
}
|
||||
|
||||
|
@ -35,6 +35,7 @@ namespace osu.Game.Screens.Select
|
||||
{
|
||||
public class BeatmapInfoWedge : VisibilityContainer
|
||||
{
|
||||
public const float BORDER_THICKNESS = 2.5f;
|
||||
private const float shear_width = 36.75f;
|
||||
|
||||
private static readonly Vector2 wedged_container_shear = new Vector2(shear_width / SongSelect.WEDGE_HEIGHT, 0);
|
||||
@ -59,7 +60,7 @@ namespace osu.Game.Screens.Select
|
||||
Shear = wedged_container_shear;
|
||||
Masking = true;
|
||||
BorderColour = new Color4(221, 255, 255, 255);
|
||||
BorderThickness = 2.5f;
|
||||
BorderThickness = BORDER_THICKNESS;
|
||||
Alpha = 0;
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
|
@ -203,6 +203,7 @@ namespace osu.Game.Screens.Select
|
||||
Margin = new MarginPadding
|
||||
{
|
||||
Right = left_area_padding,
|
||||
Left = -BeatmapInfoWedge.BORDER_THICKNESS, // Hide the left border
|
||||
},
|
||||
},
|
||||
new Container
|
||||
|
@ -178,7 +178,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
Debug.Assert(Room != null);
|
||||
|
||||
return ((IMultiplayerClient)this).UserLeft(Room.Users.Single(u => u.UserID == userId));
|
||||
return ((IMultiplayerClient)this).UserKicked(Room.Users.Single(u => u.UserID == userId));
|
||||
}
|
||||
|
||||
public override async Task ChangeSettings(MultiplayerRoomSettings settings)
|
||||
|
Loading…
Reference in New Issue
Block a user