mirror of
https://github.com/ppy/osu.git
synced 2025-01-15 00:02:54 +08:00
Merge branch 'master' into remove-judgement-occurred
This commit is contained in:
commit
c379480e7b
@ -2,7 +2,5 @@ clone_depth: 1
|
||||
version: '{branch}-{build}'
|
||||
image: Previous Visual Studio 2017
|
||||
test: off
|
||||
install:
|
||||
- cmd: git submodule update --init --recursive --depth=5
|
||||
build_script:
|
||||
- cmd: PowerShell -Version 2.0 .\build.ps1
|
||||
|
10
appveyor_deploy.yml
Normal file
10
appveyor_deploy.yml
Normal file
@ -0,0 +1,10 @@
|
||||
clone_depth: 1
|
||||
version: '{build}'
|
||||
image: Previous Visual Studio 2017
|
||||
test: off
|
||||
skip_non_tags: true
|
||||
build_script:
|
||||
- cmd: PowerShell -Version 2.0 .\build.ps1
|
||||
deploy:
|
||||
- provider: Environment
|
||||
name: nuget
|
@ -61,6 +61,6 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.904.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2019.830.1" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2019.905.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
@ -14,6 +14,7 @@ using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osuTK.Graphics;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Audio;
|
||||
|
||||
@ -99,8 +100,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
public Texture GetTexture(string componentName) =>
|
||||
throw new NotImplementedException();
|
||||
|
||||
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration =>
|
||||
throw new NotImplementedException();
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
@ -135,6 +136,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
public SampleChannel GetSample(ISampleInfo sampleInfo) => null;
|
||||
|
||||
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration => default;
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => null;
|
||||
|
||||
public event Action SourceChanged;
|
||||
|
||||
|
@ -12,6 +12,7 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Osu.Configuration;
|
||||
using osu.Game.Rulesets.Osu.Skinning;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osuTK.Graphics;
|
||||
using osu.Game.Skinning;
|
||||
@ -166,12 +167,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
base.SkinChanged(skin, allowFallback);
|
||||
|
||||
Body.BorderSize = skin.GetValue<SkinConfiguration, float?>(s => s.SliderBorderSize) ?? SliderBody.DEFAULT_BORDER_SIZE;
|
||||
sliderPathRadius = skin.GetValue<SkinConfiguration, float?>(s => s.SliderPathRadius) ?? OsuHitObject.OBJECT_RADIUS;
|
||||
Body.BorderSize = skin.GetConfig<OsuSkinConfiguration, float>(OsuSkinConfiguration.SliderBorderSize)?.Value ?? SliderBody.DEFAULT_BORDER_SIZE;
|
||||
sliderPathRadius = skin.GetConfig<OsuSkinConfiguration, float>(OsuSkinConfiguration.SliderPathRadius)?.Value ?? OsuHitObject.OBJECT_RADIUS;
|
||||
updatePathRadius();
|
||||
|
||||
Body.AccentColour = skin.GetValue<SkinConfiguration, Color4?>(s => s.CustomColours.ContainsKey("SliderTrackOverride") ? s.CustomColours["SliderTrackOverride"] : (Color4?)null) ?? AccentColour.Value;
|
||||
Body.BorderColour = skin.GetValue<SkinConfiguration, Color4?>(s => s.CustomColours.ContainsKey("SliderBorder") ? s.CustomColours["SliderBorder"] : (Color4?)null) ?? Color4.White;
|
||||
Body.AccentColour = skin.GetConfig<OsuSkinColour, Color4>(OsuSkinColour.SliderTrackOverride)?.Value ?? AccentColour.Value;
|
||||
Body.BorderColour = skin.GetConfig<OsuSkinColour, Color4>(OsuSkinColour.SliderBorder)?.Value ?? Color4.White;
|
||||
}
|
||||
|
||||
private void updatePathRadius() => Body.PathRadius = slider.Scale * sliderPathRadius;
|
||||
|
@ -11,6 +11,7 @@ using osu.Framework.Input;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Osu.Skinning;
|
||||
using osuTK.Graphics;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
@ -218,7 +219,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
float radius = skin.GetValue<SkinConfiguration, float?>(s => s.SliderPathRadius) ?? OsuHitObject.OBJECT_RADIUS;
|
||||
float radius = skin.GetConfig<OsuSkinConfiguration, float>(OsuSkinConfiguration.SliderPathRadius)?.Value ?? OsuHitObject.OBJECT_RADIUS;
|
||||
|
||||
InternalChild = new CircularContainer
|
||||
{
|
||||
|
@ -167,7 +167,7 @@ namespace osu.Game.Rulesets.Osu
|
||||
|
||||
public override RulesetSettingsSubsection CreateSettings() => new OsuSettingsSubsection(this);
|
||||
|
||||
public override ISkin CreateLegacySkinProvider(ISkinSource source) => new OsuLegacySkin(source);
|
||||
public override ISkin CreateLegacySkinProvider(ISkinSource source) => new OsuLegacySkinTransformer(source);
|
||||
|
||||
public override int? LegacyID => 0;
|
||||
|
||||
|
@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(ISkinSource skin, DrawableHitObject drawableObject)
|
||||
{
|
||||
animationContent.Colour = skin.GetValue<SkinConfiguration, Color4?>(s => s.CustomColours.ContainsKey("SliderBall") ? s.CustomColours["SliderBall"] : (Color4?)null) ?? Color4.White;
|
||||
animationContent.Colour = skin.GetConfig<OsuSkinColour, Color4>(OsuSkinColour.SliderBall)?.Value ?? Color4.White;
|
||||
|
||||
InternalChildren = new[]
|
||||
{
|
||||
|
@ -3,21 +3,19 @@
|
||||
|
||||
using System;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Skinning
|
||||
{
|
||||
public class OsuLegacySkin : ISkin
|
||||
public class OsuLegacySkinTransformer : ISkin
|
||||
{
|
||||
private readonly ISkin source;
|
||||
|
||||
private Lazy<SkinConfiguration> configuration;
|
||||
|
||||
private Lazy<bool> hasHitCircle;
|
||||
|
||||
/// <summary>
|
||||
@ -27,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
||||
/// </summary>
|
||||
private const float legacy_circle_radius = 64 - 5;
|
||||
|
||||
public OsuLegacySkin(ISkinSource source)
|
||||
public OsuLegacySkinTransformer(ISkinSource source)
|
||||
{
|
||||
this.source = source;
|
||||
|
||||
@ -37,21 +35,6 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
||||
|
||||
private void sourceChanged()
|
||||
{
|
||||
// these need to be lazy in order to ensure they aren't called before the dependencies have been loaded into our source.
|
||||
configuration = new Lazy<SkinConfiguration>(() =>
|
||||
{
|
||||
var config = new SkinConfiguration();
|
||||
if (hasHitCircle.Value)
|
||||
config.SliderPathRadius = legacy_circle_radius;
|
||||
|
||||
// defaults should only be applied for non-beatmap skins (which are parsed via this constructor).
|
||||
config.CustomColours["SliderBall"] =
|
||||
source.GetValue<SkinConfiguration, Color4?>(s => s.CustomColours.TryGetValue("SliderBall", out var val) ? val : (Color4?)null)
|
||||
?? new Color4(2, 170, 255, 255);
|
||||
|
||||
return config;
|
||||
});
|
||||
|
||||
hasHitCircle = new Lazy<bool>(() => source.GetTexture("hitcircle") != null);
|
||||
}
|
||||
|
||||
@ -96,8 +79,8 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.HitCircleText:
|
||||
string font = GetValue<SkinConfiguration, string>(config => config.HitCircleFont);
|
||||
var overlap = GetValue<SkinConfiguration, float>(config => config.HitCircleOverlap);
|
||||
var font = GetConfig<OsuSkinConfiguration, string>(OsuSkinConfiguration.HitCircleFont)?.Value ?? "default";
|
||||
var overlap = GetConfig<OsuSkinConfiguration, float>(OsuSkinConfiguration.HitCircleOverlap)?.Value ?? 0;
|
||||
|
||||
return !hasFont(font)
|
||||
? null
|
||||
@ -116,13 +99,27 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
||||
|
||||
public SampleChannel GetSample(ISampleInfo sample) => source.GetSample(sample);
|
||||
|
||||
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
|
||||
{
|
||||
TValue val;
|
||||
if (configuration.Value is TConfiguration conf && (val = query.Invoke(conf)) != null)
|
||||
return val;
|
||||
switch (lookup)
|
||||
{
|
||||
case OsuSkinColour colour:
|
||||
return source.GetConfig<SkinCustomColourLookup, TValue>(new SkinCustomColourLookup(colour));
|
||||
|
||||
return source.GetValue(query);
|
||||
case OsuSkinConfiguration osuLookup:
|
||||
switch (osuLookup)
|
||||
{
|
||||
case OsuSkinConfiguration.SliderPathRadius:
|
||||
if (hasHitCircle.Value)
|
||||
return SkinUtils.As<TValue>(new BindableFloat(legacy_circle_radius));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return source.GetConfig<TLookup, TValue>(lookup);
|
||||
}
|
||||
|
||||
private bool hasFont(string fontName) => source.GetTexture($"{fontName}-0") != null;
|
12
osu.Game.Rulesets.Osu/Skinning/OsuSkinColour.cs
Normal file
12
osu.Game.Rulesets.Osu/Skinning/OsuSkinColour.cs
Normal file
@ -0,0 +1,12 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Skinning
|
||||
{
|
||||
public enum OsuSkinColour
|
||||
{
|
||||
SliderTrackOverride,
|
||||
SliderBorder,
|
||||
SliderBall
|
||||
}
|
||||
}
|
14
osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs
Normal file
14
osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs
Normal file
@ -0,0 +1,14 @@
|
||||
// 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.
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Skinning
|
||||
{
|
||||
public enum OsuSkinConfiguration
|
||||
{
|
||||
HitCircleFont,
|
||||
HitCircleOverlap,
|
||||
SliderBorderSize,
|
||||
SliderPathRadius,
|
||||
CursorExpand,
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@ using osu.Framework.Graphics.Effects;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Osu.Skinning;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
@ -38,7 +39,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||
|
||||
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
||||
{
|
||||
cursorExpand = skin.GetValue<SkinConfiguration, bool>(s => s.CursorExpand ?? true);
|
||||
cursorExpand = skin.GetConfig<OsuSkinConfiguration, bool>(OsuSkinConfiguration.CursorExpand)?.Value ?? true;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
|
@ -1,5 +1,6 @@
|
||||
[General]
|
||||
Name: test skin
|
||||
TestLookup: TestValue
|
||||
|
||||
[Colours]
|
||||
Combo1 : 142,199,255
|
||||
|
@ -41,5 +41,20 @@ namespace osu.Game.Tests.Skins
|
||||
Assert.AreEqual(expectedColors[i], comboColors[i]);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDecodeGeneral()
|
||||
{
|
||||
var decoder = new LegacySkinDecoder();
|
||||
|
||||
using (var resStream = TestResources.OpenResource("skin.ini"))
|
||||
using (var stream = new StreamReader(resStream))
|
||||
{
|
||||
var config = decoder.Decode(stream);
|
||||
|
||||
Assert.AreEqual("test skin", config.SkinInfo.Name);
|
||||
Assert.AreEqual("TestValue", config.ConfigDictionary["TestLookup"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
156
osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs
Normal file
156
osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs
Normal file
@ -0,0 +1,156 @@
|
||||
// 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.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Tests.Visual;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Tests.Skins
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestSceneSkinConfigurationLookup : OsuTestScene
|
||||
{
|
||||
private LegacySkin source1;
|
||||
private LegacySkin source2;
|
||||
private SkinRequester requester;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp() => Schedule(() =>
|
||||
{
|
||||
Add(new SkinProvidingContainer(source1 = new SkinSource())
|
||||
.WithChild(new SkinProvidingContainer(source2 = new SkinSource())
|
||||
.WithChild(requester = new SkinRequester())));
|
||||
});
|
||||
|
||||
[Test]
|
||||
public void TestBasicLookup()
|
||||
{
|
||||
AddStep("Add config values", () =>
|
||||
{
|
||||
source1.Configuration.ConfigDictionary["Lookup"] = "source1";
|
||||
source2.Configuration.ConfigDictionary["Lookup"] = "source2";
|
||||
});
|
||||
|
||||
AddAssert("Check lookup finds source2", () => requester.GetConfig<string, string>("Lookup")?.Value == "source2");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestFloatLookup()
|
||||
{
|
||||
AddStep("Add config values", () => source1.Configuration.ConfigDictionary["FloatTest"] = "1.1");
|
||||
AddAssert("Check float parse lookup", () => requester.GetConfig<string, float>("FloatTest")?.Value == 1.1f);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBoolLookup()
|
||||
{
|
||||
AddStep("Add config values", () => source1.Configuration.ConfigDictionary["BoolTest"] = "1");
|
||||
AddAssert("Check bool parse lookup", () => requester.GetConfig<string, bool>("BoolTest")?.Value == true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestEnumLookup()
|
||||
{
|
||||
AddStep("Add config values", () => source1.Configuration.ConfigDictionary["Test"] = "Test2");
|
||||
AddAssert("Check enum parse lookup", () => requester.GetConfig<LookupType, ValueType>(LookupType.Test)?.Value == ValueType.Test2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLookupFailure()
|
||||
{
|
||||
AddAssert("Check lookup failure", () => requester.GetConfig<string, float>("Lookup") == null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLookupNull()
|
||||
{
|
||||
AddStep("Add config values", () => source1.Configuration.ConfigDictionary["Lookup"] = null);
|
||||
|
||||
AddAssert("Check lookup null", () =>
|
||||
{
|
||||
var bindable = requester.GetConfig<string, string>("Lookup");
|
||||
return bindable != null && bindable.Value == null;
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestColourLookup()
|
||||
{
|
||||
AddStep("Add config colour", () => source1.Configuration.CustomColours["Lookup"] = Color4.Red);
|
||||
AddAssert("Check colour lookup", () => requester.GetConfig<SkinCustomColourLookup, Color4>(new SkinCustomColourLookup("Lookup"))?.Value == Color4.Red);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestGlobalLookup()
|
||||
{
|
||||
AddAssert("Check combo colours", () => requester.GetConfig<GlobalSkinConfiguration, List<Color4>>(GlobalSkinConfiguration.ComboColours)?.Value?.Count > 0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestWrongColourType()
|
||||
{
|
||||
AddStep("Add config colour", () => source1.Configuration.CustomColours["Lookup"] = Color4.Red);
|
||||
|
||||
AddAssert("perform incorrect lookup", () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
requester.GetConfig<SkinCustomColourLookup, int>(new SkinCustomColourLookup("Lookup"));
|
||||
return false;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public enum LookupType
|
||||
{
|
||||
Test
|
||||
}
|
||||
|
||||
public enum ValueType
|
||||
{
|
||||
Test1,
|
||||
Test2,
|
||||
Test3
|
||||
}
|
||||
|
||||
public class SkinSource : LegacySkin
|
||||
{
|
||||
public SkinSource()
|
||||
: base(new SkinInfo(), null, null, string.Empty)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class SkinRequester : Drawable, ISkin
|
||||
{
|
||||
private ISkinSource skin;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(ISkinSource skin)
|
||||
{
|
||||
this.skin = skin;
|
||||
}
|
||||
|
||||
public Drawable GetDrawableComponent(ISkinComponent component) => skin.GetDrawableComponent(component);
|
||||
|
||||
public Texture GetTexture(string componentName) => skin.GetTexture(componentName);
|
||||
|
||||
public SampleChannel GetSample(ISampleInfo sampleInfo) => skin.GetSample(sampleInfo);
|
||||
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => skin.GetConfig<TLookup, TValue>(lookup);
|
||||
}
|
||||
}
|
||||
}
|
@ -17,7 +17,7 @@ using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
[System.ComponentModel.Description("player pause/fail screens")]
|
||||
[Description("player pause/fail screens")]
|
||||
public class TestSceneGameplayMenuOverlay : ManualInputManagerTestScene
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(FailOverlay), typeof(PauseOverlay) };
|
||||
@ -152,7 +152,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that entering menu with cursor initially on button selects it.
|
||||
/// Tests that entering menu with cursor initially on button doesn't selects it immediately.
|
||||
/// This is to allow for stable keyboard navigation.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestInitialButtonHover()
|
||||
@ -164,6 +165,10 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddStep("Hide overlay", () => pauseOverlay.Hide());
|
||||
showOverlay();
|
||||
|
||||
AddAssert("First button not selected", () => !getButton(0).Selected.Value);
|
||||
|
||||
AddStep("Move slightly", () => InputManager.MoveMouseTo(InputManager.CurrentState.Mouse.Position + new Vector2(1)));
|
||||
|
||||
AddAssert("First button selected", () => getButton(0).Selected.Value);
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,9 @@ using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
@ -133,11 +135,54 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddAssert("skinchanged only called once", () => consumer.SkinChangedCount == 1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSwitchOff()
|
||||
{
|
||||
SkinConsumer consumer = null;
|
||||
SwitchableSkinProvidingContainer target = null;
|
||||
|
||||
AddStep("setup layout", () =>
|
||||
{
|
||||
Child = new SkinSourceContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = target = new SwitchableSkinProvidingContainer(new SecondarySource())
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
AddStep("add permissive", () => target.Add(consumer = new SkinConsumer("test", name => new NamedBox("Default Implementation"), source => true)));
|
||||
AddAssert("consumer using override source", () => consumer.Drawable is SecondarySourceBox);
|
||||
AddStep("disable", () => target.Disable());
|
||||
AddAssert("consumer using base source", () => consumer.Drawable is BaseSourceBox);
|
||||
}
|
||||
|
||||
private class SwitchableSkinProvidingContainer : SkinProvidingContainer
|
||||
{
|
||||
private bool allow = true;
|
||||
|
||||
protected override bool AllowDrawableLookup(ISkinComponent component) => allow;
|
||||
|
||||
public void Disable()
|
||||
{
|
||||
allow = false;
|
||||
TriggerSourceChanged();
|
||||
}
|
||||
|
||||
public SwitchableSkinProvidingContainer(ISkin skin)
|
||||
: base(skin)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private class ExposedSkinnableDrawable : SkinnableDrawable
|
||||
{
|
||||
public new Drawable Drawable => base.Drawable;
|
||||
|
||||
public ExposedSkinnableDrawable(string name, Func<ISkinComponent, Drawable> defaultImplementation, Func<ISkinSource, bool> allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit)
|
||||
public ExposedSkinnableDrawable(string name, Func<ISkinComponent, Drawable> defaultImplementation, Func<ISkinSource, bool> allowFallback = null,
|
||||
ConfineMode confineMode = ConfineMode.ScaleDownToFit)
|
||||
: base(new TestSkinComponent(name), defaultImplementation, allowFallback, confineMode)
|
||||
{
|
||||
}
|
||||
@ -256,7 +301,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
public SampleChannel GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
|
||||
|
||||
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration => throw new NotImplementedException();
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private class SecondarySource : ISkin
|
||||
@ -267,10 +312,11 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
public SampleChannel GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
|
||||
|
||||
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration => throw new NotImplementedException();
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private class SkinSourceContainer : Container, ISkin
|
||||
[Cached(typeof(ISkinSource))]
|
||||
private class SkinSourceContainer : Container, ISkinSource
|
||||
{
|
||||
public Drawable GetDrawableComponent(ISkinComponent componentName) => new BaseSourceBox();
|
||||
|
||||
@ -278,7 +324,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
public SampleChannel GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
|
||||
|
||||
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration => throw new NotImplementedException();
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => throw new NotImplementedException();
|
||||
|
||||
public event Action SourceChanged;
|
||||
}
|
||||
|
||||
private class TestSkinComponent : ISkinComponent
|
||||
|
@ -10,14 +10,9 @@ namespace osu.Game.Tests.Visual.Menus
|
||||
{
|
||||
public class TestSceneDisclaimer : ScreenTestScene
|
||||
{
|
||||
[Cached(typeof(IAPIProvider))]
|
||||
private readonly DummyAPIAccess api = new DummyAPIAccess();
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
private void load(IAPIProvider api)
|
||||
{
|
||||
Add(api);
|
||||
|
||||
AddStep("load disclaimer", () => LoadScreen(new Disclaimer()));
|
||||
|
||||
AddStep("toggle support", () =>
|
||||
|
@ -14,6 +14,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public class TestSceneMatchLeaderboard : MultiplayerTestScene
|
||||
{
|
||||
protected override bool RequiresAPIAccess => true;
|
||||
|
||||
public TestSceneMatchLeaderboard()
|
||||
{
|
||||
Room.RoomID.Value = 3;
|
||||
@ -27,11 +29,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
});
|
||||
}
|
||||
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; }
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
private void load(IAPIProvider api)
|
||||
{
|
||||
var req = new GetRoomScoresRequest();
|
||||
req.Success += v => { };
|
||||
|
@ -12,6 +12,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
[TestFixture]
|
||||
public class TestSceneMultiScreen : ScreenTestScene
|
||||
{
|
||||
protected override bool RequiresAPIAccess => true;
|
||||
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(Screens.Multi.Multiplayer),
|
||||
|
@ -25,17 +25,14 @@ namespace osu.Game.Tests.Visual.Online
|
||||
typeof(AccountCreationScreen),
|
||||
};
|
||||
|
||||
[Cached(typeof(IAPIProvider))]
|
||||
private DummyAPIAccess api = new DummyAPIAccess();
|
||||
private readonly Container userPanelArea;
|
||||
|
||||
public TestSceneAccountCreationOverlay()
|
||||
{
|
||||
Container userPanelArea;
|
||||
AccountCreationOverlay accountCreation;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
api,
|
||||
accountCreation = new AccountCreationOverlay(),
|
||||
userPanelArea = new Container
|
||||
{
|
||||
@ -46,11 +43,16 @@ namespace osu.Game.Tests.Visual.Online
|
||||
},
|
||||
};
|
||||
|
||||
AddStep("show", () => accountCreation.Show());
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(IAPIProvider api)
|
||||
{
|
||||
api.Logout();
|
||||
api.LocalUser.BindValueChanged(user => { userPanelArea.Child = new UserPanel(user.NewValue) { Width = 200 }; }, true);
|
||||
|
||||
AddStep("show", () => accountCreation.Show());
|
||||
AddStep("logout", () => api.Logout());
|
||||
AddStep("logout", api.Logout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,6 +42,8 @@ namespace osu.Game.Tests.Visual.Online
|
||||
typeof(BeatmapAvailability),
|
||||
};
|
||||
|
||||
protected override bool RequiresAPIAccess => true;
|
||||
|
||||
private RulesetInfo taikoRuleset;
|
||||
private RulesetInfo maniaRuleset;
|
||||
|
||||
|
@ -27,6 +27,8 @@ namespace osu.Game.Tests.Visual.Online
|
||||
typeof(Comments),
|
||||
};
|
||||
|
||||
protected override bool RequiresAPIAccess => true;
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
@ -13,6 +13,8 @@ namespace osu.Game.Tests.Visual.Online
|
||||
{
|
||||
private DirectOverlay direct;
|
||||
|
||||
protected override bool RequiresAPIAccess => true;
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
@ -17,14 +17,15 @@ namespace osu.Game.Tests.Visual.Online
|
||||
[TestFixture]
|
||||
public class TestSceneHistoricalSection : OsuTestScene
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes =>
|
||||
new[]
|
||||
{
|
||||
typeof(HistoricalSection),
|
||||
typeof(PaginatedMostPlayedBeatmapContainer),
|
||||
typeof(DrawableMostPlayedBeatmap),
|
||||
typeof(DrawableProfileRow)
|
||||
};
|
||||
protected override bool RequiresAPIAccess => true;
|
||||
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(HistoricalSection),
|
||||
typeof(PaginatedMostPlayedBeatmapContainer),
|
||||
typeof(DrawableMostPlayedBeatmap),
|
||||
typeof(DrawableProfileRow)
|
||||
};
|
||||
|
||||
public TestSceneHistoricalSection()
|
||||
{
|
||||
|
@ -13,6 +13,8 @@ namespace osu.Game.Tests.Visual.Online
|
||||
[TestFixture]
|
||||
public class TestSceneSocialOverlay : OsuTestScene
|
||||
{
|
||||
protected override bool RequiresAPIAccess => true;
|
||||
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(UserPanel),
|
||||
|
@ -17,6 +17,8 @@ namespace osu.Game.Tests.Visual.Online
|
||||
{
|
||||
public class TestSceneUserProfileHeader : OsuTestScene
|
||||
{
|
||||
protected override bool RequiresAPIAccess => true;
|
||||
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(ProfileHeader),
|
||||
|
@ -19,6 +19,8 @@ namespace osu.Game.Tests.Visual.Online
|
||||
[TestFixture]
|
||||
public class TestSceneUserProfileOverlay : OsuTestScene
|
||||
{
|
||||
protected override bool RequiresAPIAccess => true;
|
||||
|
||||
private readonly TestUserProfileOverlay profile;
|
||||
|
||||
[Resolved]
|
||||
|
@ -18,6 +18,8 @@ namespace osu.Game.Tests.Visual.Online
|
||||
[TestFixture]
|
||||
public class TestSceneUserRanks : OsuTestScene
|
||||
{
|
||||
protected override bool RequiresAPIAccess => true;
|
||||
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(DrawableProfileScore), typeof(RanksSection) };
|
||||
|
||||
public TestSceneUserRanks()
|
||||
|
@ -20,6 +20,8 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
{
|
||||
public class TestSceneUpdateableBeatmapBackgroundSprite : OsuTestScene
|
||||
{
|
||||
protected override bool RequiresAPIAccess => true;
|
||||
|
||||
private BeatmapSetInfo testBeatmap;
|
||||
private IAPIProvider api;
|
||||
private RulesetStore rulesets;
|
||||
|
@ -84,7 +84,7 @@ namespace osu.Game.Beatmaps.Drawables
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
// the null coalesce here is only present to make unit tests work (ruleset dlls aren't copied correctly for testing at the moment)
|
||||
Icon = ruleset?.CreateInstance().CreateIcon() ?? new SpriteIcon { Icon = FontAwesome.Regular.QuestionCircle }
|
||||
Icon = ruleset?.CreateInstance()?.CreateIcon() ?? new SpriteIcon { Icon = FontAwesome.Regular.QuestionCircle }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -16,11 +16,11 @@ namespace osu.Game.Configuration
|
||||
|
||||
private readonly int? variant;
|
||||
|
||||
private readonly List<DatabasedSetting> databasedSettings;
|
||||
private List<DatabasedSetting> databasedSettings;
|
||||
|
||||
private readonly RulesetInfo ruleset;
|
||||
|
||||
private readonly bool legacySettingsExist;
|
||||
private bool legacySettingsExist;
|
||||
|
||||
protected DatabasedConfigManager(SettingsStore settings, RulesetInfo ruleset = null, int? variant = null)
|
||||
{
|
||||
@ -28,21 +28,31 @@ namespace osu.Game.Configuration
|
||||
this.ruleset = ruleset;
|
||||
this.variant = variant;
|
||||
|
||||
databasedSettings = settings.Query(ruleset?.ID, variant);
|
||||
legacySettingsExist = databasedSettings.Any(s => int.TryParse(s.Key, out var _));
|
||||
Load();
|
||||
|
||||
InitialiseDefaults();
|
||||
}
|
||||
|
||||
protected override void PerformLoad()
|
||||
{
|
||||
databasedSettings = settings.Query(ruleset?.ID, variant);
|
||||
legacySettingsExist = databasedSettings.Any(s => int.TryParse(s.Key, out var _));
|
||||
}
|
||||
|
||||
protected override bool PerformSave()
|
||||
{
|
||||
lock (dirtySettings)
|
||||
{
|
||||
foreach (var setting in dirtySettings)
|
||||
settings.Update(setting);
|
||||
dirtySettings.Clear();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private readonly List<DatabasedSetting> dirtySettings = new List<DatabasedSetting>();
|
||||
|
||||
protected override void AddBindable<TBindable>(T lookup, Bindable<TBindable> bindable)
|
||||
{
|
||||
base.AddBindable(lookup, bindable);
|
||||
@ -80,7 +90,12 @@ namespace osu.Game.Configuration
|
||||
bindable.ValueChanged += b =>
|
||||
{
|
||||
setting.Value = b.NewValue;
|
||||
settings.Update(setting);
|
||||
|
||||
lock (dirtySettings)
|
||||
{
|
||||
if (!dirtySettings.Contains(setting))
|
||||
dirtySettings.Add(setting);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -57,8 +57,9 @@ namespace osu.Game.Graphics.Backgrounds
|
||||
|
||||
AddInternal(bufferedContainer = new BufferedContainer
|
||||
{
|
||||
CacheDrawnFrameBuffer = true,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
CacheDrawnFrameBuffer = true,
|
||||
RedrawOnScale = false,
|
||||
Child = Sprite
|
||||
});
|
||||
}
|
||||
|
@ -55,6 +55,7 @@ namespace osu.Game.Graphics.Sprites
|
||||
Origin = Anchor.Centre,
|
||||
BlurSigma = new Vector2(4),
|
||||
CacheDrawnFrameBuffer = true,
|
||||
RedrawOnScale = false,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Blending = BlendingParameters.Additive,
|
||||
Size = new Vector2(3f),
|
||||
|
@ -207,6 +207,7 @@ namespace osu.Game
|
||||
FileStore.Cleanup();
|
||||
|
||||
AddInternal(API);
|
||||
AddInternal(RulesetConfigCache);
|
||||
|
||||
GlobalActionContainer globalBinding;
|
||||
|
||||
|
@ -346,10 +346,12 @@ namespace osu.Game.Overlays
|
||||
public Background(WorkingBeatmap beatmap = null)
|
||||
{
|
||||
this.beatmap = beatmap;
|
||||
CacheDrawnFrameBuffer = true;
|
||||
|
||||
Depth = float.MaxValue;
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
CacheDrawnFrameBuffer = true;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
sprite = new Sprite
|
||||
|
@ -239,7 +239,11 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
base.SkinChanged(skin, allowFallback);
|
||||
|
||||
if (HitObject is IHasComboInformation combo)
|
||||
AccentColour.Value = skin.GetValue<SkinConfiguration, Color4?>(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White;
|
||||
{
|
||||
var comboColours = skin.GetConfig<GlobalSkinConfiguration, List<Color4>>(GlobalSkinConfiguration.ComboColours)?.Value;
|
||||
|
||||
AccentColour.Value = comboColours?.Count > 0 ? comboColours[combo.ComboIndex % comboColours.Count] : Color4.White;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -7,6 +7,7 @@ using System.Linq;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
@ -83,6 +84,8 @@ namespace osu.Game.Rulesets
|
||||
|
||||
public virtual Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.Solid.QuestionCircle };
|
||||
|
||||
public virtual IResourceStore<byte[]> CreateReourceStore() => new NamespacedResourceStore<byte[]>(new DllResourceStore(GetType().Assembly.Location), @"Resources");
|
||||
|
||||
public abstract string Description { get; }
|
||||
|
||||
public virtual RulesetSettingsSubsection CreateSettings() => null;
|
||||
|
@ -36,5 +36,14 @@ namespace osu.Game.Rulesets
|
||||
|
||||
return configCache.GetOrAdd(ruleset.RulesetInfo.ID.Value, _ => ruleset.CreateConfig(settingsStore));
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
// ensures any potential database operations are finalised before game destruction.
|
||||
foreach (var c in configCache.Values)
|
||||
(c as IDisposable)?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,12 @@ namespace osu.Game.Rulesets
|
||||
[JsonIgnore]
|
||||
public bool Available { get; set; }
|
||||
|
||||
public virtual Ruleset CreateInstance() => (Ruleset)Activator.CreateInstance(Type.GetType(InstantiationInfo), this);
|
||||
public virtual Ruleset CreateInstance()
|
||||
{
|
||||
if (!Available) return null;
|
||||
|
||||
return (Ruleset)Activator.CreateInstance(Type.GetType(InstantiationInfo), this);
|
||||
}
|
||||
|
||||
public bool Equals(RulesetInfo other) => other != null && ID == other.ID && Available == other.Available && Name == other.Name && InstantiationInfo == other.InstantiationInfo;
|
||||
|
||||
|
@ -11,13 +11,20 @@ using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics.Cursor;
|
||||
using osu.Game.Input.Handlers;
|
||||
@ -51,6 +58,10 @@ namespace osu.Game.Rulesets.UI
|
||||
|
||||
private readonly Lazy<Playfield> playfield;
|
||||
|
||||
private TextureStore textureStore;
|
||||
|
||||
private ISampleStore localSampleStore;
|
||||
|
||||
/// <summary>
|
||||
/// The playfield.
|
||||
/// </summary>
|
||||
@ -142,6 +153,18 @@ namespace osu.Game.Rulesets.UI
|
||||
{
|
||||
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
||||
|
||||
var resources = Ruleset.CreateReourceStore();
|
||||
|
||||
if (resources != null)
|
||||
{
|
||||
textureStore = new TextureStore(new TextureLoaderStore(new NamespacedResourceStore<byte[]>(resources, "Textures")));
|
||||
textureStore.AddStore(dependencies.Get<TextureStore>());
|
||||
dependencies.Cache(textureStore);
|
||||
|
||||
localSampleStore = dependencies.Get<AudioManager>().GetSampleStore(new NamespacedResourceStore<byte[]>(resources, "Samples"));
|
||||
dependencies.CacheAs(new FallbackSampleStore(localSampleStore, dependencies.Get<ISampleStore>()));
|
||||
}
|
||||
|
||||
onScreenDisplay = dependencies.Get<OnScreenDisplay>();
|
||||
|
||||
Config = dependencies.Get<RulesetConfigCache>().GetConfigFor(Ruleset);
|
||||
@ -314,6 +337,8 @@ namespace osu.Game.Rulesets.UI
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
localSampleStore?.Dispose();
|
||||
|
||||
if (Config != null)
|
||||
{
|
||||
onScreenDisplay?.StopTracking(this, Config);
|
||||
@ -443,4 +468,50 @@ namespace osu.Game.Rulesets.UI
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A sample store which adds a fallback source.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is a temporary implementation to workaround ISampleStore limitations.
|
||||
/// </remarks>
|
||||
public class FallbackSampleStore : ISampleStore
|
||||
{
|
||||
private readonly ISampleStore primary;
|
||||
private readonly ISampleStore secondary;
|
||||
|
||||
public FallbackSampleStore(ISampleStore primary, ISampleStore secondary)
|
||||
{
|
||||
this.primary = primary;
|
||||
this.secondary = secondary;
|
||||
}
|
||||
|
||||
public SampleChannel Get(string name) => primary.Get(name) ?? secondary.Get(name);
|
||||
|
||||
public Task<SampleChannel> GetAsync(string name) => primary.GetAsync(name) ?? secondary.GetAsync(name);
|
||||
|
||||
public Stream GetStream(string name) => primary.GetStream(name) ?? secondary.GetStream(name);
|
||||
|
||||
public IEnumerable<string> GetAvailableResources() => throw new NotImplementedException();
|
||||
|
||||
public void AddAdjustment(AdjustableProperty type, BindableDouble adjustBindable) => throw new NotImplementedException();
|
||||
|
||||
public void RemoveAdjustment(AdjustableProperty type, BindableDouble adjustBindable) => throw new NotImplementedException();
|
||||
|
||||
public BindableDouble Volume => throw new NotImplementedException();
|
||||
|
||||
public BindableDouble Balance => throw new NotImplementedException();
|
||||
|
||||
public BindableDouble Frequency => throw new NotImplementedException();
|
||||
|
||||
public int PlaybackConcurrency
|
||||
{
|
||||
get => throw new NotImplementedException();
|
||||
set => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -122,7 +122,7 @@ namespace osu.Game.Screens.Menu
|
||||
Color4 defaultColour = Color4.White.Opacity(0.2f);
|
||||
|
||||
if (user.Value?.IsSupporter ?? false)
|
||||
AccentColour = skin.Value.GetValue<SkinConfiguration, Color4?>(s => s.CustomColours.ContainsKey("MenuGlow") ? s.CustomColours["MenuGlow"] : (Color4?)null) ?? defaultColour;
|
||||
AccentColour = skin.Value.GetConfig<GlobalSkinColour, Color4>(GlobalSkinColour.MenuGlow)?.Value ?? defaultColour;
|
||||
else
|
||||
AccentColour = defaultColour;
|
||||
}
|
||||
|
@ -112,7 +112,7 @@ namespace osu.Game.Screens.Menu
|
||||
Color4 baseColour = colours.Blue;
|
||||
|
||||
if (user.Value?.IsSupporter ?? false)
|
||||
baseColour = skin.Value.GetValue<SkinConfiguration, Color4?>(s => s.CustomColours.ContainsKey("MenuGlow") ? s.CustomColours["MenuGlow"] : (Color4?)null) ?? baseColour;
|
||||
baseColour = skin.Value.GetConfig<GlobalSkinColour, Color4>(GlobalSkinColour.MenuGlow)?.Value ?? baseColour;
|
||||
|
||||
// linear colour looks better in this case, so let's use it for now.
|
||||
Color4 gradientDark = baseColour.Opacity(0).ToLinear();
|
||||
|
@ -304,6 +304,9 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
private class Button : DialogButton
|
||||
{
|
||||
// required to ensure keyboard navigation always starts from an extremity (unless the cursor is moved)
|
||||
protected override bool OnHover(HoverEvent e) => true;
|
||||
|
||||
protected override bool OnMouseMove(MouseMoveEvent e)
|
||||
{
|
||||
Selected.Value = true;
|
||||
|
@ -103,6 +103,7 @@ namespace osu.Game.Screens.Play
|
||||
var newColumns = new BufferedContainer<Column>
|
||||
{
|
||||
CacheDrawnFrameBuffer = true,
|
||||
RedrawOnScale = false,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
};
|
||||
|
||||
|
@ -154,6 +154,8 @@ namespace osu.Game.Screens.Select
|
||||
var metadata = beatmapInfo.Metadata ?? beatmap.BeatmapSetInfo?.Metadata ?? new BeatmapMetadata();
|
||||
|
||||
CacheDrawnFrameBuffer = true;
|
||||
RedrawOnScale = false;
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
titleBinding = localisation.GetLocalisedString(new LocalisedString((metadata.TitleUnicode, metadata.Title)));
|
||||
|
@ -146,6 +146,7 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
public PanelBackground(WorkingBeatmap working)
|
||||
{
|
||||
CacheDrawnFrameBuffer = true;
|
||||
RedrawOnScale = false;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.IO.Stores;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Skinning
|
||||
{
|
||||
@ -11,6 +12,7 @@ namespace osu.Game.Skinning
|
||||
public DefaultLegacySkin(IResourceStore<byte[]> storage, AudioManager audioManager)
|
||||
: base(Info, storage, audioManager, string.Empty)
|
||||
{
|
||||
Configuration.CustomColours["SliderBall"] = new Color4(2, 170, 255, 255);
|
||||
}
|
||||
|
||||
public static SkinInfo Info { get; } = new SkinInfo
|
||||
|
@ -1,10 +1,13 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// 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.Collections.Generic;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Audio;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Skinning
|
||||
{
|
||||
@ -21,5 +24,24 @@ namespace osu.Game.Skinning
|
||||
public override Texture GetTexture(string componentName) => null;
|
||||
|
||||
public override SampleChannel GetSample(ISampleInfo sampleInfo) => null;
|
||||
|
||||
public override IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
|
||||
{
|
||||
switch (lookup)
|
||||
{
|
||||
// todo: this code is pulled from LegacySkin and should not exist.
|
||||
// will likely change based on how databased storage of skin configuration goes.
|
||||
case GlobalSkinConfiguration global:
|
||||
switch (global)
|
||||
{
|
||||
case GlobalSkinConfiguration.ComboColours:
|
||||
return SkinUtils.As<TValue>(new Bindable<List<Color4>>(Configuration.ComboColours));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,8 +12,6 @@ namespace osu.Game.Skinning
|
||||
{
|
||||
public DefaultSkinConfiguration()
|
||||
{
|
||||
HitCircleFont = "default";
|
||||
|
||||
ComboColours.AddRange(new[]
|
||||
{
|
||||
new Color4(17, 136, 170, 255),
|
||||
@ -21,8 +19,6 @@ namespace osu.Game.Skinning
|
||||
new Color4(204, 102, 0, 255),
|
||||
new Color4(121, 9, 13, 255)
|
||||
});
|
||||
|
||||
CursorExpand = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ using System.Linq;
|
||||
|
||||
namespace osu.Game.Skinning
|
||||
{
|
||||
public class GameplaySkinComponent<T> : ISkinComponent where T : struct
|
||||
public class GameplaySkinComponent<T> : ISkinComponent
|
||||
{
|
||||
public readonly T Component;
|
||||
|
||||
|
10
osu.Game/Skinning/GlobalSkinColour.cs
Normal file
10
osu.Game/Skinning/GlobalSkinColour.cs
Normal file
@ -0,0 +1,10 @@
|
||||
// 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.
|
||||
|
||||
namespace osu.Game.Skinning
|
||||
{
|
||||
public enum GlobalSkinColour
|
||||
{
|
||||
MenuGlow
|
||||
}
|
||||
}
|
10
osu.Game/Skinning/GlobalSkinConfiguration.cs
Normal file
10
osu.Game/Skinning/GlobalSkinConfiguration.cs
Normal file
@ -0,0 +1,10 @@
|
||||
// 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.
|
||||
|
||||
namespace osu.Game.Skinning
|
||||
{
|
||||
public enum GlobalSkinConfiguration
|
||||
{
|
||||
ComboColours
|
||||
}
|
||||
}
|
@ -1,8 +1,9 @@
|
||||
// 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 JetBrains.Annotations;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Audio;
|
||||
@ -14,12 +15,36 @@ namespace osu.Game.Skinning
|
||||
/// </summary>
|
||||
public interface ISkin
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieve a <see cref="Drawable"/> component implementation.
|
||||
/// </summary>
|
||||
/// <param name="component">The requested component.</param>
|
||||
/// <returns>A drawable representation for the requested component, or null if unavailable.</returns>
|
||||
[CanBeNull]
|
||||
Drawable GetDrawableComponent(ISkinComponent component);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve a <see cref="Texture"/>.
|
||||
/// </summary>
|
||||
/// <param name="componentName">The requested texture.</param>
|
||||
/// <returns>A matching texture, or null if unavailable.</returns>
|
||||
[CanBeNull]
|
||||
Texture GetTexture(string componentName);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve a <see cref="SampleChannel"/>.
|
||||
/// </summary>
|
||||
/// <param name="sampleInfo">The requested sample.</param>
|
||||
/// <returns>A matching sample channel, or null if unavailable.</returns>
|
||||
[CanBeNull]
|
||||
SampleChannel GetSample(ISampleInfo sampleInfo);
|
||||
|
||||
TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration;
|
||||
/// <summary>
|
||||
/// Retrieve a configuration value.
|
||||
/// </summary>
|
||||
/// <param name="lookup">The requested configuration value.</param>
|
||||
/// <returns>A matching value boxed in an <see cref="IBindable{TValue}"/>, or null if unavailable.</returns>
|
||||
[CanBeNull]
|
||||
IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup);
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,13 @@
|
||||
// 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.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.IO.Stores;
|
||||
@ -16,29 +19,32 @@ namespace osu.Game.Skinning
|
||||
{
|
||||
public class LegacySkin : Skin
|
||||
{
|
||||
[CanBeNull]
|
||||
protected TextureStore Textures;
|
||||
|
||||
[CanBeNull]
|
||||
protected IResourceStore<SampleChannel> Samples;
|
||||
|
||||
public LegacySkin(SkinInfo skin, IResourceStore<byte[]> storage, AudioManager audioManager)
|
||||
: this(skin, new LegacySkinResourceStore<SkinFileInfo>(skin, storage), audioManager, "skin.ini")
|
||||
{
|
||||
// defaults should only be applied for non-beatmap skins (which are parsed via this constructor).
|
||||
if (!Configuration.CustomColours.ContainsKey("SliderBall")) Configuration.CustomColours["SliderBall"] = new Color4(2, 170, 255, 255);
|
||||
}
|
||||
|
||||
protected LegacySkin(SkinInfo skin, IResourceStore<byte[]> storage, AudioManager audioManager, string filename)
|
||||
: base(skin)
|
||||
{
|
||||
Stream stream = storage.GetStream(filename);
|
||||
Stream stream = storage?.GetStream(filename);
|
||||
if (stream != null)
|
||||
using (StreamReader reader = new StreamReader(stream))
|
||||
Configuration = new LegacySkinDecoder().Decode(reader);
|
||||
else
|
||||
Configuration = new DefaultSkinConfiguration();
|
||||
|
||||
Samples = audioManager.GetSampleStore(storage);
|
||||
Textures = new TextureStore(new TextureLoaderStore(storage));
|
||||
if (storage != null)
|
||||
{
|
||||
Samples = audioManager?.GetSampleStore(storage);
|
||||
Textures = new TextureStore(new TextureLoaderStore(storage));
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
@ -48,6 +54,52 @@ namespace osu.Game.Skinning
|
||||
Samples?.Dispose();
|
||||
}
|
||||
|
||||
public override IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
|
||||
{
|
||||
switch (lookup)
|
||||
{
|
||||
case GlobalSkinConfiguration global:
|
||||
switch (global)
|
||||
{
|
||||
case GlobalSkinConfiguration.ComboColours:
|
||||
return SkinUtils.As<TValue>(new Bindable<List<Color4>>(Configuration.ComboColours));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case GlobalSkinColour colour:
|
||||
return SkinUtils.As<TValue>(getCustomColour(colour.ToString()));
|
||||
|
||||
case SkinCustomColourLookup customColour:
|
||||
return SkinUtils.As<TValue>(getCustomColour(customColour.Lookup.ToString()));
|
||||
|
||||
default:
|
||||
try
|
||||
{
|
||||
if (Configuration.ConfigDictionary.TryGetValue(lookup.ToString(), out var val))
|
||||
{
|
||||
// special case for handling skins which use 1 or 0 to signify a boolean state.
|
||||
if (typeof(TValue) == typeof(bool))
|
||||
val = val == "1" ? "true" : "false";
|
||||
|
||||
var bindable = new Bindable<TValue>();
|
||||
if (val != null)
|
||||
bindable.Parse(val);
|
||||
return bindable;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private IBindable<Color4> getCustomColour(string lookup) => Configuration.CustomColours.TryGetValue(lookup, out var col) ? new Bindable<Color4>(col) : null;
|
||||
|
||||
public override Drawable GetDrawableComponent(ISkinComponent component)
|
||||
{
|
||||
switch (component)
|
||||
@ -79,12 +131,12 @@ namespace osu.Game.Skinning
|
||||
componentName = getFallbackName(componentName);
|
||||
|
||||
float ratio = 2;
|
||||
var texture = Textures.Get($"{componentName}@2x");
|
||||
var texture = Textures?.Get($"{componentName}@2x");
|
||||
|
||||
if (texture == null)
|
||||
{
|
||||
ratio = 1;
|
||||
texture = Textures.Get(componentName);
|
||||
texture = Textures?.Get(componentName);
|
||||
}
|
||||
|
||||
if (texture != null)
|
||||
@ -97,7 +149,7 @@ namespace osu.Game.Skinning
|
||||
{
|
||||
foreach (var lookup in sampleInfo.LookupNames)
|
||||
{
|
||||
var sample = Samples.Get(getFallbackName(lookup));
|
||||
var sample = Samples?.Get(getFallbackName(lookup));
|
||||
|
||||
if (sample != null)
|
||||
return sample;
|
||||
@ -105,7 +157,7 @@ namespace osu.Game.Skinning
|
||||
|
||||
if (sampleInfo is HitSampleInfo hsi)
|
||||
// Try fallback to non-bank samples.
|
||||
return Samples.Get(hsi.Name);
|
||||
return Samples?.Get(hsi.Name);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
@ -14,47 +14,31 @@ namespace osu.Game.Skinning
|
||||
|
||||
protected override void ParseLine(DefaultSkinConfiguration skin, Section section, string line)
|
||||
{
|
||||
line = StripComments(line);
|
||||
|
||||
var pair = SplitKeyVal(line);
|
||||
|
||||
switch (section)
|
||||
if (section != Section.Colours)
|
||||
{
|
||||
case Section.General:
|
||||
switch (pair.Key)
|
||||
{
|
||||
case @"Name":
|
||||
skin.SkinInfo.Name = pair.Value;
|
||||
break;
|
||||
line = StripComments(line);
|
||||
|
||||
case @"Author":
|
||||
skin.SkinInfo.Creator = pair.Value;
|
||||
break;
|
||||
var pair = SplitKeyVal(line);
|
||||
|
||||
case @"CursorExpand":
|
||||
skin.CursorExpand = pair.Value != "0";
|
||||
break;
|
||||
switch (section)
|
||||
{
|
||||
case Section.General:
|
||||
switch (pair.Key)
|
||||
{
|
||||
case @"Name":
|
||||
skin.SkinInfo.Name = pair.Value;
|
||||
return;
|
||||
|
||||
case @"SliderBorderSize":
|
||||
skin.SliderBorderSize = Parsing.ParseFloat(pair.Value);
|
||||
break;
|
||||
}
|
||||
case @"Author":
|
||||
skin.SkinInfo.Creator = pair.Value;
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
break;
|
||||
}
|
||||
|
||||
case Section.Fonts:
|
||||
switch (pair.Key)
|
||||
{
|
||||
case "HitCirclePrefix":
|
||||
skin.HitCircleFont = pair.Value;
|
||||
break;
|
||||
|
||||
case "HitCircleOverlap":
|
||||
skin.HitCircleOverlap = int.Parse(pair.Value);
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
if (!string.IsNullOrEmpty(pair.Key))
|
||||
skin.ConfigDictionary[pair.Key] = pair.Value;
|
||||
}
|
||||
|
||||
base.ParseLine(skin, section, line);
|
||||
|
@ -1,8 +1,9 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// 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;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Audio;
|
||||
@ -13,7 +14,7 @@ namespace osu.Game.Skinning
|
||||
{
|
||||
public readonly SkinInfo SkinInfo;
|
||||
|
||||
public virtual SkinConfiguration Configuration { get; protected set; }
|
||||
public SkinConfiguration Configuration { get; protected set; }
|
||||
|
||||
public abstract Drawable GetDrawableComponent(ISkinComponent componentName);
|
||||
|
||||
@ -21,8 +22,7 @@ namespace osu.Game.Skinning
|
||||
|
||||
public abstract Texture GetTexture(string componentName);
|
||||
|
||||
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration
|
||||
=> Configuration is TConfiguration conf ? query.Invoke(conf) : default;
|
||||
public abstract IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup);
|
||||
|
||||
protected Skin(SkinInfo skin)
|
||||
{
|
||||
|
16
osu.Game/Skinning/SkinConfigManager.cs
Normal file
16
osu.Game/Skinning/SkinConfigManager.cs
Normal file
@ -0,0 +1,16 @@
|
||||
// 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.Configuration;
|
||||
|
||||
namespace osu.Game.Skinning
|
||||
{
|
||||
public class SkinConfigManager<T> : ConfigManager<T> where T : struct
|
||||
{
|
||||
protected override void PerformLoad()
|
||||
{
|
||||
}
|
||||
|
||||
protected override bool PerformSave() => false;
|
||||
}
|
||||
}
|
@ -18,14 +18,6 @@ namespace osu.Game.Skinning
|
||||
|
||||
public Dictionary<string, Color4> CustomColours { get; set; } = new Dictionary<string, Color4>();
|
||||
|
||||
public string HitCircleFont { get; set; }
|
||||
|
||||
public int HitCircleOverlap { get; set; }
|
||||
|
||||
public float? SliderBorderSize { get; set; }
|
||||
|
||||
public float? SliderPathRadius { get; set; }
|
||||
|
||||
public bool? CursorExpand { get; set; }
|
||||
public readonly Dictionary<string, string> ConfigDictionary = new Dictionary<string, string>();
|
||||
}
|
||||
}
|
||||
|
15
osu.Game/Skinning/SkinCustomColourLookup.cs
Normal file
15
osu.Game/Skinning/SkinCustomColourLookup.cs
Normal file
@ -0,0 +1,15 @@
|
||||
// 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.
|
||||
|
||||
namespace osu.Game.Skinning
|
||||
{
|
||||
public class SkinCustomColourLookup
|
||||
{
|
||||
public readonly object Lookup;
|
||||
|
||||
public SkinCustomColourLookup(object lookup)
|
||||
{
|
||||
Lookup = lookup;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// 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;
|
||||
@ -131,6 +131,6 @@ namespace osu.Game.Skinning
|
||||
|
||||
public SampleChannel GetSample(ISampleInfo sampleInfo) => CurrentSkin.Value.GetSample(sampleInfo);
|
||||
|
||||
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration => CurrentSkin.Value.GetValue(query);
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => CurrentSkin.Value.GetConfig<TLookup, TValue>(lookup);
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
@ -64,13 +65,16 @@ namespace osu.Game.Skinning
|
||||
return fallbackSource?.GetSample(sampleInfo);
|
||||
}
|
||||
|
||||
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
|
||||
{
|
||||
TValue val;
|
||||
if (AllowConfigurationLookup && skin != null && (val = skin.GetValue(query)) != null)
|
||||
return val;
|
||||
if (AllowConfigurationLookup && skin != null)
|
||||
{
|
||||
var bindable = skin.GetConfig<TLookup, TValue>(lookup);
|
||||
if (bindable != null)
|
||||
return bindable;
|
||||
}
|
||||
|
||||
return fallbackSource == null ? default : fallbackSource.GetValue(query);
|
||||
return fallbackSource?.GetConfig<TLookup, TValue>(lookup);
|
||||
}
|
||||
|
||||
protected virtual void TriggerSourceChanged() => SourceChanged?.Invoke();
|
||||
|
21
osu.Game/Skinning/SkinUtils.cs
Normal file
21
osu.Game/Skinning/SkinUtils.cs
Normal file
@ -0,0 +1,21 @@
|
||||
// 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.Bindables;
|
||||
|
||||
namespace osu.Game.Skinning
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains helper methods to assist in implementing <see cref="ISkin"/>s.
|
||||
/// </summary>
|
||||
public static class SkinUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts an <see cref="object"/> to a <see cref="Bindable{TValue}"/>. Used for returning configuration values of specific types.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <typeparam name="TValue">The type of value <paramref name="value"/>, and the type of the resulting bindable.</typeparam>
|
||||
/// <returns>The resulting bindable.</returns>
|
||||
public static Bindable<TValue> As<TValue>(object value) => (Bindable<TValue>)value;
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Game.Audio;
|
||||
@ -20,7 +21,7 @@ namespace osu.Game.Skinning
|
||||
|
||||
private SampleChannel[] channels;
|
||||
|
||||
private AudioManager audio;
|
||||
private ISampleStore samples;
|
||||
|
||||
public SkinnableSound(IEnumerable<ISampleInfo> hitSamples)
|
||||
{
|
||||
@ -33,9 +34,9 @@ namespace osu.Game.Skinning
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(AudioManager audio)
|
||||
private void load(ISampleStore samples)
|
||||
{
|
||||
this.audio = audio;
|
||||
this.samples = samples;
|
||||
}
|
||||
|
||||
private bool looping;
|
||||
@ -81,7 +82,7 @@ namespace osu.Game.Skinning
|
||||
|
||||
if (ch == null && allowFallback)
|
||||
foreach (var lookup in s.LookupNames)
|
||||
if ((ch = audio.Samples.Get($"Gameplay/{lookup}")) != null)
|
||||
if ((ch = samples.Get($"Gameplay/{lookup}")) != null)
|
||||
break;
|
||||
|
||||
if (ch != null)
|
||||
@ -102,8 +103,9 @@ namespace osu.Game.Skinning
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
foreach (var c in channels)
|
||||
c.Dispose();
|
||||
if (channels != null)
|
||||
foreach (var c in channels)
|
||||
c.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -64,5 +64,11 @@ namespace osu.Game.Storyboards.Drawables
|
||||
LifetimeEnd = sampleInfo.StartTime;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
channel?.Stop();
|
||||
base.Dispose(isDisposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ using osu.Framework.Testing;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
@ -47,6 +48,12 @@ namespace osu.Game.Tests.Visual
|
||||
private readonly Lazy<DatabaseContextFactory> contextFactory;
|
||||
protected DatabaseContextFactory ContextFactory => contextFactory.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this test scene requires API access
|
||||
/// Setting this will cache an actual <see cref="APIAccess"/>.
|
||||
/// </summary>
|
||||
protected virtual bool RequiresAPIAccess => false;
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||
{
|
||||
// This is the earliest we can get OsuGameBase, which is used by the dummy working beatmap to find textures
|
||||
@ -57,7 +64,17 @@ namespace osu.Game.Tests.Visual
|
||||
Default = working
|
||||
};
|
||||
|
||||
return Dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
||||
Dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
||||
|
||||
if (!RequiresAPIAccess)
|
||||
{
|
||||
var dummyAPI = new DummyAPIAccess();
|
||||
|
||||
Dependencies.CacheAs<IAPIProvider>(dummyAPI);
|
||||
Add(dummyAPI);
|
||||
}
|
||||
|
||||
return Dependencies;
|
||||
}
|
||||
|
||||
protected OsuTestScene()
|
||||
|
@ -50,7 +50,11 @@ namespace osu.Game.Tests.Visual
|
||||
Beatmap.Value = CreateWorkingBeatmap(beatmap);
|
||||
|
||||
if (!AllowFail)
|
||||
Mods.Value = new[] { ruleset.GetAllMods().First(m => m is ModNoFail) };
|
||||
{
|
||||
var noFailMod = ruleset.GetAllMods().FirstOrDefault(m => m is ModNoFail);
|
||||
if (noFailMod != null)
|
||||
Mods.Value = new[] { noFailMod };
|
||||
}
|
||||
|
||||
if (Autoplay)
|
||||
{
|
||||
|
@ -6,16 +6,27 @@
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Nuget">
|
||||
<Title>osu!</Title>
|
||||
<PackageId>ppy.osu.Game</PackageId>
|
||||
<Authors>ppy Pty Ltd</Authors>
|
||||
<PackageLicenseUrl>https://github.com/ppy/osu/blob/master/LICENCE.md</PackageLicenseUrl>
|
||||
<PackageProjectUrl>https://github.com/ppy/osu</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/ppy/osu</RepositoryUrl>
|
||||
<PackageReleaseNotes>Automated release.</PackageReleaseNotes>
|
||||
<copyright>Copyright (c) 2019 ppy Pty Ltd</copyright>
|
||||
<PackageTags>osu game</PackageTags>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Service">
|
||||
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Humanizer" Version="2.6.2" />
|
||||
<PackageReference Include="Humanizer" Version="2.7.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.904.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2019.830.1" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2019.905.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.24.0" />
|
||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
||||
|
@ -113,13 +113,13 @@
|
||||
<Reference Include="System.Net.Http" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Humanizer" Version="2.5.16" />
|
||||
<PackageReference Include="Humanizer" Version="2.7.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.904.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2019.830.1" />
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2019.830.1" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2019.905.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2019.905.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.24.0" />
|
||||
<PackageReference Include="NUnit" Version="3.11.0" />
|
||||
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
||||
|
Loading…
Reference in New Issue
Block a user