1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-27 11:12:59 +08:00

Merge branch 'legacy-skin-default-fallback' into fix-skin-sample-lookup

This commit is contained in:
Dean Herbert 2021-06-08 15:19:52 +09:00
commit e0f568aa8f
11 changed files with 78 additions and 40 deletions

View File

@ -14,18 +14,21 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
{ {
private readonly Drawable animationContent; private readonly Drawable animationContent;
private readonly ISkin skin;
private Sprite layerNd; private Sprite layerNd;
private Sprite layerSpec; private Sprite layerSpec;
public LegacySliderBall(Drawable animationContent) public LegacySliderBall(Drawable animationContent, ISkin skin)
{ {
this.animationContent = animationContent; this.animationContent = animationContent;
this.skin = skin;
AutoSizeAxes = Axes.Both; AutoSizeAxes = Axes.Both;
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(ISkinSource skin) private void load()
{ {
var ballColour = skin.GetConfig<OsuSkinColour, Color4>(OsuSkinColour.SliderBall)?.Value ?? Color4.White; var ballColour = skin.GetConfig<OsuSkinColour, Color4>(OsuSkinColour.SliderBall)?.Value ?? Color4.White;

View File

@ -49,13 +49,16 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
return followCircle; return followCircle;
case OsuSkinComponents.SliderBall: case OsuSkinComponents.SliderBall:
var sliderBallContent = this.GetAnimation("sliderb", true, true, animationSeparator: ""); // specular and nd layers must come from the same source as the ball texure.
var ballProvider = Source.FindProvider(s => s.GetTexture("sliderb") != null || s.GetTexture("sliderb0") != null);
var sliderBallContent = ballProvider.GetAnimation("sliderb", true, true, animationSeparator: "");
// todo: slider ball has a custom frame delay based on velocity // todo: slider ball has a custom frame delay based on velocity
// Math.Max((150 / Velocity) * GameBase.SIXTY_FRAME_TIME, GameBase.SIXTY_FRAME_TIME); // Math.Max((150 / Velocity) * GameBase.SIXTY_FRAME_TIME, GameBase.SIXTY_FRAME_TIME);
if (sliderBallContent != null) if (sliderBallContent != null)
return new LegacySliderBall(sliderBallContent); return new LegacySliderBall(sliderBallContent, ballProvider);
return null; return null;

View File

@ -85,8 +85,12 @@ namespace osu.Game.Rulesets.Taiko.UI
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(ISkinSource skin) private void load(ISkinSource source)
{ {
ISkin skin = source.FindProvider(s => getAnimationFrame(s, state, 0) != null);
if (skin == null) return;
for (int frameIndex = 0; true; frameIndex++) for (int frameIndex = 0; true; frameIndex++)
{ {
var texture = getAnimationFrame(skin, state, frameIndex); var texture = getAnimationFrame(skin, state, frameIndex);
@ -112,8 +116,12 @@ namespace osu.Game.Rulesets.Taiko.UI
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(ISkinSource skin) private void load(ISkinSource source)
{ {
ISkin skin = source.FindProvider(s => getAnimationFrame(s, TaikoMascotAnimationState.Clear, 0) != null);
if (skin == null) return;
foreach (var frameIndex in clear_animation_sequence) foreach (var frameIndex in clear_animation_sequence)
{ {
var texture = getAnimationFrame(skin, TaikoMascotAnimationState.Clear, frameIndex); var texture = getAnimationFrame(skin, TaikoMascotAnimationState.Clear, frameIndex);

View File

@ -31,8 +31,6 @@ namespace osu.Game.Skinning
Configuration.LegacyVersion = 2.7m; Configuration.LegacyVersion = 2.7m;
} }
protected override DefaultLegacySkin CreateFallbackSkin(IResourceStore<byte[]> storage, IStorageResourceProvider resources) => null;
public static SkinInfo Info { get; } = new SkinInfo public static SkinInfo Info { get; } = new SkinInfo
{ {
ID = SkinInfo.CLASSIC_SKIN, // this is temporary until database storage is decided upon. ID = SkinInfo.CLASSIC_SKIN, // this is temporary until database storage is decided upon.

View File

@ -70,14 +70,6 @@ namespace osu.Game.Skinning
return base.GetSample(sampleInfo); return base.GetSample(sampleInfo);
} }
protected override DefaultLegacySkin CreateFallbackSkin(IResourceStore<byte[]> storage, IStorageResourceProvider resources)
{
// for simplicity, beatmap skins don't do lookups on the default skin.
// this will mean that fallback always occurs to the user (then default) skin.
// this may not offer perfect behaviour, but helps keep things simple.
return null;
}
private static SkinInfo createSkinInfo(BeatmapInfo beatmap) => private static SkinInfo createSkinInfo(BeatmapInfo beatmap) =>
new SkinInfo { Name = beatmap.ToString(), Creator = beatmap.Metadata?.AuthorString }; new SkinInfo { Name = beatmap.ToString(), Creator = beatmap.Metadata?.AuthorString };
} }

View File

@ -20,9 +20,6 @@ namespace osu.Game.Skinning
{ {
private const double epic_cutoff = 0.5; private const double epic_cutoff = 0.5;
[Resolved]
private ISkinSource skin { get; set; }
private LegacyHealthPiece fill; private LegacyHealthPiece fill;
private LegacyHealthPiece marker; private LegacyHealthPiece marker;
@ -31,14 +28,14 @@ namespace osu.Game.Skinning
private bool isNewStyle; private bool isNewStyle;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load(ISkinSource source)
{ {
AutoSizeAxes = Axes.Both; AutoSizeAxes = Axes.Both;
var backgroundSource = skin.FindProvider(s => getTexture(s, "bg") != null); var skin = source.FindProvider(s => getTexture(s, "bg") != null);
// the marker lookup to decide which display style must be performed on the source of the bg, which is the most common element. // the marker lookup to decide which display style must be performed on the source of the bg, which is the most common element.
isNewStyle = getTexture(backgroundSource, "marker") != null; isNewStyle = getTexture(skin, "marker") != null;
// background implementation is the same for both versions. // background implementation is the same for both versions.
AddInternal(new Sprite { Texture = getTexture(skin, "bg") }); AddInternal(new Sprite { Texture = getTexture(skin, "bg") });
@ -98,7 +95,7 @@ namespace osu.Game.Skinning
private readonly Texture dangerTexture; private readonly Texture dangerTexture;
private readonly Texture superDangerTexture; private readonly Texture superDangerTexture;
public LegacyOldStyleMarker(ISkinSource skin) public LegacyOldStyleMarker(ISkin skin)
{ {
normalTexture = getTexture(skin, "ki"); normalTexture = getTexture(skin, "ki");
dangerTexture = getTexture(skin, "kidanger"); dangerTexture = getTexture(skin, "kidanger");
@ -129,9 +126,9 @@ namespace osu.Game.Skinning
public class LegacyNewStyleMarker : LegacyMarker public class LegacyNewStyleMarker : LegacyMarker
{ {
private readonly ISkinSource skin; private readonly ISkin skin;
public LegacyNewStyleMarker(ISkinSource skin) public LegacyNewStyleMarker(ISkin skin)
{ {
this.skin = skin; this.skin = skin;
} }
@ -153,7 +150,7 @@ namespace osu.Game.Skinning
internal class LegacyOldStyleFill : LegacyHealthPiece internal class LegacyOldStyleFill : LegacyHealthPiece
{ {
public LegacyOldStyleFill(ISkinSource skin) public LegacyOldStyleFill(ISkin skin)
{ {
// required for sizing correctly.. // required for sizing correctly..
var firstFrame = getTexture(skin, "colour-0"); var firstFrame = getTexture(skin, "colour-0");
@ -176,7 +173,7 @@ namespace osu.Game.Skinning
internal class LegacyNewStyleFill : LegacyHealthPiece internal class LegacyNewStyleFill : LegacyHealthPiece
{ {
public LegacyNewStyleFill(ISkinSource skin) public LegacyNewStyleFill(ISkin skin)
{ {
InternalChild = new Sprite InternalChild = new Sprite
{ {

View File

@ -109,9 +109,6 @@ namespace osu.Game.Skinning
true) != null); true) != null);
} }
[CanBeNull]
protected virtual DefaultLegacySkin CreateFallbackSkin(IResourceStore<byte[]> storage, IStorageResourceProvider resources) => new DefaultLegacySkin(resources);
public override IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) public override IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
{ {
switch (lookup) switch (lookup)

View File

@ -27,6 +27,18 @@ namespace osu.Game.Skinning
{ {
Texture texture; Texture texture;
// find the first source which provides either the animated or non-animated version.
ISkin skin = (source as ISkinSource)?.FindProvider(s =>
{
if (animatable && s.GetTexture(getFrameName(0)) != null)
return true;
return s.GetTexture(componentName, wrapModeS, wrapModeT) != null;
}) ?? source;
if (skin == null)
return null;
if (animatable) if (animatable)
{ {
var textures = getTextures().ToArray(); var textures = getTextures().ToArray();
@ -35,7 +47,7 @@ namespace osu.Game.Skinning
{ {
var animation = new SkinnableTextureAnimation(startAtCurrentTime) var animation = new SkinnableTextureAnimation(startAtCurrentTime)
{ {
DefaultFrameLength = frameLength ?? getFrameLength(source, applyConfigFrameRate, textures), DefaultFrameLength = frameLength ?? getFrameLength(skin, applyConfigFrameRate, textures),
Loop = looping, Loop = looping,
}; };
@ -47,7 +59,7 @@ namespace osu.Game.Skinning
} }
// if an animation was not allowed or not found, fall back to a sprite retrieval. // if an animation was not allowed or not found, fall back to a sprite retrieval.
if ((texture = source.GetTexture(componentName, wrapModeS, wrapModeT)) != null) if ((texture = skin.GetTexture(componentName, wrapModeS, wrapModeT)) != null)
return new Sprite { Texture = texture }; return new Sprite { Texture = texture };
return null; return null;
@ -56,12 +68,14 @@ namespace osu.Game.Skinning
{ {
for (int i = 0; true; i++) for (int i = 0; true; i++)
{ {
if ((texture = source.GetTexture($"{componentName}{animationSeparator}{i}", wrapModeS, wrapModeT)) == null) if ((texture = skin.GetTexture(getFrameName(i), wrapModeS, wrapModeT)) == null)
break; break;
yield return texture; yield return texture;
} }
} }
string getFrameName(int frameIndex) => $"{componentName}{animationSeparator}{frameIndex}";
} }
public static bool HasFont(this ISkin source, LegacyFont font) public static bool HasFont(this ISkin source, LegacyFont font)

View File

@ -16,7 +16,7 @@ namespace osu.Game.Skinning
/// <summary> /// <summary>
/// Transformer used to handle support of legacy features for individual rulesets. /// Transformer used to handle support of legacy features for individual rulesets.
/// </summary> /// </summary>
public abstract class LegacySkinTransformer : ISkin public abstract class LegacySkinTransformer : ISkinSource
{ {
/// <summary> /// <summary>
/// Source of the <see cref="ISkin"/> which is being transformed. /// Source of the <see cref="ISkin"/> which is being transformed.
@ -50,5 +50,11 @@ namespace osu.Game.Skinning
public abstract IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup); public abstract IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup);
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => Source.FindProvider(lookupFunction); public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => Source.FindProvider(lookupFunction);
public event Action SourceChanged
{
add { throw new NotSupportedException(); }
remove { }
}
} }
} }

View File

@ -48,6 +48,8 @@ namespace osu.Game.Skinning
protected override string ImportFromStablePath => "Skins"; protected override string ImportFromStablePath => "Skins";
private readonly Skin defaultLegacySkin;
public SkinManager(Storage storage, DatabaseContextFactory contextFactory, GameHost host, IResourceStore<byte[]> resources, AudioManager audio) public SkinManager(Storage storage, DatabaseContextFactory contextFactory, GameHost host, IResourceStore<byte[]> resources, AudioManager audio)
: base(storage, contextFactory, new SkinStore(contextFactory, storage), host) : base(storage, contextFactory, new SkinStore(contextFactory, storage), host)
{ {
@ -55,6 +57,8 @@ namespace osu.Game.Skinning
this.host = host; this.host = host;
this.resources = resources; this.resources = resources;
defaultLegacySkin = new DefaultLegacySkin(this);
CurrentSkinInfo.ValueChanged += skin => CurrentSkin.Value = GetSkin(skin.NewValue); CurrentSkinInfo.ValueChanged += skin => CurrentSkin.Value = GetSkin(skin.NewValue);
CurrentSkin.Value = new DefaultSkin(this); CurrentSkin.Value = new DefaultSkin(this);
@ -214,9 +218,16 @@ namespace osu.Game.Skinning
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => lookupWithFallback(s => s.GetConfig<TLookup, TValue>(lookup)); public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => lookupWithFallback(s => s.GetConfig<TLookup, TValue>(lookup));
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => lookupFunction(CurrentSkin.Value) ? CurrentSkin.Value : null; public ISkin FindProvider(Func<ISkin, bool> lookupFunction)
{
if (lookupFunction(CurrentSkin.Value))
return CurrentSkin.Value;
private Skin defaultLegacySkin; if (CurrentSkin.Value is LegacySkin && lookupFunction(defaultLegacySkin))
return defaultLegacySkin;
return null;
}
private T lookupWithFallback<T>(Func<ISkin, T> func) private T lookupWithFallback<T>(Func<ISkin, T> func)
where T : class where T : class
@ -226,8 +237,9 @@ namespace osu.Game.Skinning
if (selectedSkin != null) if (selectedSkin != null)
return selectedSkin; return selectedSkin;
defaultLegacySkin ??= new DefaultLegacySkin(this); // TODO: we also want to return a DefaultLegacySkin here if the current *beatmap* is providing any skinned elements.
// When attempting to address this, we may want to move the full DefaultLegacySkin fallback logic to within Player itself (to better allow
// for beatmap skin visibility).
if (CurrentSkin.Value is LegacySkin) if (CurrentSkin.Value is LegacySkin)
return func(defaultLegacySkin); return func(defaultLegacySkin);

View File

@ -46,8 +46,16 @@ namespace osu.Game.Skinning
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) public ISkin FindProvider(Func<ISkin, bool> lookupFunction)
{ {
if (skin != null && lookupFunction(skin)) if (skin is ISkinSource source)
return skin; {
if (source.FindProvider(lookupFunction) is ISkin found)
return found;
}
else if (skin != null)
{
if (lookupFunction(skin))
return skin;
}
return fallbackSource?.FindProvider(lookupFunction); return fallbackSource?.FindProvider(lookupFunction);
} }