1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-26 13:32:07 +08:00

Convert gameplay leaderboard to skinnable component

This PR converts the leaderboard into a full-fledged skinnable component
that can be manipulated by users at will.

Notably, this finally allows https://github.com/ppy/osu/issues/20422 to
be fixed - although it's a very mixed bag, for several reasons:

- Because of taiko players' refusal to see reason^W^W^W^Winsistence on
  keeping stable behaviours related to aspect ratio treatment, I have to
  assume the worst case scenario, which means than on typical
  resolutions like 16:9 (or even worse, 4:3), the leaderboard will
  likely not occupy as much vertical space as it probably could.

- Additionally, there's the problem of where to put the spectator list.
  I settled on putting it to the right of the leaderboard, but that's
  kind of janky, because the leaderboard sometimes collapses and
  sometimes fully hides, leading to a very awkward space left behind. If
  we had the capability to anchor elements to other elements, maybe this
  could be resolved, but for now, I'm not sure what to do. I think if
  some users are bothered by it they can move it where they want it. Or
  delete it.
This commit is contained in:
Bartłomiej Dach
2025-04-22 14:49:45 +02:00
Unverified
parent 33f45b0a6d
commit 59b826c321
19 changed files with 422 additions and 166 deletions
@@ -49,6 +49,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy
{
var keyCounter = container.OfType<LegacyKeyCounterDisplay>().FirstOrDefault();
var spectatorList = container.OfType<SpectatorList>().FirstOrDefault();
var leaderboard = container.OfType<DrawableGameplayLeaderboard>().FirstOrDefault();
if (keyCounter != null)
{
@@ -64,12 +65,20 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy
spectatorList.Origin = Anchor.BottomLeft;
spectatorList.Position = new Vector2(10, -10);
}
if (leaderboard != null)
{
leaderboard.Anchor = Anchor.CentreLeft;
leaderboard.Origin = Anchor.CentreLeft;
leaderboard.X = 10;
}
})
{
Children = new Drawable[]
{
new LegacyKeyCounterDisplay(),
new SpectatorList(),
new DrawableGameplayLeaderboard(),
}
};
}
@@ -40,9 +40,13 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
case GlobalSkinnableContainers.MainHUDComponents:
return new DefaultSkinComponentsContainer(container =>
{
var leaderboard = container.OfType<DrawableGameplayLeaderboard>().FirstOrDefault();
var combo = container.ChildrenOfType<ArgonManiaComboCounter>().FirstOrDefault();
var spectatorList = container.OfType<SpectatorList>().FirstOrDefault();
if (leaderboard != null)
leaderboard.Position = new Vector2(36, 115);
if (combo != null)
{
combo.ShowLabel.Value = false;
@@ -55,6 +59,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
spectatorList.Position = new Vector2(36, -66);
})
{
new DrawableGameplayLeaderboard(),
new ArgonManiaComboCounter(),
new SpectatorList
{
@@ -98,6 +98,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
{
var combo = container.ChildrenOfType<LegacyManiaComboCounter>().FirstOrDefault();
var spectatorList = container.OfType<SpectatorList>().FirstOrDefault();
var leaderboard = container.OfType<DrawableGameplayLeaderboard>().FirstOrDefault();
if (combo != null)
{
@@ -112,10 +113,18 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
spectatorList.Origin = Anchor.BottomLeft;
spectatorList.Position = new Vector2(10, -10);
}
if (leaderboard != null)
{
leaderboard.Anchor = Anchor.CentreLeft;
leaderboard.Origin = Anchor.CentreLeft;
leaderboard.X = 10;
}
})
{
new LegacyManiaComboCounter(),
new SpectatorList(),
new DrawableGameplayLeaderboard(),
};
}
@@ -72,6 +72,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
var combo = container.OfType<LegacyDefaultComboCounter>().FirstOrDefault();
var spectatorList = container.OfType<SpectatorList>().FirstOrDefault();
var leaderboard = container.OfType<DrawableGameplayLeaderboard>().FirstOrDefault();
Vector2 pos = new Vector2();
@@ -89,6 +90,16 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
spectatorList.Anchor = Anchor.BottomLeft;
spectatorList.Origin = Anchor.BottomLeft;
spectatorList.Position = pos;
// maximum height of the spectator list is around ~172 units
pos += new Vector2(0, -185);
}
if (leaderboard != null)
{
leaderboard.Anchor = Anchor.BottomLeft;
leaderboard.Origin = Anchor.BottomLeft;
leaderboard.Position = pos;
}
})
{
@@ -97,6 +108,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
new LegacyDefaultComboCounter(),
new LegacyKeyCounterDisplay(),
new SpectatorList(),
new DrawableGameplayLeaderboard(),
}
};
}
@@ -1,9 +1,12 @@
// 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.Linq;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Play.HUD;
using osu.Game.Skinning;
using osuTK;
namespace osu.Game.Rulesets.Taiko.Skinning.Argon
{
@@ -18,6 +21,59 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Argon
{
switch (lookup)
{
case GlobalSkinnableContainerLookup containerLookup:
// Only handle per ruleset defaults here.
if (containerLookup.Ruleset == null)
return base.GetDrawableComponent(lookup);
switch (containerLookup.Lookup)
{
case GlobalSkinnableContainers.MainHUDComponents:
return new DefaultSkinComponentsContainer(container =>
{
var leaderboard = container.OfType<DrawableGameplayLeaderboard>().FirstOrDefault();
var comboCounter = container.OfType<ArgonComboCounter>().FirstOrDefault();
var spectatorList = container.OfType<SpectatorList>().FirstOrDefault();
if (leaderboard != null)
{
leaderboard.Anchor = leaderboard.Origin = Anchor.BottomLeft;
leaderboard.Position = new Vector2(36, -140);
leaderboard.Height = 140;
}
if (comboCounter != null)
comboCounter.Position = new Vector2(36, -66);
if (spectatorList != null)
{
spectatorList.Position = new Vector2(320, -280);
spectatorList.Anchor = Anchor.BottomLeft;
spectatorList.Origin = Anchor.TopLeft;
}
})
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
new DrawableGameplayLeaderboard(),
new ArgonComboCounter
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Scale = new Vector2(1.3f),
},
new SpectatorList
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
}
},
};
}
return null;
case SkinComponentLookup<HitResult> resultComponent:
// This should eventually be moved to a skin setting, when supported.
if (Skin is ArgonProSkin && resultComponent.Component >= HitResult.Great)
@@ -0,0 +1,76 @@
// 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.Linq;
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Screens.Play.HUD;
using osu.Game.Skinning;
using osuTK;
namespace osu.Game.Rulesets.Taiko.Skinning.Default
{
public class TaikoTrianglesSkinTransformer : SkinTransformer
{
public TaikoTrianglesSkinTransformer(ISkin skin)
: base(skin)
{
}
public override Drawable? GetDrawableComponent(ISkinComponentLookup lookup)
{
switch (lookup)
{
case GlobalSkinnableContainerLookup containerLookup:
{
// Only handle per ruleset defaults here.
if (containerLookup.Ruleset == null)
return base.GetDrawableComponent(lookup);
switch (containerLookup.Lookup)
{
case GlobalSkinnableContainers.MainHUDComponents:
return new DefaultSkinComponentsContainer(container =>
{
var leaderboard = container.OfType<DrawableGameplayLeaderboard>().FirstOrDefault();
var spectatorList = container.OfType<SpectatorList>().FirstOrDefault();
if (leaderboard != null)
{
leaderboard.Position = new Vector2(40, -100);
leaderboard.Height = 180;
leaderboard.Anchor = Anchor.BottomLeft;
leaderboard.Origin = Anchor.BottomLeft;
}
if (spectatorList != null)
{
spectatorList.HeaderFont.Value = Typeface.Venera;
spectatorList.HeaderColour.Value = new OsuColour().BlueLighter;
spectatorList.Anchor = Anchor.BottomLeft;
spectatorList.Origin = Anchor.TopLeft;
spectatorList.Position = new Vector2(320, -280);
}
})
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
new DrawableGameplayLeaderboard(),
new SpectatorList
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
}
},
};
}
return null;
}
}
return base.GetDrawableComponent(lookup);
}
}
}
@@ -3,12 +3,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Audio.Sample;
using osu.Framework.Graphics;
using osu.Game.Audio;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.UI;
using osu.Game.Screens.Play.HUD;
using osu.Game.Skinning;
using osuTK;
namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
{
@@ -29,119 +32,180 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
public override Drawable? GetDrawableComponent(ISkinComponentLookup lookup)
{
if (lookup is SkinComponentLookup<HitResult>)
switch (lookup)
{
// if a taiko skin is providing explosion sprites, hide the judgements completely
if (hasExplosion.Value)
return Drawable.Empty().With(d => d.Expire());
}
if (lookup is TaikoSkinComponentLookup taikoComponent)
{
switch (taikoComponent.Component)
case GlobalSkinnableContainerLookup containerLookup:
{
case TaikoSkinComponents.DrumRollBody:
if (GetTexture("taiko-roll-middle") != null)
return new LegacyDrumRoll();
// Modifications for global components.
if (containerLookup.Ruleset == null)
return base.GetDrawableComponent(lookup);
// we don't have enough assets to display these components (this is especially the case on a "beatmap" skin).
if (!IsProvidingLegacyResources)
return null;
case TaikoSkinComponents.InputDrum:
if (hasBarLeft)
return new LegacyInputDrum();
switch (containerLookup.Lookup)
{
case GlobalSkinnableContainers.MainHUDComponents:
return new DefaultSkinComponentsContainer(container =>
{
var combo = container.OfType<LegacyDefaultComboCounter>().FirstOrDefault();
var spectatorList = container.OfType<SpectatorList>().FirstOrDefault();
var leaderboard = container.OfType<DrawableGameplayLeaderboard>().FirstOrDefault();
return null;
Vector2 pos = new Vector2();
case TaikoSkinComponents.DrumSamplePlayer:
return null;
if (combo != null)
{
combo.Anchor = Anchor.BottomLeft;
combo.Origin = Anchor.BottomLeft;
combo.Scale = new Vector2(1.28f);
case TaikoSkinComponents.CentreHit:
case TaikoSkinComponents.RimHit:
if (hasHitCircle)
return new LegacyHit(taikoComponent.Component);
pos += new Vector2(10, -(combo.DrawHeight * 1.56f + 20) * combo.Scale.X);
}
return null;
if (leaderboard != null)
{
leaderboard.Anchor = Anchor.BottomLeft;
leaderboard.Origin = Anchor.BottomLeft;
leaderboard.Position = pos;
leaderboard.Height = 170;
pos += new Vector2(10 + leaderboard.Width, -leaderboard.Height);
}
case TaikoSkinComponents.DrumRollTick:
return this.GetAnimation("sliderscorepoint", false, false);
if (spectatorList != null)
{
spectatorList.Anchor = Anchor.BottomLeft;
spectatorList.Origin = Anchor.TopLeft;
spectatorList.Position = pos;
}
})
{
new LegacyDefaultComboCounter(),
new SpectatorList(),
new DrawableGameplayLeaderboard(),
};
}
case TaikoSkinComponents.Swell:
if (GetTexture("spinner-circle") != null)
return new LegacySwell();
return null;
}
return null;
case SkinComponentLookup<HitResult>:
{
// if a taiko skin is providing explosion sprites, hide the judgements completely
if (hasExplosion.Value)
return Drawable.Empty().With(d => d.Expire());
case TaikoSkinComponents.HitTarget:
if (GetTexture("taikobigcircle") != null)
return new TaikoLegacyHitTarget();
break;
}
return null;
case TaikoSkinComponentLookup taikoComponent:
{
switch (taikoComponent.Component)
{
case TaikoSkinComponents.DrumRollBody:
if (GetTexture("taiko-roll-middle") != null)
return new LegacyDrumRoll();
case TaikoSkinComponents.PlayfieldBackgroundRight:
if (GetTexture("taiko-bar-right") != null)
return new TaikoLegacyPlayfieldBackgroundRight();
return null;
return null;
case TaikoSkinComponents.InputDrum:
if (hasBarLeft)
return new LegacyInputDrum();
case TaikoSkinComponents.PlayfieldBackgroundLeft:
// This is displayed inside LegacyInputDrum. It is required to be there for layout purposes (can be seen on legacy skins).
if (GetTexture("taiko-bar-right") != null)
return Drawable.Empty();
return null;
return null;
case TaikoSkinComponents.DrumSamplePlayer:
return null;
case TaikoSkinComponents.BarLine:
if (GetTexture("taiko-barline") != null)
return new LegacyBarLine();
case TaikoSkinComponents.CentreHit:
case TaikoSkinComponents.RimHit:
if (hasHitCircle)
return new LegacyHit(taikoComponent.Component);
return null;
return null;
case TaikoSkinComponents.TaikoExplosionMiss:
var missSprite = this.GetAnimation(getHitName(taikoComponent.Component), true, false);
if (missSprite != null)
return new LegacyHitExplosion(missSprite);
case TaikoSkinComponents.DrumRollTick:
return this.GetAnimation("sliderscorepoint", false, false);
return null;
case TaikoSkinComponents.Swell:
if (GetTexture("spinner-circle") != null)
return new LegacySwell();
case TaikoSkinComponents.TaikoExplosionOk:
case TaikoSkinComponents.TaikoExplosionGreat:
string hitName = getHitName(taikoComponent.Component);
var hitSprite = this.GetAnimation(hitName, true, false);
return null;
if (hitSprite != null)
{
var strongHitSprite = this.GetAnimation($"{hitName}k", true, false);
case TaikoSkinComponents.HitTarget:
if (GetTexture("taikobigcircle") != null)
return new TaikoLegacyHitTarget();
return new LegacyHitExplosion(hitSprite, strongHitSprite);
}
return null;
return null;
case TaikoSkinComponents.PlayfieldBackgroundRight:
if (GetTexture("taiko-bar-right") != null)
return new TaikoLegacyPlayfieldBackgroundRight();
case TaikoSkinComponents.TaikoExplosionKiai:
// suppress the default kiai explosion if the skin brings its own sprites.
// the drawable needs to expire as soon as possible to avoid accumulating empty drawables on the playfield.
if (hasExplosion.Value)
return Drawable.Empty().With(d => d.Expire());
return null;
return null;
case TaikoSkinComponents.PlayfieldBackgroundLeft:
// This is displayed inside LegacyInputDrum. It is required to be there for layout purposes (can be seen on legacy skins).
if (GetTexture("taiko-bar-right") != null)
return Drawable.Empty();
case TaikoSkinComponents.Scroller:
if (GetTexture("taiko-slider") != null)
return new LegacyTaikoScroller();
return null;
return null;
case TaikoSkinComponents.BarLine:
if (GetTexture("taiko-barline") != null)
return new LegacyBarLine();
case TaikoSkinComponents.Mascot:
return new DrawableTaikoMascot();
return null;
case TaikoSkinComponents.KiaiGlow:
if (GetTexture("taiko-glow") != null)
return new LegacyKiaiGlow();
case TaikoSkinComponents.TaikoExplosionMiss:
var missSprite = this.GetAnimation(getHitName(taikoComponent.Component), true, false);
if (missSprite != null)
return new LegacyHitExplosion(missSprite);
return null;
return null;
default:
throw new UnsupportedSkinComponentException(lookup);
case TaikoSkinComponents.TaikoExplosionOk:
case TaikoSkinComponents.TaikoExplosionGreat:
string hitName = getHitName(taikoComponent.Component);
var hitSprite = this.GetAnimation(hitName, true, false);
if (hitSprite != null)
{
var strongHitSprite = this.GetAnimation($"{hitName}k", true, false);
return new LegacyHitExplosion(hitSprite, strongHitSprite);
}
return null;
case TaikoSkinComponents.TaikoExplosionKiai:
// suppress the default kiai explosion if the skin brings its own sprites.
// the drawable needs to expire as soon as possible to avoid accumulating empty drawables on the playfield.
if (hasExplosion.Value)
return Drawable.Empty().With(d => d.Expire());
return null;
case TaikoSkinComponents.Scroller:
if (GetTexture("taiko-slider") != null)
return new LegacyTaikoScroller();
return null;
case TaikoSkinComponents.Mascot:
return new DrawableTaikoMascot();
case TaikoSkinComponents.KiaiGlow:
if (GetTexture("taiko-glow") != null)
return new LegacyKiaiGlow();
return null;
default:
throw new UnsupportedSkinComponentException(lookup);
}
}
}
+4
View File
@@ -36,6 +36,7 @@ using osu.Game.Configuration;
using osu.Game.Rulesets.Scoring.Legacy;
using osu.Game.Rulesets.Taiko.Configuration;
using osu.Game.Rulesets.Taiko.Edit.Setup;
using osu.Game.Rulesets.Taiko.Skinning.Default;
using osu.Game.Screens.Edit.Setup;
namespace osu.Game.Rulesets.Taiko
@@ -57,6 +58,9 @@ namespace osu.Game.Rulesets.Taiko
case ArgonSkin:
return new TaikoArgonSkinTransformer(skin);
case TrianglesSkin:
return new TaikoTrianglesSkinTransformer(skin);
case LegacySkin:
return new TaikoLegacySkinTransformer(skin);
}
@@ -13,6 +13,7 @@ using osu.Framework.Graphics.Rendering;
using osu.Framework.Graphics.Textures;
using osu.Framework.IO.Stores;
using osu.Framework.Platform;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Database;
@@ -53,6 +54,12 @@ namespace osu.Game.Tests.Visual.Gameplay
skins["legacy"] = new DefaultLegacySkin(this);
}
[SetUpSteps]
public void SetUpSteps()
{
AddToggleStep("toggle leaderboard", b => configManager.SetValue(OsuSetting.GameplayLeaderboard, b));
}
[Test]
public void TestLayout(
[Values("argon", "triangles", "legacy")]
@@ -25,8 +25,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
{
protected override bool PauseOnFocusLost => false;
protected override bool ShowLeaderboard => true;
protected override UserActivity InitialActivity => new UserActivity.InMultiplayerGame(Beatmap.Value.BeatmapInfo, Ruleset.Value);
[Resolved]
@@ -42,6 +40,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
private readonly MultiplayerLeaderboardProvider leaderboardProvider;
private GameplayMatchScoreDisplay teamScoreDisplay = null!;
private GameplayChatDisplay chat;
/// <summary>
/// Construct a multiplayer player.
@@ -57,7 +56,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
AllowFailAnimation = false,
AllowSkipping = room.AutoSkip,
AutomaticallySkipIntro = room.AutoSkip,
AlwaysShowLeaderboard = true,
ShowLeaderboard = true,
})
{
leaderboardProvider = new MultiplayerLeaderboardProvider(users);
@@ -71,10 +70,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
ScoreProcessor.ApplyNewJudgementsWhenFailed = true;
LoadComponentAsync(new GameplayChatDisplay(Room)
{
Expanded = { BindTarget = LeaderboardExpandedState },
}, chat => HUDOverlay.LeaderboardFlow.Insert(2, chat));
LoadComponentAsync(chat = new GameplayChatDisplay(Room), HUDOverlay.LeaderboardFlow.Add);
LoadComponentAsync(teamScoreDisplay = new GameplayMatchScoreDisplay
{
@@ -124,6 +120,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
failAndBail();
}
}), true);
LocalUserPlaying.BindValueChanged(_ => chat.Expanded.Value = !LocalUserPlaying.Value, true);
}
protected override void LoadComplete()
@@ -13,12 +13,13 @@ using osu.Framework.Graphics.Containers;
using osu.Game.Configuration;
using osu.Game.Graphics.Containers;
using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Skinning;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Screens.Play.HUD
{
public partial class DrawableGameplayLeaderboard : CompositeDrawable
public partial class DrawableGameplayLeaderboard : CompositeDrawable, ISerialisableDrawable
{
private readonly Cached sorting = new Cached();
@@ -31,11 +32,16 @@ namespace osu.Game.Screens.Play.HUD
public DrawableGameplayLeaderboardScore? TrackedScore { get; private set; }
[Resolved]
private Player? player { get; set; }
[Resolved]
private IGameplayLeaderboardProvider? leaderboardProvider { get; set; }
private readonly IBindableList<GameplayLeaderboardScore> scores = new BindableList<GameplayLeaderboardScore>();
private readonly Bindable<bool> configVisibility = new Bindable<bool>();
private readonly IBindable<LocalUserPlayingState> userPlayingState = new Bindable<LocalUserPlayingState>();
private readonly IBindable<bool> holdingForHUD = new Bindable<bool>();
private const int max_panels = 8;
@@ -45,6 +51,7 @@ namespace osu.Game.Screens.Play.HUD
public DrawableGameplayLeaderboard()
{
Width = DrawableGameplayLeaderboardScore.EXTENDED_WIDTH + DrawableGameplayLeaderboardScore.SHEAR_WIDTH;
Height = 300;
InternalChildren = new Drawable[]
{
@@ -67,9 +74,15 @@ namespace osu.Game.Screens.Play.HUD
}
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
private void load(OsuConfigManager config, GameplayState? gameplayState, HUDOverlay? hudOverlay)
{
config.BindWith(OsuSetting.GameplayLeaderboard, configVisibility);
if (gameplayState != null)
userPlayingState.BindTo(gameplayState.PlayingState);
if (hudOverlay != null)
holdingForHUD.BindTo(hudOverlay.HoldingForHUD);
}
protected override void LoadComplete()
@@ -88,7 +101,20 @@ namespace osu.Game.Screens.Play.HUD
}
Scheduler.AddDelayed(sort, 1000, true);
configVisibility.BindValueChanged(_ => this.FadeTo(configVisibility.Value ? 1 : 0, 100, Easing.OutQuint), true);
configVisibility.BindValueChanged(_ => Scheduler.AddOnce(updateState));
userPlayingState.BindValueChanged(_ => Scheduler.AddOnce(updateState));
holdingForHUD.BindValueChanged(_ => Scheduler.AddOnce(updateState));
updateState();
}
private void updateState()
{
// prevents weird delay in the flow correctly appearing when toggling the leaderboard on.
if (Flow.Alpha < 1)
scroll.ScrollToStart(false);
Flow.FadeTo(player?.Configuration.ShowLeaderboard != false && configVisibility.Value ? 1 : 0, 100, Easing.OutQuint);
Expanded.Value = userPlayingState.Value == LocalUserPlayingState.Playing || holdingForHUD.Value;
}
/// <summary>
@@ -111,10 +137,6 @@ namespace osu.Game.Screens.Play.HUD
Flow.Add(drawable);
drawable.TotalScore.BindValueChanged(_ => sorting.Invalidate(), true);
drawable.DisplayOrder.BindValueChanged(_ => sorting.Invalidate(), true);
int displayCount = Math.Min(Flow.Count, max_panels);
Height = displayCount * (DrawableGameplayLeaderboardScore.PANEL_HEIGHT + Flow.Spacing.Y);
requiresScroll = displayCount != Flow.Count;
}
public void Clear()
@@ -131,6 +153,8 @@ namespace osu.Game.Screens.Play.HUD
{
base.Update();
requiresScroll = Flow.DrawHeight > Height;
if (requiresScroll && TrackedScore != null)
{
double scrollTarget = scroll.GetChildPosInContent(TrackedScore) + TrackedScore.DrawHeight / 2 - scroll.DrawHeight / 2;
@@ -207,5 +231,7 @@ namespace osu.Game.Screens.Play.HUD
public override bool HandlePositionalInput => false;
public override bool HandleNonPositionalInput => false;
}
public bool UsesFixedAnchor { get; set; }
}
}
+2 -3
View File
@@ -114,7 +114,7 @@ namespace osu.Game.Screens.Play
/// </summary>
internal readonly Drawable PlayfieldSkinLayer;
public HUDOverlay([CanBeNull] DrawableRuleset drawableRuleset, IReadOnlyList<Mod> mods, bool alwaysShowLeaderboard = true)
public HUDOverlay([CanBeNull] DrawableRuleset drawableRuleset, IReadOnlyList<Mod> mods)
{
Container rightSettings;
@@ -191,8 +191,7 @@ namespace osu.Game.Screens.Play
if (rulesetComponents != null)
hideTargets.Add(rulesetComponents);
if (!alwaysShowLeaderboard)
hideTargets.Add(LeaderboardFlow);
hideTargets.Add(LeaderboardFlow);
}
[BackgroundDependencyLoader(true)]
+1 -35
View File
@@ -34,7 +34,6 @@ using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Scoring;
using osu.Game.Scoring.Legacy;
using osu.Game.Screens.Play.HUD;
using osu.Game.Screens.Ranking;
using osu.Game.Skinning;
using osu.Game.Users;
@@ -425,8 +424,6 @@ namespace osu.Game.Screens.Play
IsBreakTime.BindTo(breakTracker.IsBreakTime);
IsBreakTime.BindValueChanged(onBreakTimeChanged, true);
loadLeaderboard();
}
protected virtual GameplayClockContainer CreateGameplayClockContainer(WorkingBeatmap beatmap, double gameplayStart) => new MasterGameplayClockContainer(beatmap, gameplayStart);
@@ -477,7 +474,7 @@ namespace osu.Game.Screens.Play
Children = new[]
{
DimmableStoryboard.OverlayLayerContainer.CreateProxy(),
HUDOverlay = new HUDOverlay(DrawableRuleset, GameplayState.Mods, Configuration.AlwaysShowLeaderboard)
HUDOverlay = new HUDOverlay(DrawableRuleset, GameplayState.Mods)
{
HoldToQuit =
{
@@ -933,37 +930,6 @@ namespace osu.Game.Screens.Play
});
}
#region Gameplay leaderboard
protected virtual bool ShowLeaderboard => false;
protected readonly Bindable<bool> LeaderboardExpandedState = new BindableBool();
private void loadLeaderboard()
{
if (!ShowLeaderboard)
return;
HUDOverlay.HoldingForHUD.BindValueChanged(_ => updateLeaderboardExpandedState());
LocalUserPlaying.BindValueChanged(_ => updateLeaderboardExpandedState(), true);
var gameplayLeaderboard = new DrawableGameplayLeaderboard();
LoadComponentAsync(gameplayLeaderboard, leaderboard =>
{
if (!LoadedBeatmapSuccessfully)
return;
leaderboard.Expanded.BindTo(LeaderboardExpandedState);
HUDOverlay.LeaderboardFlow.Add(leaderboard);
});
}
private void updateLeaderboardExpandedState() =>
LeaderboardExpandedState.Value = !LocalUserPlaying.Value || HUDOverlay.HoldingForHUD.Value;
#endregion
#region Fail Logic
/// <summary>
+2 -2
View File
@@ -42,8 +42,8 @@ namespace osu.Game.Screens.Play
public bool AutomaticallySkipIntro { get; set; }
/// <summary>
/// Whether the gameplay leaderboard should always be shown (usually in a contracted state).
/// Whether the gameplay leaderboard should be shown.
/// </summary>
public bool AlwaysShowLeaderboard { get; set; }
public bool ShowLeaderboard { get; set; }
}
}
+1 -2
View File
@@ -49,8 +49,6 @@ namespace osu.Game.Screens.Play
return base.CheckModsAllowFailure();
}
protected override bool ShowLeaderboard => true;
public ReplayPlayer(Score score, PlayerConfiguration configuration = null)
: this((_, _) => score, configuration)
{
@@ -61,6 +59,7 @@ namespace osu.Game.Screens.Play
: base(configuration)
{
this.createScore = createScore;
Configuration.ShowLeaderboard = true;
}
/// <summary>
+1 -2
View File
@@ -19,14 +19,13 @@ namespace osu.Game.Screens.Play
{
public partial class SoloPlayer : SubmittingPlayer
{
protected override bool ShowLeaderboard => true;
[Cached(typeof(IGameplayLeaderboardProvider))]
private readonly SoloGameplayLeaderboardProvider leaderboardProvider = new SoloGameplayLeaderboardProvider();
public SoloPlayer([CanBeNull] PlayerConfiguration configuration = null)
: base(configuration)
{
Configuration.ShowLeaderboard = true;
}
[BackgroundDependencyLoader]
+5
View File
@@ -111,9 +111,13 @@ namespace osu.Game.Skinning
{
return new DefaultSkinComponentsContainer(container =>
{
var leaderboard = container.OfType<DrawableGameplayLeaderboard>().FirstOrDefault();
var comboCounter = container.OfType<ArgonComboCounter>().FirstOrDefault();
var spectatorList = container.OfType<SpectatorList>().FirstOrDefault();
if (leaderboard != null)
leaderboard.Position = new Vector2(36, 115);
Vector2 pos = new Vector2(36, -66);
if (comboCounter != null)
@@ -129,6 +133,7 @@ namespace osu.Game.Skinning
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
new DrawableGameplayLeaderboard(),
new ArgonComboCounter
{
Anchor = Anchor.BottomLeft,
+9
View File
@@ -374,6 +374,7 @@ namespace osu.Game.Skinning
{
var combo = container.OfType<LegacyDefaultComboCounter>().FirstOrDefault();
var spectatorList = container.OfType<SpectatorList>().FirstOrDefault();
var leaderboard = container.OfType<DrawableGameplayLeaderboard>().FirstOrDefault();
Vector2 pos = new Vector2();
@@ -392,10 +393,18 @@ namespace osu.Game.Skinning
spectatorList.Origin = Anchor.BottomLeft;
spectatorList.Position = pos;
}
if (leaderboard != null)
{
leaderboard.Anchor = Anchor.CentreLeft;
leaderboard.Origin = Anchor.CentreLeft;
leaderboard.X = 10;
}
})
{
new LegacyDefaultComboCounter(),
new SpectatorList(),
new DrawableGameplayLeaderboard(),
};
}
+42 -29
View File
@@ -68,10 +68,6 @@ namespace osu.Game.Skinning
switch (lookup)
{
case GlobalSkinnableContainerLookup containerLookup:
// Only handle global level defaults for now.
if (containerLookup.Ruleset != null)
return null;
switch (containerLookup.Lookup)
{
case GlobalSkinnableContainers.SongSelect:
@@ -83,6 +79,44 @@ namespace osu.Game.Skinning
return songSelectComponents;
case GlobalSkinnableContainers.MainHUDComponents:
// elements default to beneath the health bar
const float score_vertical_offset = 30;
const float horizontal_padding = 20;
const float screen_edge_padding = 10;
// Hard to find this at runtime, so taken from the most expanded state during replay.
const float song_progress_offset_height = 73;
if (containerLookup.Ruleset != null)
{
return new DefaultSkinComponentsContainer(container =>
{
var leaderboard = container.OfType<DrawableGameplayLeaderboard>().FirstOrDefault();
var spectatorList = container.OfType<SpectatorList>().FirstOrDefault();
if (leaderboard != null)
leaderboard.Position = new Vector2(40, 60);
if (spectatorList != null)
{
spectatorList.HeaderFont.Value = Typeface.Venera;
spectatorList.HeaderColour.Value = new OsuColour().BlueLighter;
spectatorList.Anchor = Anchor.BottomLeft;
spectatorList.Origin = Anchor.BottomLeft;
spectatorList.Position = new Vector2(screen_edge_padding, -(song_progress_offset_height + screen_edge_padding));
}
})
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
new DrawableGameplayLeaderboard(),
new SpectatorList(),
},
};
}
var skinnableTargetWrapper = new DefaultSkinComponentsContainer(container =>
{
var score = container.OfType<DefaultScoreCounter>().FirstOrDefault();
@@ -91,19 +125,13 @@ namespace osu.Game.Skinning
var ppCounter = container.OfType<PerformancePointsCounter>().FirstOrDefault();
var songProgress = container.OfType<DefaultSongProgress>().FirstOrDefault();
var keyCounter = container.OfType<DefaultKeyCounterDisplay>().FirstOrDefault();
var spectatorList = container.OfType<SpectatorList>().FirstOrDefault();
if (score != null)
{
score.Anchor = Anchor.TopCentre;
score.Origin = Anchor.TopCentre;
// elements default to beneath the health bar
const float vertical_offset = 30;
const float horizontal_padding = 20;
score.Position = new Vector2(0, vertical_offset);
score.Position = new Vector2(0, score_vertical_offset);
if (ppCounter != null)
{
@@ -114,13 +142,13 @@ namespace osu.Game.Skinning
if (accuracy != null)
{
accuracy.Position = new Vector2(-accuracy.ScreenSpaceDeltaToParentSpace(score.ScreenSpaceDrawQuad.Size).X / 2 - horizontal_padding, vertical_offset + 5);
accuracy.Position = new Vector2(-accuracy.ScreenSpaceDeltaToParentSpace(score.ScreenSpaceDrawQuad.Size).X / 2 - horizontal_padding, score_vertical_offset + 5);
accuracy.Origin = Anchor.TopRight;
accuracy.Anchor = Anchor.TopCentre;
if (combo != null)
{
combo.Position = new Vector2(accuracy.ScreenSpaceDeltaToParentSpace(score.ScreenSpaceDrawQuad.Size).X / 2 + horizontal_padding, vertical_offset + 5);
combo.Position = new Vector2(accuracy.ScreenSpaceDeltaToParentSpace(score.ScreenSpaceDrawQuad.Size).X / 2 + horizontal_padding, score_vertical_offset + 5);
combo.Anchor = Anchor.TopCentre;
}
}
@@ -144,25 +172,11 @@ namespace osu.Game.Skinning
}
}
const float padding = 10;
// Hard to find this at runtime, so taken from the most expanded state during replay.
const float song_progress_offset_height = 73;
if (songProgress != null && keyCounter != null)
{
keyCounter.Anchor = Anchor.BottomRight;
keyCounter.Origin = Anchor.BottomRight;
keyCounter.Position = new Vector2(-padding, -(song_progress_offset_height + padding));
}
if (spectatorList != null)
{
spectatorList.HeaderFont.Value = Typeface.Venera;
spectatorList.HeaderColour.Value = new OsuColour().BlueLighter;
spectatorList.Anchor = Anchor.BottomLeft;
spectatorList.Origin = Anchor.BottomLeft;
spectatorList.Position = new Vector2(padding, -(song_progress_offset_height + padding));
keyCounter.Position = new Vector2(-screen_edge_padding, -(song_progress_offset_height + screen_edge_padding));
}
})
{
@@ -177,7 +191,6 @@ namespace osu.Game.Skinning
new BarHitErrorMeter(),
new BarHitErrorMeter(),
new TrianglesPerformancePointsCounter(),
new SpectatorList(),
}
};