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:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user