2019-08-30 03:59:58 +00:00
|
|
|
// 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;
|
2024-08-06 09:17:21 +00:00
|
|
|
using System.Linq;
|
2019-09-03 08:57:34 +00:00
|
|
|
using osu.Framework.Bindables;
|
2019-08-30 03:59:58 +00:00
|
|
|
using osu.Framework.Graphics;
|
2023-09-19 00:57:22 +00:00
|
|
|
using osu.Game.Rulesets.Osu.Objects;
|
2019-08-30 03:59:58 +00:00
|
|
|
using osu.Game.Skinning;
|
|
|
|
using osuTK;
|
|
|
|
|
2020-12-07 03:27:12 +00:00
|
|
|
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
2019-08-30 03:59:58 +00:00
|
|
|
{
|
2020-06-21 20:04:10 +00:00
|
|
|
public class OsuLegacySkinTransformer : LegacySkinTransformer
|
2019-08-30 03:59:58 +00:00
|
|
|
{
|
2022-10-12 08:47:20 +00:00
|
|
|
public override bool IsProvidingLegacyResources => base.IsProvidingLegacyResources || hasHitCircle.Value;
|
|
|
|
|
2021-06-09 10:34:42 +00:00
|
|
|
private readonly Lazy<bool> hasHitCircle;
|
2019-08-30 03:59:58 +00:00
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// On osu-stable, hitcircles have 5 pixels of transparent padding on each side to allow for shadows etc.
|
|
|
|
/// Their hittable area is 128px, but the actual circle portion is 118px.
|
|
|
|
/// We must account for some gameplay elements such as slider bodies, where this padding is not present.
|
|
|
|
/// </summary>
|
2023-09-19 00:57:22 +00:00
|
|
|
public const float LEGACY_CIRCLE_RADIUS = OsuHitObject.OBJECT_RADIUS - 5;
|
2019-08-30 03:59:58 +00:00
|
|
|
|
2023-10-05 20:33:49 +00:00
|
|
|
/// <summary>
|
|
|
|
/// The maximum allowed size of sprites that reside in the follow circle area of a slider.
|
|
|
|
/// </summary>
|
|
|
|
/// <remarks>
|
|
|
|
/// The reason this is extracted out to a constant, rather than be inlined in the follow circle sprite retrieval,
|
|
|
|
/// is that some skins will use `sliderb` elements to emulate a slider follow circle with slightly different visual effects applied
|
|
|
|
/// (`sliderb` is always shown and doesn't pulsate; `sliderfollowcircle` isn't always shown and pulsates).
|
|
|
|
/// </remarks>
|
|
|
|
public static readonly Vector2 MAX_FOLLOW_CIRCLE_AREA_SIZE = OsuHitObject.OBJECT_DIMENSIONS * 3;
|
|
|
|
|
2021-06-09 10:34:42 +00:00
|
|
|
public OsuLegacySkinTransformer(ISkin skin)
|
|
|
|
: base(skin)
|
2019-08-30 03:59:58 +00:00
|
|
|
{
|
2021-06-11 09:44:44 +00:00
|
|
|
hasHitCircle = new Lazy<bool>(() => GetTexture("hitcircle") != null);
|
2019-08-30 03:59:58 +00:00
|
|
|
}
|
|
|
|
|
2022-11-09 07:04:56 +00:00
|
|
|
public override Drawable? GetDrawableComponent(ISkinComponentLookup lookup)
|
2019-08-30 03:59:58 +00:00
|
|
|
{
|
2024-08-06 09:17:21 +00:00
|
|
|
switch (lookup)
|
2019-08-30 03:59:58 +00:00
|
|
|
{
|
2024-08-06 09:17:21 +00:00
|
|
|
case SkinComponentsContainerLookup containerLookup:
|
|
|
|
// Only handle per ruleset defaults here.
|
|
|
|
if (containerLookup.Ruleset == null)
|
|
|
|
return base.GetDrawableComponent(lookup);
|
|
|
|
|
|
|
|
// Skin has configuration.
|
2024-08-08 07:29:54 +00:00
|
|
|
if (base.GetDrawableComponent(lookup) is UserConfiguredLayoutContainer d)
|
2024-08-06 09:17:21 +00:00
|
|
|
return d;
|
|
|
|
|
2024-08-15 07:16:52 +00:00
|
|
|
// we don't have enough assets to display these components (this is especially the case on a "beatmap" skin).
|
|
|
|
if (!IsProvidingLegacyResources)
|
|
|
|
return null;
|
|
|
|
|
2024-08-06 09:17:21 +00:00
|
|
|
// Our own ruleset components default.
|
|
|
|
switch (containerLookup.Target)
|
|
|
|
{
|
|
|
|
case SkinComponentsContainerLookup.TargetArea.MainHUDComponents:
|
|
|
|
return new DefaultSkinComponentsContainer(container =>
|
|
|
|
{
|
|
|
|
var keyCounter = container.OfType<LegacyKeyCounterDisplay>().FirstOrDefault();
|
|
|
|
|
|
|
|
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;
|
2024-08-20 15:02:05 +00:00
|
|
|
keyCounter.Origin = Anchor.TopRight;
|
|
|
|
keyCounter.Position = new Vector2(0, -40) * 1.6f;
|
2024-08-06 09:17:21 +00:00
|
|
|
}
|
2024-08-12 05:27:21 +00:00
|
|
|
|
|
|
|
var combo = container.OfType<LegacyDefaultComboCounter>().FirstOrDefault();
|
|
|
|
|
|
|
|
if (combo != null)
|
|
|
|
{
|
|
|
|
combo.Anchor = Anchor.BottomLeft;
|
|
|
|
combo.Origin = Anchor.BottomLeft;
|
|
|
|
combo.Scale = new Vector2(1.28f);
|
|
|
|
}
|
2024-08-06 09:17:21 +00:00
|
|
|
})
|
|
|
|
{
|
|
|
|
Children = new Drawable[]
|
|
|
|
{
|
2024-08-12 05:27:21 +00:00
|
|
|
new LegacyDefaultComboCounter(),
|
2024-08-06 09:17:21 +00:00
|
|
|
new LegacyKeyCounterDisplay(),
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
case OsuSkinComponentLookup osuComponent:
|
|
|
|
switch (osuComponent.Component)
|
|
|
|
{
|
|
|
|
case OsuSkinComponents.FollowPoint:
|
2024-08-12 05:27:21 +00:00
|
|
|
return this.GetAnimation("followpoint", true, true, true, startAtCurrentTime: false,
|
|
|
|
maxSize: new Vector2(OsuHitObject.OBJECT_RADIUS * 2, OsuHitObject.OBJECT_RADIUS));
|
2024-08-06 09:17:21 +00:00
|
|
|
|
|
|
|
case OsuSkinComponents.SliderScorePoint:
|
|
|
|
return this.GetAnimation("sliderscorepoint", false, false, maxSize: OsuHitObject.OBJECT_DIMENSIONS);
|
|
|
|
|
|
|
|
case OsuSkinComponents.SliderFollowCircle:
|
|
|
|
var followCircleContent = this.GetAnimation("sliderfollowcircle", true, true, true, maxSize: MAX_FOLLOW_CIRCLE_AREA_SIZE);
|
|
|
|
if (followCircleContent != null)
|
|
|
|
return new LegacyFollowCircle(followCircleContent);
|
2019-08-30 05:40:36 +00:00
|
|
|
|
2024-08-06 09:17:21 +00:00
|
|
|
return null;
|
2022-04-22 22:08:53 +00:00
|
|
|
|
2024-08-06 09:17:21 +00:00
|
|
|
case OsuSkinComponents.SliderBall:
|
|
|
|
if (GetTexture("sliderb") != null || GetTexture("sliderb0") != null)
|
|
|
|
return new LegacySliderBall(this);
|
2022-06-29 08:23:35 +00:00
|
|
|
|
2024-08-06 09:17:21 +00:00
|
|
|
return null;
|
2020-02-07 05:58:29 +00:00
|
|
|
|
2024-08-06 09:17:21 +00:00
|
|
|
case OsuSkinComponents.SliderBody:
|
|
|
|
if (hasHitCircle.Value)
|
|
|
|
return new LegacySliderBody();
|
2019-08-30 03:59:58 +00:00
|
|
|
|
2024-08-06 09:17:21 +00:00
|
|
|
return null;
|
2019-12-17 09:16:25 +00:00
|
|
|
|
2024-08-06 09:17:21 +00:00
|
|
|
case OsuSkinComponents.SliderTailHitCircle:
|
|
|
|
if (hasHitCircle.Value)
|
|
|
|
return new LegacyMainCirclePiece("sliderendcircle", false);
|
2019-12-17 09:16:25 +00:00
|
|
|
|
2024-08-06 09:17:21 +00:00
|
|
|
return null;
|
2020-10-02 04:41:22 +00:00
|
|
|
|
2024-08-06 09:17:21 +00:00
|
|
|
case OsuSkinComponents.SliderHeadHitCircle:
|
|
|
|
if (hasHitCircle.Value)
|
|
|
|
return new LegacySliderHeadHitCircle();
|
2020-10-02 04:41:22 +00:00
|
|
|
|
2024-08-06 09:17:21 +00:00
|
|
|
return null;
|
2020-03-28 04:39:08 +00:00
|
|
|
|
2024-08-06 09:17:21 +00:00
|
|
|
case OsuSkinComponents.ReverseArrow:
|
|
|
|
if (hasHitCircle.Value)
|
|
|
|
return new LegacyReverseArrow();
|
2021-09-01 10:34:57 +00:00
|
|
|
|
2024-08-06 09:17:21 +00:00
|
|
|
return null;
|
2021-09-01 10:34:57 +00:00
|
|
|
|
2024-08-06 09:17:21 +00:00
|
|
|
case OsuSkinComponents.HitCircle:
|
|
|
|
if (hasHitCircle.Value)
|
|
|
|
return new LegacyMainCirclePiece();
|
2020-03-28 04:39:08 +00:00
|
|
|
|
2024-08-06 09:17:21 +00:00
|
|
|
return null;
|
2019-08-30 03:59:58 +00:00
|
|
|
|
2024-08-06 09:17:21 +00:00
|
|
|
case OsuSkinComponents.Cursor:
|
|
|
|
if (GetTexture("cursor") != null)
|
|
|
|
return new LegacyCursor(this);
|
2019-08-30 04:42:29 +00:00
|
|
|
|
2024-08-06 09:17:21 +00:00
|
|
|
return null;
|
2019-08-30 04:42:29 +00:00
|
|
|
|
2024-08-06 09:17:21 +00:00
|
|
|
case OsuSkinComponents.CursorTrail:
|
|
|
|
if (GetTexture("cursortrail") != null)
|
|
|
|
return new LegacyCursorTrail(this);
|
2019-08-30 04:42:29 +00:00
|
|
|
|
2024-08-06 09:17:21 +00:00
|
|
|
return null;
|
2019-09-09 08:53:51 +00:00
|
|
|
|
2024-08-06 09:17:21 +00:00
|
|
|
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;
|
|
|
|
}
|
2019-09-09 08:53:51 +00:00
|
|
|
|
2024-08-06 09:17:21 +00:00
|
|
|
return null;
|
2021-03-06 23:18:31 +00:00
|
|
|
|
2024-08-06 09:17:21 +00:00
|
|
|
case OsuSkinComponents.CursorParticles:
|
|
|
|
if (GetTexture("star2") != null)
|
|
|
|
return new LegacyCursorParticles();
|
2023-04-30 02:19:03 +00:00
|
|
|
|
2024-08-06 09:17:21 +00:00
|
|
|
return null;
|
2023-04-30 02:19:03 +00:00
|
|
|
|
2024-08-06 09:17:21 +00:00
|
|
|
case OsuSkinComponents.CursorSmoke:
|
|
|
|
if (GetTexture("cursor-smoke") != null)
|
|
|
|
return new LegacySmokeSegment();
|
2023-04-29 07:05:45 +00:00
|
|
|
|
2024-08-06 09:17:21 +00:00
|
|
|
return null;
|
2023-04-29 07:06:08 +00:00
|
|
|
|
2024-08-06 09:17:21 +00:00
|
|
|
case OsuSkinComponents.HitCircleText:
|
|
|
|
if (!this.HasFont(LegacyFont.HitCircle))
|
|
|
|
return null;
|
2021-09-12 21:45:50 +00:00
|
|
|
|
2024-08-06 09:17:21 +00:00
|
|
|
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,
|
|
|
|
};
|
2021-09-09 22:29:05 +00:00
|
|
|
|
2024-08-06 09:17:21 +00:00
|
|
|
case OsuSkinComponents.SpinnerBody:
|
|
|
|
bool hasBackground = GetTexture("spinner-background") != null;
|
2022-09-18 19:08:34 +00:00
|
|
|
|
2024-08-06 09:17:21 +00:00
|
|
|
if (GetTexture("spinner-top") != null && !hasBackground)
|
|
|
|
return new LegacyNewStyleSpinner();
|
|
|
|
else if (hasBackground)
|
|
|
|
return new LegacyOldStyleSpinner();
|
2022-09-18 19:08:34 +00:00
|
|
|
|
2021-05-30 10:15:59 +00:00
|
|
|
return null;
|
2020-07-29 07:25:10 +00:00
|
|
|
|
2024-08-06 09:17:21 +00:00
|
|
|
case OsuSkinComponents.ApproachCircle:
|
|
|
|
if (GetTexture(@"approachcircle") != null)
|
|
|
|
return new LegacyApproachCircle();
|
2020-07-29 07:55:42 +00:00
|
|
|
|
2024-08-06 09:17:21 +00:00
|
|
|
return null;
|
2024-01-17 13:35:39 +00:00
|
|
|
|
2024-08-06 09:17:21 +00:00
|
|
|
default:
|
|
|
|
throw new UnsupportedSkinComponentException(lookup);
|
|
|
|
}
|
2022-04-22 22:08:53 +00:00
|
|
|
|
2024-08-06 09:17:21 +00:00
|
|
|
default:
|
|
|
|
return base.GetDrawableComponent(lookup);
|
2021-05-27 02:15:29 +00:00
|
|
|
}
|
2019-08-30 03:59:58 +00:00
|
|
|
}
|
|
|
|
|
2022-11-09 04:36:52 +00:00
|
|
|
public override IBindable<TValue>? GetConfig<TLookup, TValue>(TLookup lookup)
|
2019-09-02 03:36:08 +00:00
|
|
|
{
|
2019-09-03 08:57:34 +00:00
|
|
|
switch (lookup)
|
|
|
|
{
|
|
|
|
case OsuSkinColour colour:
|
2021-06-11 09:44:44 +00:00
|
|
|
return base.GetConfig<SkinCustomColourLookup, TValue>(new SkinCustomColourLookup(colour));
|
2019-09-03 08:57:34 +00:00
|
|
|
|
|
|
|
case OsuSkinConfiguration osuLookup:
|
|
|
|
switch (osuLookup)
|
|
|
|
{
|
|
|
|
case OsuSkinConfiguration.SliderPathRadius:
|
|
|
|
if (hasHitCircle.Value)
|
2019-12-18 06:39:36 +00:00
|
|
|
return SkinUtils.As<TValue>(new BindableFloat(LEGACY_CIRCLE_RADIUS));
|
2019-09-03 08:57:34 +00:00
|
|
|
|
|
|
|
break;
|
2020-04-04 08:13:25 +00:00
|
|
|
|
|
|
|
case OsuSkinConfiguration.HitCircleOverlayAboveNumber:
|
2020-04-05 05:13:06 +00:00
|
|
|
// See https://osu.ppy.sh/help/wiki/Skinning/skin.ini#%5Bgeneral%5D
|
|
|
|
// HitCircleOverlayAboveNumer (with typo) should still be supported for now.
|
2021-06-11 09:44:44 +00:00
|
|
|
return base.GetConfig<OsuSkinConfiguration, TValue>(OsuSkinConfiguration.HitCircleOverlayAboveNumber) ??
|
|
|
|
base.GetConfig<OsuSkinConfiguration, TValue>(OsuSkinConfiguration.HitCircleOverlayAboveNumer);
|
2019-09-03 08:57:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
2019-09-02 03:36:08 +00:00
|
|
|
|
2021-06-11 09:44:44 +00:00
|
|
|
return base.GetConfig<TLookup, TValue>(lookup);
|
2019-09-02 03:36:08 +00:00
|
|
|
}
|
2019-08-30 03:59:58 +00:00
|
|
|
}
|
|
|
|
}
|