mirror of
https://github.com/ppy/osu.git
synced 2025-03-09 07:07:18 +08:00
Merge pull request #29027 from normalid-awa/feature/skin/legacy-input-overlay
Add legacy key counter support
This commit is contained in:
commit
b1488fd5b7
@ -6,6 +6,7 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
||||
@ -28,11 +29,15 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
||||
|
||||
public override Drawable? GetDrawableComponent(ISkinComponentLookup lookup)
|
||||
{
|
||||
if (lookup is SkinComponentsContainerLookup containerLookup)
|
||||
switch (lookup)
|
||||
{
|
||||
switch (containerLookup.Target)
|
||||
{
|
||||
case SkinComponentsContainerLookup.TargetArea.MainHUDComponents:
|
||||
case SkinComponentsContainerLookup containerLookup:
|
||||
if (containerLookup.Target != SkinComponentsContainerLookup.TargetArea.MainHUDComponents)
|
||||
return base.GetDrawableComponent(lookup);
|
||||
|
||||
// Modifications for global components.
|
||||
if (containerLookup.Ruleset == null)
|
||||
{
|
||||
var components = base.GetDrawableComponent(lookup) as Container;
|
||||
|
||||
if (providesComboCounter && components != null)
|
||||
@ -44,60 +49,84 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
||||
}
|
||||
|
||||
return components;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lookup is CatchSkinComponentLookup catchSkinComponent)
|
||||
{
|
||||
switch (catchSkinComponent.Component)
|
||||
{
|
||||
case CatchSkinComponents.Fruit:
|
||||
if (hasPear)
|
||||
return new LegacyFruitPiece();
|
||||
// Skin has configuration.
|
||||
if (base.GetDrawableComponent(lookup) is Drawable d)
|
||||
return d;
|
||||
|
||||
return null;
|
||||
// Our own ruleset components default.
|
||||
return new DefaultSkinComponentsContainer(container =>
|
||||
{
|
||||
var keyCounter = container.OfType<LegacyKeyCounterDisplay>().FirstOrDefault();
|
||||
|
||||
case CatchSkinComponents.Banana:
|
||||
if (GetTexture("fruit-bananas") != null)
|
||||
return new LegacyBananaPiece();
|
||||
|
||||
return null;
|
||||
|
||||
case CatchSkinComponents.Droplet:
|
||||
if (GetTexture("fruit-drop") != null)
|
||||
return new LegacyDropletPiece();
|
||||
|
||||
return null;
|
||||
|
||||
case CatchSkinComponents.Catcher:
|
||||
decimal version = GetConfig<SkinConfiguration.LegacySetting, decimal>(SkinConfiguration.LegacySetting.Version)?.Value ?? 1;
|
||||
|
||||
if (version < 2.3m)
|
||||
if (keyCounter != null)
|
||||
{
|
||||
if (hasOldStyleCatcherSprite())
|
||||
return new LegacyCatcherOld();
|
||||
// set the anchor to top right so that it won't squash to the return button to the top
|
||||
keyCounter.Anchor = Anchor.CentreRight;
|
||||
keyCounter.Origin = Anchor.CentreRight;
|
||||
keyCounter.X = 0;
|
||||
// 340px is the default height inherit from stable
|
||||
keyCounter.Y = container.ToLocalSpace(new Vector2(0, container.ScreenSpaceDrawQuad.Centre.Y - 340f)).Y;
|
||||
}
|
||||
})
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new LegacyKeyCounterDisplay(),
|
||||
}
|
||||
};
|
||||
|
||||
if (hasNewStyleCatcherSprite())
|
||||
return new LegacyCatcherNew();
|
||||
case CatchSkinComponentLookup catchSkinComponent:
|
||||
switch (catchSkinComponent.Component)
|
||||
{
|
||||
case CatchSkinComponents.Fruit:
|
||||
if (hasPear)
|
||||
return new LegacyFruitPiece();
|
||||
|
||||
return null;
|
||||
return null;
|
||||
|
||||
case CatchSkinComponents.CatchComboCounter:
|
||||
if (providesComboCounter)
|
||||
return new LegacyCatchComboCounter();
|
||||
case CatchSkinComponents.Banana:
|
||||
if (GetTexture("fruit-bananas") != null)
|
||||
return new LegacyBananaPiece();
|
||||
|
||||
return null;
|
||||
return null;
|
||||
|
||||
case CatchSkinComponents.HitExplosion:
|
||||
if (hasOldStyleCatcherSprite() || hasNewStyleCatcherSprite())
|
||||
return new LegacyHitExplosion();
|
||||
case CatchSkinComponents.Droplet:
|
||||
if (GetTexture("fruit-drop") != null)
|
||||
return new LegacyDropletPiece();
|
||||
|
||||
return null;
|
||||
return null;
|
||||
|
||||
default:
|
||||
throw new UnsupportedSkinComponentException(lookup);
|
||||
}
|
||||
case CatchSkinComponents.Catcher:
|
||||
decimal version = GetConfig<SkinConfiguration.LegacySetting, decimal>(SkinConfiguration.LegacySetting.Version)?.Value ?? 1;
|
||||
|
||||
if (version < 2.3m)
|
||||
{
|
||||
if (hasOldStyleCatcherSprite())
|
||||
return new LegacyCatcherOld();
|
||||
}
|
||||
|
||||
if (hasNewStyleCatcherSprite())
|
||||
return new LegacyCatcherNew();
|
||||
|
||||
return null;
|
||||
|
||||
case CatchSkinComponents.CatchComboCounter:
|
||||
if (providesComboCounter)
|
||||
return new LegacyCatchComboCounter();
|
||||
|
||||
return null;
|
||||
|
||||
case CatchSkinComponents.HitExplosion:
|
||||
if (hasOldStyleCatcherSprite() || hasNewStyleCatcherSprite())
|
||||
return new LegacyHitExplosion();
|
||||
|
||||
return null;
|
||||
|
||||
default:
|
||||
throw new UnsupportedSkinComponentException(lookup);
|
||||
}
|
||||
}
|
||||
|
||||
return base.GetDrawableComponent(lookup);
|
||||
|
@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
@ -41,139 +42,178 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
|
||||
public override Drawable? GetDrawableComponent(ISkinComponentLookup lookup)
|
||||
{
|
||||
if (lookup is OsuSkinComponentLookup osuComponent)
|
||||
switch (lookup)
|
||||
{
|
||||
switch (osuComponent.Component)
|
||||
{
|
||||
case OsuSkinComponents.FollowPoint:
|
||||
return this.GetAnimation("followpoint", true, true, true, startAtCurrentTime: false, maxSize: new Vector2(OsuHitObject.OBJECT_RADIUS * 2, OsuHitObject.OBJECT_RADIUS));
|
||||
case SkinComponentsContainerLookup containerLookup:
|
||||
// Only handle per ruleset defaults here.
|
||||
if (containerLookup.Ruleset == null)
|
||||
return base.GetDrawableComponent(lookup);
|
||||
|
||||
case OsuSkinComponents.SliderScorePoint:
|
||||
return this.GetAnimation("sliderscorepoint", false, false, maxSize: OsuHitObject.OBJECT_DIMENSIONS);
|
||||
// Skin has configuration.
|
||||
if (base.GetDrawableComponent(lookup) is Drawable d)
|
||||
return d;
|
||||
|
||||
case OsuSkinComponents.SliderFollowCircle:
|
||||
var followCircleContent = this.GetAnimation("sliderfollowcircle", true, true, true, maxSize: MAX_FOLLOW_CIRCLE_AREA_SIZE);
|
||||
if (followCircleContent != null)
|
||||
return new LegacyFollowCircle(followCircleContent);
|
||||
// Our own ruleset components default.
|
||||
switch (containerLookup.Target)
|
||||
{
|
||||
case SkinComponentsContainerLookup.TargetArea.MainHUDComponents:
|
||||
return new DefaultSkinComponentsContainer(container =>
|
||||
{
|
||||
var keyCounter = container.OfType<LegacyKeyCounterDisplay>().FirstOrDefault();
|
||||
|
||||
return null;
|
||||
if (keyCounter != null)
|
||||
{
|
||||
// set the anchor to top right so that it won't squash to the return button to the top
|
||||
keyCounter.Anchor = Anchor.CentreRight;
|
||||
keyCounter.Origin = Anchor.CentreRight;
|
||||
keyCounter.X = 0;
|
||||
// 340px is the default height inherit from stable
|
||||
keyCounter.Y = container.ToLocalSpace(new Vector2(0, container.ScreenSpaceDrawQuad.Centre.Y - 340f)).Y;
|
||||
}
|
||||
})
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new LegacyKeyCounterDisplay(),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
case OsuSkinComponents.SliderBall:
|
||||
if (GetTexture("sliderb") != null || GetTexture("sliderb0") != null)
|
||||
return new LegacySliderBall(this);
|
||||
return null;
|
||||
|
||||
return null;
|
||||
case OsuSkinComponentLookup osuComponent:
|
||||
switch (osuComponent.Component)
|
||||
{
|
||||
case OsuSkinComponents.FollowPoint:
|
||||
return this.GetAnimation("followpoint", true, true, true, startAtCurrentTime: false, maxSize: new Vector2(OsuHitObject.OBJECT_RADIUS * 2, OsuHitObject.OBJECT_RADIUS));
|
||||
|
||||
case OsuSkinComponents.SliderBody:
|
||||
if (hasHitCircle.Value)
|
||||
return new LegacySliderBody();
|
||||
case OsuSkinComponents.SliderScorePoint:
|
||||
return this.GetAnimation("sliderscorepoint", false, false, maxSize: OsuHitObject.OBJECT_DIMENSIONS);
|
||||
|
||||
return null;
|
||||
case OsuSkinComponents.SliderFollowCircle:
|
||||
var followCircleContent = this.GetAnimation("sliderfollowcircle", true, true, true, maxSize: MAX_FOLLOW_CIRCLE_AREA_SIZE);
|
||||
if (followCircleContent != null)
|
||||
return new LegacyFollowCircle(followCircleContent);
|
||||
|
||||
case OsuSkinComponents.SliderTailHitCircle:
|
||||
if (hasHitCircle.Value)
|
||||
return new LegacyMainCirclePiece("sliderendcircle", false);
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.SliderHeadHitCircle:
|
||||
if (hasHitCircle.Value)
|
||||
return new LegacySliderHeadHitCircle();
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.ReverseArrow:
|
||||
if (hasHitCircle.Value)
|
||||
return new LegacyReverseArrow();
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.HitCircle:
|
||||
if (hasHitCircle.Value)
|
||||
return new LegacyMainCirclePiece();
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.Cursor:
|
||||
if (GetTexture("cursor") != null)
|
||||
return new LegacyCursor(this);
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.CursorTrail:
|
||||
if (GetTexture("cursortrail") != null)
|
||||
return new LegacyCursorTrail(this);
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.CursorRipple:
|
||||
if (GetTexture("cursor-ripple") != null)
|
||||
{
|
||||
var ripple = this.GetAnimation("cursor-ripple", false, false);
|
||||
|
||||
// In stable this element was scaled down to 50% and opacity 20%, but this makes the elements WAY too big and inflexible.
|
||||
// If anyone complains about these not being applied, this can be uncommented.
|
||||
//
|
||||
// But if no one complains I'd rather fix this in lazer. Wiki documentation doesn't mention size,
|
||||
// so we might be okay.
|
||||
//
|
||||
// if (ripple != null)
|
||||
// {
|
||||
// ripple.Scale = new Vector2(0.5f);
|
||||
// ripple.Alpha = 0.2f;
|
||||
// }
|
||||
|
||||
return ripple;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.CursorParticles:
|
||||
if (GetTexture("star2") != null)
|
||||
return new LegacyCursorParticles();
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.CursorSmoke:
|
||||
if (GetTexture("cursor-smoke") != null)
|
||||
return new LegacySmokeSegment();
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.HitCircleText:
|
||||
if (!this.HasFont(LegacyFont.HitCircle))
|
||||
return null;
|
||||
|
||||
const float hitcircle_text_scale = 0.8f;
|
||||
return new LegacySpriteText(LegacyFont.HitCircle)
|
||||
{
|
||||
// stable applies a blanket 0.8x scale to hitcircle fonts
|
||||
Scale = new Vector2(hitcircle_text_scale),
|
||||
MaxSizePerGlyph = OsuHitObject.OBJECT_DIMENSIONS * 2 / hitcircle_text_scale,
|
||||
};
|
||||
case OsuSkinComponents.SliderBall:
|
||||
if (GetTexture("sliderb") != null || GetTexture("sliderb0") != null)
|
||||
return new LegacySliderBall(this);
|
||||
|
||||
case OsuSkinComponents.SpinnerBody:
|
||||
bool hasBackground = GetTexture("spinner-background") != null;
|
||||
return null;
|
||||
|
||||
if (GetTexture("spinner-top") != null && !hasBackground)
|
||||
return new LegacyNewStyleSpinner();
|
||||
else if (hasBackground)
|
||||
return new LegacyOldStyleSpinner();
|
||||
case OsuSkinComponents.SliderBody:
|
||||
if (hasHitCircle.Value)
|
||||
return new LegacySliderBody();
|
||||
|
||||
return null;
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.ApproachCircle:
|
||||
if (GetTexture(@"approachcircle") != null)
|
||||
return new LegacyApproachCircle();
|
||||
case OsuSkinComponents.SliderTailHitCircle:
|
||||
if (hasHitCircle.Value)
|
||||
return new LegacyMainCirclePiece("sliderendcircle", false);
|
||||
|
||||
return null;
|
||||
return null;
|
||||
|
||||
default:
|
||||
throw new UnsupportedSkinComponentException(lookup);
|
||||
}
|
||||
case OsuSkinComponents.SliderHeadHitCircle:
|
||||
if (hasHitCircle.Value)
|
||||
return new LegacySliderHeadHitCircle();
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.ReverseArrow:
|
||||
if (hasHitCircle.Value)
|
||||
return new LegacyReverseArrow();
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.HitCircle:
|
||||
if (hasHitCircle.Value)
|
||||
return new LegacyMainCirclePiece();
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.Cursor:
|
||||
if (GetTexture("cursor") != null)
|
||||
return new LegacyCursor(this);
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.CursorTrail:
|
||||
if (GetTexture("cursortrail") != null)
|
||||
return new LegacyCursorTrail(this);
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.CursorRipple:
|
||||
if (GetTexture("cursor-ripple") != null)
|
||||
{
|
||||
var ripple = this.GetAnimation("cursor-ripple", false, false);
|
||||
|
||||
// In stable this element was scaled down to 50% and opacity 20%, but this makes the elements WAY too big and inflexible.
|
||||
// If anyone complains about these not being applied, this can be uncommented.
|
||||
//
|
||||
// But if no one complains I'd rather fix this in lazer. Wiki documentation doesn't mention size,
|
||||
// so we might be okay.
|
||||
//
|
||||
// if (ripple != null)
|
||||
// {
|
||||
// ripple.Scale = new Vector2(0.5f);
|
||||
// ripple.Alpha = 0.2f;
|
||||
// }
|
||||
|
||||
return ripple;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.CursorParticles:
|
||||
if (GetTexture("star2") != null)
|
||||
return new LegacyCursorParticles();
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.CursorSmoke:
|
||||
if (GetTexture("cursor-smoke") != null)
|
||||
return new LegacySmokeSegment();
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.HitCircleText:
|
||||
if (!this.HasFont(LegacyFont.HitCircle))
|
||||
return null;
|
||||
|
||||
const float hitcircle_text_scale = 0.8f;
|
||||
return new LegacySpriteText(LegacyFont.HitCircle)
|
||||
{
|
||||
// stable applies a blanket 0.8x scale to hitcircle fonts
|
||||
Scale = new Vector2(hitcircle_text_scale),
|
||||
MaxSizePerGlyph = OsuHitObject.OBJECT_DIMENSIONS * 2 / hitcircle_text_scale,
|
||||
};
|
||||
|
||||
case OsuSkinComponents.SpinnerBody:
|
||||
bool hasBackground = GetTexture("spinner-background") != null;
|
||||
|
||||
if (GetTexture("spinner-top") != null && !hasBackground)
|
||||
return new LegacyNewStyleSpinner();
|
||||
else if (hasBackground)
|
||||
return new LegacyOldStyleSpinner();
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.ApproachCircle:
|
||||
if (GetTexture(@"approachcircle") != null)
|
||||
return new LegacyApproachCircle();
|
||||
|
||||
return null;
|
||||
|
||||
default:
|
||||
throw new UnsupportedSkinComponentException(lookup);
|
||||
}
|
||||
|
||||
default:
|
||||
return base.GetDrawableComponent(lookup);
|
||||
}
|
||||
|
||||
return base.GetDrawableComponent(lookup);
|
||||
}
|
||||
|
||||
public override IBindable<TValue>? GetConfig<TLookup, TValue>(TLookup lookup)
|
||||
|
BIN
osu.Game.Tests/Resources/Archives/modified-classic-20240724.osk
Normal file
BIN
osu.Game.Tests/Resources/Archives/modified-classic-20240724.osk
Normal file
Binary file not shown.
@ -65,7 +65,9 @@ namespace osu.Game.Tests.Skins
|
||||
// Covers default rank display
|
||||
"Archives/modified-default-20230809.osk",
|
||||
// Covers legacy rank display
|
||||
"Archives/modified-classic-20230809.osk"
|
||||
"Archives/modified-classic-20230809.osk",
|
||||
// Covers legacy key counter
|
||||
"Archives/modified-classic-20240724.osk"
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
@ -9,7 +9,6 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics.Containers;
|
||||
@ -45,7 +44,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
// best way to check without exposing.
|
||||
private Drawable hideTarget => hudOverlay.ChildrenOfType<SkinComponentsContainer>().First();
|
||||
private Drawable keyCounterFlow => hudOverlay.ChildrenOfType<KeyCounterDisplay>().First().ChildrenOfType<FillFlowContainer<KeyCounter>>().Single();
|
||||
private Drawable keyCounterContent => hudOverlay.ChildrenOfType<KeyCounterDisplay>().First().ChildrenOfType<Drawable>().Skip(1).First();
|
||||
|
||||
public TestSceneHUDOverlay()
|
||||
{
|
||||
@ -79,7 +78,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddAssert("showhud is set", () => hudOverlay.ShowHud.Value);
|
||||
|
||||
AddAssert("hidetarget is visible", () => hideTarget.Alpha, () => Is.GreaterThan(0));
|
||||
AddAssert("key counter flow is visible", () => keyCounterFlow.IsPresent);
|
||||
AddAssert("key counter flow is visible", () => keyCounterContent.IsPresent);
|
||||
AddAssert("pause button is visible", () => hudOverlay.HoldToQuit.IsPresent);
|
||||
}
|
||||
|
||||
@ -104,7 +103,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddAssert("pause button is still visible", () => hudOverlay.HoldToQuit.IsPresent);
|
||||
|
||||
// Key counter flow container should not be affected by this, only the key counter display will be hidden as checked above.
|
||||
AddAssert("key counter flow not affected", () => keyCounterFlow.IsPresent);
|
||||
AddAssert("key counter flow not affected", () => keyCounterContent.IsPresent);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -150,11 +149,11 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
AddStep("set showhud false", () => hudOverlay.ShowHud.Value = false);
|
||||
AddUntilStep("hidetarget is hidden", () => hideTarget.Alpha, () => Is.LessThanOrEqualTo(0));
|
||||
AddUntilStep("key counters hidden", () => !keyCounterFlow.IsPresent);
|
||||
AddUntilStep("key counters hidden", () => !keyCounterContent.IsPresent);
|
||||
|
||||
AddStep("set showhud true", () => hudOverlay.ShowHud.Value = true);
|
||||
AddUntilStep("hidetarget is visible", () => hideTarget.Alpha, () => Is.GreaterThan(0));
|
||||
AddUntilStep("key counters still hidden", () => !keyCounterFlow.IsPresent);
|
||||
AddUntilStep("key counters still hidden", () => !keyCounterContent.IsPresent);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -9,6 +9,7 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
using osuTK.Input;
|
||||
|
||||
@ -56,6 +57,11 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
Anchor = Anchor.Centre,
|
||||
Scale = new Vector2(1, -1)
|
||||
},
|
||||
new LegacyKeyCounterDisplay
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
@ -89,6 +95,12 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
Anchor = Anchor.Centre,
|
||||
Rotation = 90,
|
||||
},
|
||||
new LegacyKeyCounterDisplay
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Rotation = 90,
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
@ -16,12 +16,13 @@ using osu.Framework.Testing;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Beatmaps;
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Tests.Gameplay;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
@ -91,10 +92,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
SetContents(_ =>
|
||||
{
|
||||
hudOverlay = new HUDOverlay(null, Array.Empty<Mod>());
|
||||
|
||||
// Add any key just to display the key counter visually.
|
||||
hudOverlay.InputCountController.Add(new KeyCounterKeyboardTrigger(Key.Space));
|
||||
hudOverlay = new HUDOverlay(new DrawableOsuRuleset(new OsuRuleset(), new OsuBeatmap()), Array.Empty<Mod>());
|
||||
|
||||
action?.Invoke(hudOverlay);
|
||||
|
||||
|
@ -0,0 +1,42 @@
|
||||
// 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.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
public partial class TestSceneSkinnableKeyCounter : SkinnableHUDComponentTestScene
|
||||
{
|
||||
[Cached]
|
||||
private readonly InputCountController controller = new InputCountController();
|
||||
|
||||
public override void SetUpSteps()
|
||||
{
|
||||
AddStep("create dependencies", () =>
|
||||
{
|
||||
Add(controller);
|
||||
controller.Add(new KeyCounterKeyboardTrigger(Key.Z));
|
||||
controller.Add(new KeyCounterKeyboardTrigger(Key.X));
|
||||
controller.Add(new KeyCounterKeyboardTrigger(Key.C));
|
||||
controller.Add(new KeyCounterKeyboardTrigger(Key.V));
|
||||
|
||||
foreach (var trigger in controller.Triggers)
|
||||
Add(trigger);
|
||||
});
|
||||
base.SetUpSteps();
|
||||
}
|
||||
|
||||
protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset();
|
||||
|
||||
protected override Drawable CreateDefaultImplementation() => new ArgonKeyCounterDisplay();
|
||||
|
||||
protected override Drawable CreateLegacyImplementation() => new LegacyKeyCounterDisplay();
|
||||
}
|
||||
}
|
@ -93,14 +93,8 @@ namespace osu.Game.Beatmaps.Formats
|
||||
return line;
|
||||
}
|
||||
|
||||
protected void HandleColours<TModel>(TModel output, string line, bool allowAlpha)
|
||||
private Color4 convertSettingStringToColor4(string[] split, bool allowAlpha, KeyValuePair<string, string> pair)
|
||||
{
|
||||
var pair = SplitKeyVal(line);
|
||||
|
||||
bool isCombo = pair.Key.StartsWith(@"Combo", StringComparison.Ordinal);
|
||||
|
||||
string[] split = pair.Value.Split(',');
|
||||
|
||||
if (split.Length != 3 && split.Length != 4)
|
||||
throw new InvalidOperationException($@"Color specified in incorrect format (should be R,G,B or R,G,B,A): {pair.Value}");
|
||||
|
||||
@ -116,6 +110,18 @@ namespace osu.Game.Beatmaps.Formats
|
||||
throw new InvalidOperationException(@"Color must be specified with 8-bit integer components");
|
||||
}
|
||||
|
||||
return colour;
|
||||
}
|
||||
|
||||
protected void HandleColours<TModel>(TModel output, string line, bool allowAlpha)
|
||||
{
|
||||
var pair = SplitKeyVal(line);
|
||||
|
||||
string[] split = pair.Value.Split(',');
|
||||
Color4 colour = convertSettingStringToColor4(split, allowAlpha, pair);
|
||||
|
||||
bool isCombo = pair.Key.StartsWith(@"Combo", StringComparison.Ordinal);
|
||||
|
||||
if (isCombo)
|
||||
{
|
||||
if (!(output is IHasComboColours tHasComboColours)) return;
|
||||
|
@ -14,11 +14,10 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
public ArgonKeyCounterDisplay()
|
||||
{
|
||||
InternalChild = KeyFlow = new FillFlowContainer<KeyCounter>
|
||||
Child = KeyFlow = new FillFlowContainer<KeyCounter>
|
||||
{
|
||||
Direction = FillDirection.Horizontal,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Alpha = 0,
|
||||
Spacing = new Vector2(2),
|
||||
};
|
||||
}
|
||||
|
@ -16,11 +16,10 @@ namespace osu.Game.Screens.Play.HUD
|
||||
|
||||
public DefaultKeyCounterDisplay()
|
||||
{
|
||||
InternalChild = KeyFlow = new FillFlowContainer<KeyCounter>
|
||||
Child = KeyFlow = new FillFlowContainer<KeyCounter>
|
||||
{
|
||||
Direction = FillDirection.Horizontal,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Alpha = 0,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,7 @@ namespace osu.Game.Screens.Play.HUD
|
||||
/// <summary>
|
||||
/// A flowing display of all gameplay keys. Individual keys can be added using <see cref="InputTrigger"/> implementations.
|
||||
/// </summary>
|
||||
public abstract partial class KeyCounterDisplay : CompositeDrawable, ISerialisableDrawable
|
||||
public abstract partial class KeyCounterDisplay : Container, ISerialisableDrawable
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether the key counter should be visible regardless of the configuration value.
|
||||
@ -29,25 +29,22 @@ namespace osu.Game.Screens.Play.HUD
|
||||
|
||||
private readonly IBindableList<InputTrigger> triggers = new BindableList<InputTrigger>();
|
||||
|
||||
protected override Container<Drawable> Content { get; } = new Container
|
||||
{
|
||||
Alpha = 0,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
};
|
||||
|
||||
[Resolved]
|
||||
private InputCountController controller { get; set; } = null!;
|
||||
|
||||
private const int duration = 100;
|
||||
|
||||
protected void UpdateVisibility()
|
||||
protected KeyCounterDisplay()
|
||||
{
|
||||
bool visible = AlwaysVisible.Value || ConfigVisibility.Value;
|
||||
|
||||
// Isolate changing visibility of the key counters from fading this component.
|
||||
KeyFlow.FadeTo(visible ? 1 : 0, duration);
|
||||
|
||||
// Ensure a valid size is immediately obtained even if partially off-screen
|
||||
// See https://github.com/ppy/osu/issues/14793.
|
||||
KeyFlow.AlwaysPresent = visible;
|
||||
AddInternal(Content);
|
||||
}
|
||||
|
||||
protected abstract KeyCounter CreateCounter(InputTrigger trigger);
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuConfigManager config, DrawableRuleset? drawableRuleset)
|
||||
{
|
||||
@ -70,6 +67,20 @@ namespace osu.Game.Screens.Play.HUD
|
||||
ConfigVisibility.BindValueChanged(_ => UpdateVisibility(), true);
|
||||
}
|
||||
|
||||
protected void UpdateVisibility()
|
||||
{
|
||||
bool visible = AlwaysVisible.Value || ConfigVisibility.Value;
|
||||
|
||||
// Isolate changing visibility of the key counters from fading this component.
|
||||
Content.FadeTo(visible ? 1 : 0, duration);
|
||||
|
||||
// Ensure a valid size is immediately obtained even if partially off-screen
|
||||
// See https://github.com/ppy/osu/issues/14793.
|
||||
Content.AlwaysPresent = visible;
|
||||
}
|
||||
|
||||
protected abstract KeyCounter CreateCounter(InputTrigger trigger);
|
||||
|
||||
private void triggersChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
KeyFlow.Clear();
|
||||
|
@ -47,6 +47,9 @@ namespace osu.Game.Skinning
|
||||
/// <summary>
|
||||
/// Retrieve a configuration value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Note that while this returns a bindable value, it is not actually updated.
|
||||
/// Until the API is fixed, just use the received bindable's <see cref="IBindable{TValue}.Value"/> immediately.</remarks>
|
||||
/// <param name="lookup">The requested configuration value.</param>
|
||||
/// <returns>A matching value boxed in an <see cref="IBindable{TValue}"/>, or null if unavailable.</returns>
|
||||
IBindable<TValue>? GetConfig<TLookup, TValue>(TLookup lookup)
|
||||
|
101
osu.Game/Skinning/LegacyKeyCounter.cs
Normal file
101
osu.Game/Skinning/LegacyKeyCounter.cs
Normal file
@ -0,0 +1,101 @@
|
||||
// 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.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
|
||||
namespace osu.Game.Skinning
|
||||
{
|
||||
public partial class LegacyKeyCounter : KeyCounter
|
||||
{
|
||||
private const float transition_duration = 160;
|
||||
|
||||
public Colour4 ActiveColour { get; set; }
|
||||
|
||||
private Colour4 textColour;
|
||||
|
||||
public Colour4 TextColour
|
||||
{
|
||||
get => textColour;
|
||||
set
|
||||
{
|
||||
textColour = value;
|
||||
overlayKeyText.Colour = value;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Container keyContainer;
|
||||
private readonly OsuSpriteText overlayKeyText;
|
||||
private readonly Sprite keySprite;
|
||||
|
||||
public LegacyKeyCounter(InputTrigger trigger)
|
||||
: base(trigger)
|
||||
{
|
||||
Origin = Anchor.Centre;
|
||||
Anchor = Anchor.Centre;
|
||||
Child = keyContainer = new Container
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
keySprite = new Sprite
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
},
|
||||
new UprightAspectMaintainingContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Child = overlayKeyText = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Text = trigger.Name,
|
||||
Colour = textColour,
|
||||
Font = OsuFont.GetFont(weight: FontWeight.SemiBold),
|
||||
},
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
// matches longest dimension of default skin asset
|
||||
Height = Width = 46;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(ISkinSource source)
|
||||
{
|
||||
Texture? keyTexture = source.GetTexture(@"inputoverlay-key");
|
||||
|
||||
if (keyTexture != null)
|
||||
keySprite.Texture = keyTexture;
|
||||
}
|
||||
|
||||
protected override void Activate(bool forwardPlayback = true)
|
||||
{
|
||||
base.Activate(forwardPlayback);
|
||||
keyContainer.ScaleTo(0.75f, transition_duration, Easing.Out);
|
||||
keySprite.Colour = ActiveColour;
|
||||
overlayKeyText.Text = CountPresses.Value.ToString();
|
||||
overlayKeyText.Font = overlayKeyText.Font.With(weight: FontWeight.SemiBold);
|
||||
}
|
||||
|
||||
protected override void Deactivate(bool forwardPlayback = true)
|
||||
{
|
||||
base.Deactivate(forwardPlayback);
|
||||
keyContainer.ScaleTo(1f, transition_duration, Easing.Out);
|
||||
keySprite.Colour = Colour4.White;
|
||||
}
|
||||
}
|
||||
}
|
92
osu.Game/Skinning/LegacyKeyCounterDisplay.cs
Normal file
92
osu.Game/Skinning/LegacyKeyCounterDisplay.cs
Normal file
@ -0,0 +1,92 @@
|
||||
// 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.Framework.Graphics.Containers;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Skinning
|
||||
{
|
||||
public partial class LegacyKeyCounterDisplay : KeyCounterDisplay
|
||||
{
|
||||
private static readonly Colour4 active_colour_top = Colour4.FromHex(@"#ffde00");
|
||||
private static readonly Colour4 active_colour_bottom = Colour4.FromHex(@"#f8009e");
|
||||
|
||||
protected override FillFlowContainer<KeyCounter> KeyFlow { get; }
|
||||
|
||||
private readonly Sprite backgroundSprite;
|
||||
|
||||
public LegacyKeyCounterDisplay()
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
||||
AddRange(new Drawable[]
|
||||
{
|
||||
backgroundSprite = new Sprite
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopLeft,
|
||||
Scale = new Vector2(1.05f, 1),
|
||||
Rotation = 90,
|
||||
},
|
||||
KeyFlow = new FillFlowContainer<KeyCounter>
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
X = -1.5f,
|
||||
Y = 7,
|
||||
Spacing = new Vector2(1.8f),
|
||||
Direction = FillDirection.Vertical,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
[Resolved]
|
||||
private ISkinSource source { get; set; } = null!;
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
KeyTextColor = source.GetConfig<SkinCustomColourLookup, Color4>(new SkinCustomColourLookup(SkinConfiguration.LegacySetting.InputOverlayText))?.Value ?? Color4.Black;
|
||||
|
||||
Texture? backgroundTexture = source.GetTexture(@"inputoverlay-background");
|
||||
|
||||
if (backgroundTexture != null)
|
||||
backgroundSprite.Texture = backgroundTexture;
|
||||
|
||||
for (int i = 0; i < KeyFlow.Count; ++i)
|
||||
{
|
||||
((LegacyKeyCounter)KeyFlow[i]).ActiveColour = i < 2 ? active_colour_top : active_colour_bottom;
|
||||
}
|
||||
}
|
||||
|
||||
protected override KeyCounter CreateCounter(InputTrigger trigger) => new LegacyKeyCounter(trigger)
|
||||
{
|
||||
TextColour = keyTextColor,
|
||||
};
|
||||
|
||||
private Colour4 keyTextColor = Colour4.White;
|
||||
|
||||
public Colour4 KeyTextColor
|
||||
{
|
||||
get => keyTextColor;
|
||||
set
|
||||
{
|
||||
if (value != keyTextColor)
|
||||
{
|
||||
keyTextColor = value;
|
||||
foreach (var child in KeyFlow.Cast<LegacyKeyCounter>())
|
||||
child.TextColour = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -23,7 +23,6 @@ using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osu.Game.Screens.Play.HUD.HitErrorMeters;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Skinning
|
||||
@ -310,6 +309,9 @@ namespace osu.Game.Skinning
|
||||
case SkinConfiguration.LegacySetting.Version:
|
||||
return SkinUtils.As<TValue>(new Bindable<decimal>(Configuration.LegacyVersion ?? SkinConfiguration.LATEST_VERSION));
|
||||
|
||||
case SkinConfiguration.LegacySetting.InputOverlayText:
|
||||
return SkinUtils.As<TValue>(new Bindable<Colour4>(Configuration.CustomColours.TryGetValue(@"InputOverlayText", out var colour) ? colour : Colour4.Black));
|
||||
|
||||
default:
|
||||
return genericLookup<SkinConfiguration.LegacySetting, TValue>(legacySetting);
|
||||
}
|
||||
@ -381,22 +383,12 @@ namespace osu.Game.Skinning
|
||||
}
|
||||
|
||||
var hitError = container.OfType<HitErrorMeter>().FirstOrDefault();
|
||||
var keyCounter = container.OfType<DefaultKeyCounterDisplay>().FirstOrDefault();
|
||||
|
||||
if (hitError != null)
|
||||
{
|
||||
hitError.Anchor = Anchor.BottomCentre;
|
||||
hitError.Origin = Anchor.CentreLeft;
|
||||
hitError.Rotation = -90;
|
||||
|
||||
if (keyCounter != null)
|
||||
{
|
||||
const float padding = 10;
|
||||
|
||||
keyCounter.Anchor = Anchor.BottomRight;
|
||||
keyCounter.Origin = Anchor.BottomRight;
|
||||
keyCounter.Position = new Vector2(-padding, -(padding + hitError.Width));
|
||||
}
|
||||
}
|
||||
})
|
||||
{
|
||||
@ -408,7 +400,6 @@ namespace osu.Game.Skinning
|
||||
new LegacySongProgress(),
|
||||
new LegacyHealthDisplay(),
|
||||
new BarHitErrorMeter(),
|
||||
new DefaultKeyCounterDisplay()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ namespace osu.Game.Skinning
|
||||
AnimationFramerate,
|
||||
LayeredHitSounds,
|
||||
AllowSliderBallTint,
|
||||
InputOverlayText,
|
||||
}
|
||||
|
||||
public static List<Color4> DefaultComboColours { get; } = new List<Color4>
|
||||
|
Loading…
x
Reference in New Issue
Block a user