mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 18:42:56 +08:00
Merge pull request #13264 from peppy/legacy-skin-default-fallback
Allow fallback to legacy defaults from legacy skins
This commit is contained in:
commit
e5e3509c87
@ -69,10 +69,10 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
||||
|
||||
private void sourceChanged()
|
||||
{
|
||||
isLegacySkin = new Lazy<bool>(() => Source.GetConfig<LegacySkinConfiguration.LegacySetting, decimal>(LegacySkinConfiguration.LegacySetting.Version) != null);
|
||||
hasKeyTexture = new Lazy<bool>(() => Source.GetAnimation(
|
||||
isLegacySkin = new Lazy<bool>(() => FindProvider(s => s.GetConfig<LegacySkinConfiguration.LegacySetting, decimal>(LegacySkinConfiguration.LegacySetting.Version) != null) != null);
|
||||
hasKeyTexture = new Lazy<bool>(() => FindProvider(s => s.GetAnimation(
|
||||
this.GetManiaSkinConfig<string>(LegacyManiaSkinConfigurationLookups.KeyImage, 0)?.Value
|
||||
?? "mania-key1", true, true) != null);
|
||||
?? "mania-key1", true, true) != null) != null);
|
||||
}
|
||||
|
||||
public override Drawable GetDrawableComponent(ISkinComponent component)
|
||||
|
@ -0,0 +1,10 @@
|
||||
osu file format v14
|
||||
|
||||
[General]
|
||||
Mode: 0
|
||||
|
||||
[TimingPoints]
|
||||
0,300,4,1,2,100,1,0
|
||||
|
||||
[HitObjects]
|
||||
444,320,1000,5,0,0:0:0:0:
|
@ -39,18 +39,28 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
[Test]
|
||||
public void TestLegacySmoothCursorTrail()
|
||||
{
|
||||
createTest(() => new LegacySkinContainer(false)
|
||||
createTest(() =>
|
||||
{
|
||||
Child = new LegacyCursorTrail()
|
||||
var skinContainer = new LegacySkinContainer(false);
|
||||
var legacyCursorTrail = new LegacyCursorTrail(skinContainer);
|
||||
|
||||
skinContainer.Child = legacyCursorTrail;
|
||||
|
||||
return skinContainer;
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLegacyDisjointCursorTrail()
|
||||
{
|
||||
createTest(() => new LegacySkinContainer(true)
|
||||
createTest(() =>
|
||||
{
|
||||
Child = new LegacyCursorTrail()
|
||||
var skinContainer = new LegacySkinContainer(true);
|
||||
var legacyCursorTrail = new LegacyCursorTrail(skinContainer);
|
||||
|
||||
skinContainer.Child = legacyCursorTrail;
|
||||
|
||||
return skinContainer;
|
||||
});
|
||||
}
|
||||
|
||||
@ -102,6 +112,8 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => null;
|
||||
|
||||
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => null;
|
||||
|
||||
public event Action SourceChanged
|
||||
{
|
||||
add { }
|
||||
|
@ -113,6 +113,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
public Drawable GetDrawableComponent(ISkinComponent component) => null;
|
||||
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => null;
|
||||
public ISample GetSample(ISampleInfo sampleInfo) => null;
|
||||
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => null;
|
||||
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
|
||||
{
|
||||
|
49
osu.Game.Rulesets.Osu.Tests/TestSceneOsuHitObjectSamples.cs
Normal file
49
osu.Game.Rulesets.Osu.Tests/TestSceneOsuHitObjectSamples.cs
Normal file
@ -0,0 +1,49 @@
|
||||
// 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.Reflection;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
public class TestSceneOsuHitObjectSamples : HitObjectSampleTest
|
||||
{
|
||||
protected override Ruleset CreatePlayerRuleset() => new OsuRuleset();
|
||||
|
||||
protected override IResourceStore<byte[]> RulesetResources => new DllResourceStore(Assembly.GetAssembly(typeof(TestSceneOsuHitObjectSamples)));
|
||||
|
||||
[TestCase("normal-hitnormal")]
|
||||
[TestCase("hitnormal")]
|
||||
public void TestDefaultCustomSampleFromBeatmap(string expectedSample)
|
||||
{
|
||||
SetupSkins(expectedSample, expectedSample);
|
||||
|
||||
CreateTestWithBeatmap("osu-hitobject-beatmap-custom-sample-bank.osu");
|
||||
|
||||
AssertBeatmapLookup(expectedSample);
|
||||
}
|
||||
|
||||
[TestCase("normal-hitnormal")]
|
||||
[TestCase("hitnormal")]
|
||||
public void TestDefaultCustomSampleFromUserSkinFallback(string expectedSample)
|
||||
{
|
||||
SetupSkins(string.Empty, expectedSample);
|
||||
|
||||
CreateTestWithBeatmap("osu-hitobject-beatmap-custom-sample-bank.osu");
|
||||
|
||||
AssertUserLookup(expectedSample);
|
||||
}
|
||||
|
||||
[TestCase("normal-hitnormal2")]
|
||||
public void TestUserSkinLookupIgnoresSampleBank(string unwantedSample)
|
||||
{
|
||||
SetupSkins(string.Empty, unwantedSample);
|
||||
|
||||
CreateTestWithBeatmap("osu-hitobject-beatmap-custom-sample-bank.osu");
|
||||
|
||||
AssertNoLookup(unwantedSample);
|
||||
}
|
||||
}
|
||||
}
|
@ -166,6 +166,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
|
||||
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration => default;
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => null;
|
||||
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => null;
|
||||
|
||||
public event Action SourceChanged;
|
||||
|
||||
|
@ -11,10 +11,12 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
{
|
||||
public class LegacyCursor : OsuCursorSprite
|
||||
{
|
||||
private readonly ISkin skin;
|
||||
private bool spin;
|
||||
|
||||
public LegacyCursor()
|
||||
public LegacyCursor(ISkin skin)
|
||||
{
|
||||
this.skin = skin;
|
||||
Size = new Vector2(50);
|
||||
|
||||
Anchor = Anchor.Centre;
|
||||
@ -22,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(ISkinSource skin)
|
||||
private void load()
|
||||
{
|
||||
bool centre = skin.GetConfig<OsuSkinConfiguration, bool>(OsuSkinConfiguration.CursorCentre)?.Value ?? true;
|
||||
spin = skin.GetConfig<OsuSkinConfiguration, bool>(OsuSkinConfiguration.CursorRotate)?.Value ?? true;
|
||||
|
@ -14,14 +14,20 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
{
|
||||
public class LegacyCursorTrail : CursorTrail
|
||||
{
|
||||
private readonly ISkin skin;
|
||||
private const double disjoint_trail_time_separation = 1000 / 60.0;
|
||||
|
||||
private bool disjointTrail;
|
||||
private double lastTrailTime;
|
||||
private IBindable<float> cursorSize;
|
||||
|
||||
public LegacyCursorTrail(ISkin skin)
|
||||
{
|
||||
this.skin = skin;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(ISkinSource skin, OsuConfigManager config)
|
||||
private void load(OsuConfigManager config)
|
||||
{
|
||||
Texture = skin.GetTexture("cursortrail");
|
||||
disjointTrail = skin.GetTexture("cursormiddle") == null;
|
||||
|
@ -14,18 +14,21 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
{
|
||||
private readonly Drawable animationContent;
|
||||
|
||||
private readonly ISkin skin;
|
||||
|
||||
private Sprite layerNd;
|
||||
private Sprite layerSpec;
|
||||
|
||||
public LegacySliderBall(Drawable animationContent)
|
||||
public LegacySliderBall(Drawable animationContent, ISkin skin)
|
||||
{
|
||||
this.animationContent = animationContent;
|
||||
this.skin = skin;
|
||||
|
||||
AutoSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(ISkinSource skin)
|
||||
private void load()
|
||||
{
|
||||
var ballColour = skin.GetConfig<OsuSkinColour, Color4>(OsuSkinColour.SliderBall)?.Value ?? Color4.White;
|
||||
|
||||
|
@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
|
||||
private void sourceChanged()
|
||||
{
|
||||
hasHitCircle = new Lazy<bool>(() => Source.GetTexture("hitcircle") != null);
|
||||
hasHitCircle = new Lazy<bool>(() => FindProvider(s => s.GetTexture("hitcircle") != null) != null);
|
||||
}
|
||||
|
||||
public override Drawable GetDrawableComponent(ISkinComponent component)
|
||||
@ -49,13 +49,16 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
return followCircle;
|
||||
|
||||
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
|
||||
// Math.Max((150 / Velocity) * GameBase.SIXTY_FRAME_TIME, GameBase.SIXTY_FRAME_TIME);
|
||||
|
||||
if (sliderBallContent != null)
|
||||
return new LegacySliderBall(sliderBallContent);
|
||||
return new LegacySliderBall(sliderBallContent, ballProvider);
|
||||
|
||||
return null;
|
||||
|
||||
@ -84,14 +87,18 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.Cursor:
|
||||
if (Source.GetTexture("cursor") != null)
|
||||
return new LegacyCursor();
|
||||
var cursorProvider = Source.FindProvider(s => s.GetTexture("cursor") != null);
|
||||
|
||||
if (cursorProvider != null)
|
||||
return new LegacyCursor(cursorProvider);
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.CursorTrail:
|
||||
if (Source.GetTexture("cursortrail") != null)
|
||||
return new LegacyCursorTrail();
|
||||
var trailProvider = Source.FindProvider(s => s.GetTexture("cursortrail") != null);
|
||||
|
||||
if (trailProvider != null)
|
||||
return new LegacyCursorTrail(trailProvider);
|
||||
|
||||
return null;
|
||||
|
||||
|
@ -152,32 +152,35 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
|
||||
throw new ArgumentOutOfRangeException(nameof(component), $"Invalid component type: {component}");
|
||||
}
|
||||
|
||||
public override ISample GetSample(ISampleInfo sampleInfo) => Source.GetSample(new LegacyTaikoSampleInfo(sampleInfo));
|
||||
public override ISample GetSample(ISampleInfo sampleInfo)
|
||||
{
|
||||
if (sampleInfo is HitSampleInfo hitSampleInfo)
|
||||
return Source.GetSample(new LegacyTaikoSampleInfo(hitSampleInfo));
|
||||
|
||||
return base.GetSample(sampleInfo);
|
||||
}
|
||||
|
||||
public override IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => Source.GetConfig<TLookup, TValue>(lookup);
|
||||
|
||||
private class LegacyTaikoSampleInfo : ISampleInfo
|
||||
private class LegacyTaikoSampleInfo : HitSampleInfo
|
||||
{
|
||||
private readonly ISampleInfo source;
|
||||
public LegacyTaikoSampleInfo(HitSampleInfo sampleInfo)
|
||||
: base(sampleInfo.Name, sampleInfo.Bank, sampleInfo.Suffix, sampleInfo.Volume)
|
||||
|
||||
public LegacyTaikoSampleInfo(ISampleInfo source)
|
||||
{
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public IEnumerable<string> LookupNames
|
||||
public override IEnumerable<string> LookupNames
|
||||
{
|
||||
get
|
||||
{
|
||||
foreach (var name in source.LookupNames)
|
||||
foreach (var name in base.LookupNames)
|
||||
yield return name.Insert(name.LastIndexOf('/') + 1, "taiko-");
|
||||
|
||||
foreach (var name in source.LookupNames)
|
||||
foreach (var name in base.LookupNames)
|
||||
yield return name;
|
||||
}
|
||||
}
|
||||
|
||||
public int Volume => source.Volume;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -85,8 +85,12 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
}
|
||||
|
||||
[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++)
|
||||
{
|
||||
var texture = getAnimationFrame(skin, state, frameIndex);
|
||||
@ -112,8 +116,12 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
}
|
||||
|
||||
[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)
|
||||
{
|
||||
var texture = getAnimationFrame(skin, TaikoMascotAnimationState.Clear, frameIndex);
|
||||
|
@ -123,6 +123,8 @@ namespace osu.Game.Tests.Gameplay
|
||||
|
||||
public ISample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
|
||||
|
||||
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => null;
|
||||
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
|
||||
{
|
||||
switch (lookup)
|
||||
|
@ -61,6 +61,7 @@ namespace osu.Game.Tests.NonVisual.Skinning
|
||||
public Drawable GetDrawableComponent(ISkinComponent component) => throw new NotSupportedException();
|
||||
public ISample GetSample(ISampleInfo sampleInfo) => throw new NotSupportedException();
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => throw new NotSupportedException();
|
||||
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => null;
|
||||
}
|
||||
|
||||
private class TestAnimationTimeReference : IAnimationTimeReference
|
||||
|
@ -1,6 +1,7 @@
|
||||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@ -222,6 +223,8 @@ namespace osu.Game.Tests.Skins
|
||||
public ISample GetSample(ISampleInfo sampleInfo) => skin.GetSample(sampleInfo);
|
||||
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => skin.GetConfig<TLookup, TValue>(lookup);
|
||||
|
||||
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => skin.FindProvider(lookupFunction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -301,6 +301,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
public ISample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
|
||||
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => throw new NotImplementedException();
|
||||
|
||||
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private class SecondarySource : ISkin
|
||||
@ -312,6 +314,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
public ISample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
|
||||
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => throw new NotImplementedException();
|
||||
|
||||
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
[Cached(typeof(ISkinSource))]
|
||||
@ -325,6 +329,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => throw new NotImplementedException();
|
||||
|
||||
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => throw new NotImplementedException();
|
||||
|
||||
public event Action SourceChanged
|
||||
{
|
||||
add { }
|
||||
|
@ -147,6 +147,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => source?.GetTexture(componentName, wrapModeS, wrapModeT);
|
||||
public ISample GetSample(ISampleInfo sampleInfo) => source?.GetSample(sampleInfo);
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => source?.GetConfig<TLookup, TValue>(lookup);
|
||||
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => source?.FindProvider(lookupFunction);
|
||||
|
||||
public void TriggerSourceChanged()
|
||||
{
|
||||
|
@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace osu.Game.Skinning
|
||||
{
|
||||
@ -11,5 +12,13 @@ namespace osu.Game.Skinning
|
||||
public interface ISkinSource : ISkin
|
||||
{
|
||||
event Action SourceChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Find the first (if any) skin that can fulfill the lookup.
|
||||
/// This should be used for cases where subsequent lookups (for related components) need to occur on the same skin.
|
||||
/// </summary>
|
||||
/// <returns>The skin to be used for subsequent lookups, or <c>null</c> if none is available.</returns>
|
||||
[CanBeNull]
|
||||
ISkin FindProvider(Func<ISkin, bool> lookupFunction);
|
||||
}
|
||||
}
|
||||
|
@ -20,9 +20,6 @@ namespace osu.Game.Skinning
|
||||
{
|
||||
private const double epic_cutoff = 0.5;
|
||||
|
||||
[Resolved]
|
||||
private ISkinSource skin { get; set; }
|
||||
|
||||
private LegacyHealthPiece fill;
|
||||
private LegacyHealthPiece marker;
|
||||
|
||||
@ -31,10 +28,13 @@ namespace osu.Game.Skinning
|
||||
private bool isNewStyle;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
private void load(ISkinSource source)
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
||||
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.
|
||||
isNewStyle = getTexture(skin, "marker") != null;
|
||||
|
||||
// background implementation is the same for both versions.
|
||||
@ -76,7 +76,7 @@ namespace osu.Game.Skinning
|
||||
|
||||
protected override void Flash(JudgementResult result) => marker.Flash(result);
|
||||
|
||||
private static Texture getTexture(ISkinSource skin, string name) => skin.GetTexture($"scorebar-{name}");
|
||||
private static Texture getTexture(ISkin skin, string name) => skin?.GetTexture($"scorebar-{name}");
|
||||
|
||||
private static Color4 getFillColour(double hp)
|
||||
{
|
||||
@ -95,7 +95,7 @@ namespace osu.Game.Skinning
|
||||
private readonly Texture dangerTexture;
|
||||
private readonly Texture superDangerTexture;
|
||||
|
||||
public LegacyOldStyleMarker(ISkinSource skin)
|
||||
public LegacyOldStyleMarker(ISkin skin)
|
||||
{
|
||||
normalTexture = getTexture(skin, "ki");
|
||||
dangerTexture = getTexture(skin, "kidanger");
|
||||
@ -126,9 +126,9 @@ namespace osu.Game.Skinning
|
||||
|
||||
public class LegacyNewStyleMarker : LegacyMarker
|
||||
{
|
||||
private readonly ISkinSource skin;
|
||||
private readonly ISkin skin;
|
||||
|
||||
public LegacyNewStyleMarker(ISkinSource skin)
|
||||
public LegacyNewStyleMarker(ISkin skin)
|
||||
{
|
||||
this.skin = skin;
|
||||
}
|
||||
@ -150,7 +150,7 @@ namespace osu.Game.Skinning
|
||||
|
||||
internal class LegacyOldStyleFill : LegacyHealthPiece
|
||||
{
|
||||
public LegacyOldStyleFill(ISkinSource skin)
|
||||
public LegacyOldStyleFill(ISkin skin)
|
||||
{
|
||||
// required for sizing correctly..
|
||||
var firstFrame = getTexture(skin, "colour-0");
|
||||
@ -173,7 +173,7 @@ namespace osu.Game.Skinning
|
||||
|
||||
internal class LegacyNewStyleFill : LegacyHealthPiece
|
||||
{
|
||||
public LegacyNewStyleFill(ISkinSource skin)
|
||||
public LegacyNewStyleFill(ISkin skin)
|
||||
{
|
||||
InternalChild = new Sprite
|
||||
{
|
||||
|
@ -60,10 +60,17 @@ namespace osu.Game.Skinning
|
||||
{
|
||||
}
|
||||
|
||||
protected LegacySkin(SkinInfo skin, [CanBeNull] IResourceStore<byte[]> storage, [CanBeNull] IStorageResourceProvider resources, string filename)
|
||||
/// <summary>
|
||||
/// Construct a new legacy skin instance.
|
||||
/// </summary>
|
||||
/// <param name="skin">The model for this skin.</param>
|
||||
/// <param name="storage">A storage for looking up files within this skin using user-facing filenames.</param>
|
||||
/// <param name="resources">Access to raw game resources.</param>
|
||||
/// <param name="configurationFilename">The user-facing filename of the configuration file to be parsed. Can accept an .osu or skin.ini file.</param>
|
||||
protected LegacySkin(SkinInfo skin, [CanBeNull] IResourceStore<byte[]> storage, [CanBeNull] IStorageResourceProvider resources, string configurationFilename)
|
||||
: base(skin, resources)
|
||||
{
|
||||
using (var stream = storage?.GetStream(filename))
|
||||
using (var stream = storage?.GetStream(configurationFilename))
|
||||
{
|
||||
if (stream != null)
|
||||
{
|
||||
@ -127,7 +134,7 @@ namespace osu.Game.Skinning
|
||||
|
||||
case LegacyManiaSkinConfigurationLookup maniaLookup:
|
||||
if (!AllowManiaSkin)
|
||||
return null;
|
||||
break;
|
||||
|
||||
var result = lookupForMania<TValue>(maniaLookup);
|
||||
if (result != null)
|
||||
@ -390,6 +397,7 @@ namespace osu.Game.Skinning
|
||||
return null;
|
||||
|
||||
case GameplaySkinComponent<HitResult> resultComponent:
|
||||
// TODO: this should be inside the judgement pieces.
|
||||
Func<Drawable> createDrawable = () => getJudgementAnimation(resultComponent.Component);
|
||||
|
||||
// kind of wasteful that we throw this away, but should do for now.
|
||||
@ -485,7 +493,9 @@ namespace osu.Game.Skinning
|
||||
var sample = Samples?.Get(lookup);
|
||||
|
||||
if (sample != null)
|
||||
{
|
||||
return sample;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
@ -518,8 +528,7 @@ namespace osu.Game.Skinning
|
||||
yield return componentName;
|
||||
|
||||
// Fall back to using the last piece for components coming from lazer (e.g. "Gameplay/osu/approachcircle" -> "approachcircle").
|
||||
string lastPiece = componentName.Split('/').Last();
|
||||
yield return componentName.StartsWith("Gameplay/taiko/", StringComparison.Ordinal) ? "taiko-" + lastPiece : lastPiece;
|
||||
yield return componentName.Split('/').Last();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
|
@ -27,6 +27,18 @@ namespace osu.Game.Skinning
|
||||
{
|
||||
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)
|
||||
{
|
||||
var textures = getTextures().ToArray();
|
||||
@ -35,7 +47,7 @@ namespace osu.Game.Skinning
|
||||
{
|
||||
var animation = new SkinnableTextureAnimation(startAtCurrentTime)
|
||||
{
|
||||
DefaultFrameLength = frameLength ?? getFrameLength(source, applyConfigFrameRate, textures),
|
||||
DefaultFrameLength = frameLength ?? getFrameLength(skin, applyConfigFrameRate, textures),
|
||||
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 ((texture = source.GetTexture(componentName, wrapModeS, wrapModeT)) != null)
|
||||
if ((texture = skin.GetTexture(componentName, wrapModeS, wrapModeT)) != null)
|
||||
return new Sprite { Texture = texture };
|
||||
|
||||
return null;
|
||||
@ -56,12 +68,14 @@ namespace osu.Game.Skinning
|
||||
{
|
||||
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;
|
||||
|
||||
yield return texture;
|
||||
}
|
||||
}
|
||||
|
||||
string getFrameName(int frameIndex) => $"{componentName}{animationSeparator}{frameIndex}";
|
||||
}
|
||||
|
||||
public static bool HasFont(this ISkin source, LegacyFont font)
|
||||
|
@ -1,6 +1,7 @@
|
||||
// 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;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
@ -15,7 +16,7 @@ namespace osu.Game.Skinning
|
||||
/// <summary>
|
||||
/// Transformer used to handle support of legacy features for individual rulesets.
|
||||
/// </summary>
|
||||
public abstract class LegacySkinTransformer : ISkin
|
||||
public abstract class LegacySkinTransformer : ISkinSource
|
||||
{
|
||||
/// <summary>
|
||||
/// Source of the <see cref="ISkin"/> which is being transformed.
|
||||
@ -47,5 +48,13 @@ namespace osu.Game.Skinning
|
||||
}
|
||||
|
||||
public abstract IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup);
|
||||
|
||||
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => Source.FindProvider(lookupFunction);
|
||||
|
||||
public event Action SourceChanged
|
||||
{
|
||||
add { throw new NotSupportedException(); }
|
||||
remove { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -48,6 +48,8 @@ namespace osu.Game.Skinning
|
||||
|
||||
protected override string ImportFromStablePath => "Skins";
|
||||
|
||||
private readonly Skin defaultLegacySkin;
|
||||
|
||||
public SkinManager(Storage storage, DatabaseContextFactory contextFactory, GameHost host, IResourceStore<byte[]> resources, AudioManager audio)
|
||||
: base(storage, contextFactory, new SkinStore(contextFactory, storage), host)
|
||||
{
|
||||
@ -55,6 +57,8 @@ namespace osu.Game.Skinning
|
||||
this.host = host;
|
||||
this.resources = resources;
|
||||
|
||||
defaultLegacySkin = new DefaultLegacySkin(this);
|
||||
|
||||
CurrentSkinInfo.ValueChanged += skin => CurrentSkin.Value = GetSkin(skin.NewValue);
|
||||
CurrentSkin.ValueChanged += skin =>
|
||||
{
|
||||
@ -204,13 +208,41 @@ namespace osu.Game.Skinning
|
||||
|
||||
public event Action SourceChanged;
|
||||
|
||||
public Drawable GetDrawableComponent(ISkinComponent component) => CurrentSkin.Value.GetDrawableComponent(component);
|
||||
public Drawable GetDrawableComponent(ISkinComponent component) => lookupWithFallback(s => s.GetDrawableComponent(component));
|
||||
|
||||
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => CurrentSkin.Value.GetTexture(componentName, wrapModeS, wrapModeT);
|
||||
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => lookupWithFallback(s => s.GetTexture(componentName, wrapModeS, wrapModeT));
|
||||
|
||||
public ISample GetSample(ISampleInfo sampleInfo) => CurrentSkin.Value.GetSample(sampleInfo);
|
||||
public ISample GetSample(ISampleInfo sampleInfo) => lookupWithFallback(s => s.GetSample(sampleInfo));
|
||||
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => CurrentSkin.Value.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)
|
||||
{
|
||||
if (lookupFunction(CurrentSkin.Value))
|
||||
return CurrentSkin.Value;
|
||||
|
||||
if (CurrentSkin.Value is LegacySkin && lookupFunction(defaultLegacySkin))
|
||||
return defaultLegacySkin;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private T lookupWithFallback<T>(Func<ISkin, T> func)
|
||||
where T : class
|
||||
{
|
||||
var selectedSkin = func(CurrentSkin.Value);
|
||||
|
||||
if (selectedSkin != null)
|
||||
return selectedSkin;
|
||||
|
||||
// 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)
|
||||
return func(defaultLegacySkin);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
#region IResourceStorageProvider
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Bindables;
|
||||
@ -20,8 +21,10 @@ namespace osu.Game.Skinning
|
||||
{
|
||||
public event Action SourceChanged;
|
||||
|
||||
[CanBeNull]
|
||||
private readonly ISkin skin;
|
||||
|
||||
[CanBeNull]
|
||||
private ISkinSource fallbackSource;
|
||||
|
||||
protected virtual bool AllowDrawableLookup(ISkinComponent component) => true;
|
||||
@ -41,6 +44,22 @@ namespace osu.Game.Skinning
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
public ISkin FindProvider(Func<ISkin, bool> lookupFunction)
|
||||
{
|
||||
if (skin is ISkinSource source)
|
||||
{
|
||||
if (source.FindProvider(lookupFunction) is ISkin found)
|
||||
return found;
|
||||
}
|
||||
else if (skin != null)
|
||||
{
|
||||
if (lookupFunction(skin))
|
||||
return skin;
|
||||
}
|
||||
|
||||
return fallbackSource?.FindProvider(lookupFunction);
|
||||
}
|
||||
|
||||
public Drawable GetDrawableComponent(ISkinComponent component)
|
||||
{
|
||||
Drawable sourceDrawable;
|
||||
@ -85,7 +104,7 @@ namespace osu.Game.Skinning
|
||||
{
|
||||
if (canUseSkinLookup)
|
||||
{
|
||||
var bindable = skin.GetConfig<TLookup, TValue>(lookup);
|
||||
var bindable = skin?.GetConfig<TLookup, TValue>(lookup);
|
||||
if (bindable != null)
|
||||
return bindable;
|
||||
}
|
||||
|
@ -158,6 +158,8 @@ namespace osu.Game.Tests.Beatmaps
|
||||
add { }
|
||||
remove { }
|
||||
}
|
||||
|
||||
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user