Merge branch 'master' into multi-polling-request-refactor

This commit is contained in:
smoogipoo 2021-08-16 13:12:08 +09:00
commit 08d347f10b
22 changed files with 512 additions and 44 deletions

View File

@ -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;
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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,

View 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}";
}
}

View File

@ -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>

View File

@ -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)

View File

@ -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);

View File

@ -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),
},

View File

@ -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
{

View File

@ -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)

View File

@ -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)

View File

@ -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);
}
}
}
}

View File

@ -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;
}
}
}

View File

@ -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");

View File

@ -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");
}
}
}

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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 });
}

View File

@ -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
{

View File

@ -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

View File

@ -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)