mirror of
https://github.com/osukey/osukey.git
synced 2025-08-05 15:44:04 +09:00
Merge branch 'master' into wiki-article
This commit is contained in:
@ -52,6 +52,6 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.525.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.525.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.607.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.608.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -20,7 +20,8 @@ namespace osu.Android
|
|||||||
[Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.FullUser, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = false, LaunchMode = LaunchMode.SingleInstance, Exported = true)]
|
[Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.FullUser, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = false, LaunchMode = LaunchMode.SingleInstance, Exported = true)]
|
||||||
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osz", DataHost = "*", DataMimeType = "*/*")]
|
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osz", DataHost = "*", DataMimeType = "*/*")]
|
||||||
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osk", DataHost = "*", DataMimeType = "*/*")]
|
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osk", DataHost = "*", DataMimeType = "*/*")]
|
||||||
[IntentFilter(new[] { Intent.ActionSend, Intent.ActionSendMultiple }, Categories = new[] { Intent.CategoryDefault }, DataMimeTypes = new[] { "application/zip", "application/octet-stream", "application/download", "application/x-zip", "application/x-zip-compressed" })]
|
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataMimeType = "application/x-osu-archive")]
|
||||||
|
[IntentFilter(new[] { Intent.ActionSend, Intent.ActionSendMultiple }, Categories = new[] { Intent.CategoryDefault }, DataMimeTypes = new[] { "application/zip", "application/octet-stream", "application/download", "application/x-zip", "application/x-zip-compressed", "application/x-osu-archive" })]
|
||||||
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryBrowsable, Intent.CategoryDefault }, DataSchemes = new[] { "osu", "osump" })]
|
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryBrowsable, Intent.CategoryDefault }, DataSchemes = new[] { "osu", "osump" })]
|
||||||
public class OsuGameActivity : AndroidGameActivity
|
public class OsuGameActivity : AndroidGameActivity
|
||||||
{
|
{
|
||||||
|
@ -4,10 +4,8 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Configuration;
|
|
||||||
using osu.Game.Rulesets.Catch.Mods;
|
using osu.Game.Rulesets.Catch.Mods;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
||||||
@ -21,12 +19,6 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
{
|
{
|
||||||
public class TestSceneCatchModHidden : ModTestScene
|
public class TestSceneCatchModHidden : ModTestScene
|
||||||
{
|
{
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load()
|
|
||||||
{
|
|
||||||
LocalConfig.SetValue(OsuSetting.IncreaseFirstObjectVisibility, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestJuiceStream()
|
public void TestJuiceStream()
|
||||||
{
|
{
|
||||||
|
@ -29,8 +29,7 @@ namespace osu.Game.Rulesets.Catch.Mods
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state)
|
protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state)
|
||||||
{
|
=> ApplyNormalVisibilityState(hitObject, state);
|
||||||
}
|
|
||||||
|
|
||||||
protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state)
|
protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state)
|
||||||
{
|
{
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
// 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.Game.Rulesets.Scoring;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Scoring
|
|
||||||
{
|
|
||||||
public class CatchHitWindows : HitWindows
|
|
||||||
{
|
|
||||||
public override bool IsHitResultAllowed(HitResult result)
|
|
||||||
{
|
|
||||||
switch (result)
|
|
||||||
{
|
|
||||||
case HitResult.Great:
|
|
||||||
case HitResult.Miss:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -25,9 +25,9 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
protected override void SkinChanged(ISkinSource skin)
|
||||||
{
|
{
|
||||||
base.SkinChanged(skin, allowFallback);
|
base.SkinChanged(skin);
|
||||||
ComboCounter?.UpdateCombo(currentCombo);
|
ComboCounter?.UpdateCombo(currentCombo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -396,9 +396,9 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
private void updateTrailVisibility() => trails.DisplayTrail = Dashing || HyperDashing;
|
private void updateTrailVisibility() => trails.DisplayTrail = Dashing || HyperDashing;
|
||||||
|
|
||||||
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
protected override void SkinChanged(ISkinSource skin)
|
||||||
{
|
{
|
||||||
base.SkinChanged(skin, allowFallback);
|
base.SkinChanged(skin);
|
||||||
|
|
||||||
hyperDashColour =
|
hyperDashColour =
|
||||||
skin.GetConfig<CatchSkinColour, Color4>(CatchSkinColour.HyperDash)?.Value ??
|
skin.GetConfig<CatchSkinColour, Color4>(CatchSkinColour.HyperDash)?.Value ??
|
||||||
|
@ -69,10 +69,10 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
|||||||
|
|
||||||
private void sourceChanged()
|
private void sourceChanged()
|
||||||
{
|
{
|
||||||
isLegacySkin = new Lazy<bool>(() => Source.GetConfig<LegacySkinConfiguration.LegacySetting, decimal>(LegacySkinConfiguration.LegacySetting.Version) != null);
|
isLegacySkin = new Lazy<bool>(() => FindProvider(s => s.GetConfig<LegacySkinConfiguration.LegacySetting, decimal>(LegacySkinConfiguration.LegacySetting.Version) != null) != null);
|
||||||
hasKeyTexture = new Lazy<bool>(() => Source.GetAnimation(
|
hasKeyTexture = new Lazy<bool>(() => FindProvider(s => s.GetAnimation(
|
||||||
this.GetManiaSkinConfig<string>(LegacyManiaSkinConfigurationLookups.KeyImage, 0)?.Value
|
s.GetManiaSkinConfig<string>(LegacyManiaSkinConfigurationLookups.KeyImage, 0)?.Value
|
||||||
?? "mania-key1", true, true) != null);
|
?? "mania-key1", true, true) != null) != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Drawable GetDrawableComponent(ISkinComponent component)
|
public override Drawable GetDrawableComponent(ISkinComponent component)
|
||||||
|
@ -33,9 +33,9 @@ namespace osu.Game.Rulesets.Mania.UI.Components
|
|||||||
Direction.BindValueChanged(onDirectionChanged, true);
|
Direction.BindValueChanged(onDirectionChanged, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
protected override void SkinChanged(ISkinSource skin)
|
||||||
{
|
{
|
||||||
base.SkinChanged(skin, allowFallback);
|
base.SkinChanged(skin);
|
||||||
UpdateHitPosition();
|
UpdateHitPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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]
|
[Test]
|
||||||
public void TestLegacySmoothCursorTrail()
|
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]
|
[Test]
|
||||||
public void TestLegacyDisjointCursorTrail()
|
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 IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => null;
|
||||||
|
|
||||||
|
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => null;
|
||||||
|
|
||||||
public event Action SourceChanged
|
public event Action SourceChanged
|
||||||
{
|
{
|
||||||
add { }
|
add { }
|
||||||
|
@ -113,6 +113,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
public Drawable GetDrawableComponent(ISkinComponent component) => null;
|
public Drawable GetDrawableComponent(ISkinComponent component) => null;
|
||||||
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => null;
|
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => null;
|
||||||
public ISample GetSample(ISampleInfo sampleInfo) => null;
|
public ISample GetSample(ISampleInfo sampleInfo) => null;
|
||||||
|
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => null;
|
||||||
|
|
||||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
|
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 TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration => default;
|
||||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => null;
|
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => null;
|
||||||
|
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => null;
|
||||||
|
|
||||||
public event Action SourceChanged;
|
public event Action SourceChanged;
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
@ -30,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
private static readonly float border_distance_x = OsuPlayfield.BASE_SIZE.X * playfield_edge_ratio;
|
private static readonly float border_distance_x = OsuPlayfield.BASE_SIZE.X * playfield_edge_ratio;
|
||||||
private static readonly float border_distance_y = OsuPlayfield.BASE_SIZE.Y * playfield_edge_ratio;
|
private static readonly float border_distance_y = OsuPlayfield.BASE_SIZE.Y * playfield_edge_ratio;
|
||||||
|
|
||||||
private static readonly Vector2 playfield_middle = Vector2.Divide(OsuPlayfield.BASE_SIZE, 2);
|
private static readonly Vector2 playfield_middle = OsuPlayfield.BASE_SIZE / 2;
|
||||||
|
|
||||||
private static readonly float playfield_diagonal = OsuPlayfield.BASE_SIZE.LengthFast;
|
private static readonly float playfield_diagonal = OsuPlayfield.BASE_SIZE.LengthFast;
|
||||||
|
|
||||||
@ -74,22 +75,8 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
// update end position as it may have changed as a result of the position update.
|
// update end position as it may have changed as a result of the position update.
|
||||||
current.EndPositionRandomised = current.PositionRandomised;
|
current.EndPositionRandomised = current.PositionRandomised;
|
||||||
|
|
||||||
switch (hitObject)
|
if (hitObject is Slider slider)
|
||||||
{
|
moveSliderIntoPlayfield(slider, current);
|
||||||
case Slider slider:
|
|
||||||
// Shift nested objects the same distance as the slider got shifted in the randomisation process
|
|
||||||
// so that moveSliderIntoPlayfield() can determine their relative distances to slider.Position and thus minMargin
|
|
||||||
shiftNestedObjects(slider, Vector2.Subtract(slider.Position, current.PositionOriginal));
|
|
||||||
|
|
||||||
var oldPos = new Vector2(slider.Position.X, slider.Position.Y);
|
|
||||||
|
|
||||||
moveSliderIntoPlayfield(slider, current);
|
|
||||||
|
|
||||||
// Shift them again to move them to their final position after the slider got moved into the playfield
|
|
||||||
shiftNestedObjects(slider, Vector2.Subtract(slider.Position, oldPos));
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
previous = current;
|
previous = current;
|
||||||
}
|
}
|
||||||
@ -131,7 +118,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
|
|
||||||
current.AngleRad = (float)Math.Atan2(posRelativeToPrev.Y, posRelativeToPrev.X);
|
current.AngleRad = (float)Math.Atan2(posRelativeToPrev.Y, posRelativeToPrev.X);
|
||||||
|
|
||||||
var position = Vector2.Add(previous.EndPositionRandomised, posRelativeToPrev);
|
var position = previous.EndPositionRandomised + posRelativeToPrev;
|
||||||
|
|
||||||
// Move hit objects back into the playfield if they are outside of it,
|
// Move hit objects back into the playfield if they are outside of it,
|
||||||
// which would sometimes happen during big jumps otherwise.
|
// which would sometimes happen during big jumps otherwise.
|
||||||
@ -146,34 +133,41 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void moveSliderIntoPlayfield(Slider slider, RandomObjectInfo currentObjectInfo)
|
private void moveSliderIntoPlayfield(Slider slider, RandomObjectInfo currentObjectInfo)
|
||||||
{
|
{
|
||||||
// Min. distances from the slider's position to the playfield border
|
var minMargin = getMinSliderMargin(slider);
|
||||||
var minMargin = new MarginPadding();
|
|
||||||
|
|
||||||
foreach (var hitObject in slider.NestedHitObjects.Where(o => o is SliderTick || o is SliderEndCircle))
|
slider.Position = new Vector2(
|
||||||
{
|
Math.Clamp(slider.Position.X, minMargin.Left, OsuPlayfield.BASE_SIZE.X - minMargin.Right),
|
||||||
if (!(hitObject is OsuHitObject osuHitObject))
|
Math.Clamp(slider.Position.Y, minMargin.Top, OsuPlayfield.BASE_SIZE.Y - minMargin.Bottom)
|
||||||
continue;
|
);
|
||||||
|
|
||||||
var relativePos = Vector2.Subtract(osuHitObject.Position, slider.Position);
|
|
||||||
|
|
||||||
minMargin.Left = Math.Max(minMargin.Left, -relativePos.X);
|
|
||||||
minMargin.Right = Math.Max(minMargin.Right, relativePos.X);
|
|
||||||
minMargin.Top = Math.Max(minMargin.Top, -relativePos.Y);
|
|
||||||
minMargin.Bottom = Math.Max(minMargin.Bottom, relativePos.Y);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (slider.Position.X < minMargin.Left)
|
|
||||||
slider.Position = new Vector2(minMargin.Left, slider.Position.Y);
|
|
||||||
else if (slider.Position.X + minMargin.Right > OsuPlayfield.BASE_SIZE.X)
|
|
||||||
slider.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - minMargin.Right, slider.Position.Y);
|
|
||||||
|
|
||||||
if (slider.Position.Y < minMargin.Top)
|
|
||||||
slider.Position = new Vector2(slider.Position.X, minMargin.Top);
|
|
||||||
else if (slider.Position.Y + minMargin.Bottom > OsuPlayfield.BASE_SIZE.Y)
|
|
||||||
slider.Position = new Vector2(slider.Position.X, OsuPlayfield.BASE_SIZE.Y - minMargin.Bottom);
|
|
||||||
|
|
||||||
currentObjectInfo.PositionRandomised = slider.Position;
|
currentObjectInfo.PositionRandomised = slider.Position;
|
||||||
currentObjectInfo.EndPositionRandomised = slider.EndPosition;
|
currentObjectInfo.EndPositionRandomised = slider.EndPosition;
|
||||||
|
|
||||||
|
shiftNestedObjects(slider, currentObjectInfo.PositionRandomised - currentObjectInfo.PositionOriginal);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculates the min. distances from the <see cref="Slider"/>'s position to the playfield border for the slider to be fully inside of the playfield.
|
||||||
|
/// </summary>
|
||||||
|
private MarginPadding getMinSliderMargin(Slider slider)
|
||||||
|
{
|
||||||
|
var pathPositions = new List<Vector2>();
|
||||||
|
slider.Path.GetPathToProgress(pathPositions, 0, 1);
|
||||||
|
|
||||||
|
var minMargin = new MarginPadding();
|
||||||
|
|
||||||
|
foreach (var pos in pathPositions)
|
||||||
|
{
|
||||||
|
minMargin.Left = Math.Max(minMargin.Left, -pos.X);
|
||||||
|
minMargin.Right = Math.Max(minMargin.Right, pos.X);
|
||||||
|
minMargin.Top = Math.Max(minMargin.Top, -pos.Y);
|
||||||
|
minMargin.Bottom = Math.Max(minMargin.Bottom, pos.Y);
|
||||||
|
}
|
||||||
|
|
||||||
|
minMargin.Left = Math.Min(minMargin.Left, OsuPlayfield.BASE_SIZE.X - minMargin.Right);
|
||||||
|
minMargin.Top = Math.Min(minMargin.Top, OsuPlayfield.BASE_SIZE.Y - minMargin.Bottom);
|
||||||
|
|
||||||
|
return minMargin;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -188,7 +182,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
if (!(hitObject is OsuHitObject osuHitObject))
|
if (!(hitObject is OsuHitObject osuHitObject))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
osuHitObject.Position = Vector2.Add(osuHitObject.Position, shift);
|
osuHitObject.Position += shift;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,7 +152,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
while (Math.Abs(aimRotation - Arrow.Rotation) > 180)
|
while (Math.Abs(aimRotation - Arrow.Rotation) > 180)
|
||||||
aimRotation += aimRotation < Arrow.Rotation ? 360 : -360;
|
aimRotation += aimRotation < Arrow.Rotation ? 360 : -360;
|
||||||
|
|
||||||
if (!hasRotation)
|
// The clock may be paused in a scenario like the editor.
|
||||||
|
if (!hasRotation || !Clock.IsRunning)
|
||||||
{
|
{
|
||||||
Arrow.Rotation = aimRotation;
|
Arrow.Rotation = aimRotation;
|
||||||
hasRotation = true;
|
hasRotation = true;
|
||||||
|
@ -18,9 +18,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
protected override void SkinChanged(ISkinSource skin)
|
||||||
{
|
{
|
||||||
base.SkinChanged(skin, allowFallback);
|
base.SkinChanged(skin);
|
||||||
updateColour();
|
updateColour();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,5 +128,13 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
|||||||
spmContainer.FadeIn(drawableSpinner.HitObject.TimeFadeIn);
|
spmContainer.FadeIn(drawableSpinner.HitObject.TimeFadeIn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
base.Dispose(isDisposing);
|
||||||
|
|
||||||
|
if (drawableSpinner != null)
|
||||||
|
drawableSpinner.ApplyCustomUpdateState -= updateStateTransforms;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
|||||||
|
|
||||||
private readonly IBindable<Color4> accentColour = new Bindable<Color4>();
|
private readonly IBindable<Color4> accentColour = new Bindable<Color4>();
|
||||||
private readonly IBindable<int> indexInCurrentCombo = new Bindable<int>();
|
private readonly IBindable<int> indexInCurrentCombo = new Bindable<int>();
|
||||||
private readonly IBindable<ArmedState> armedState = new Bindable<ArmedState>();
|
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private DrawableHitObject drawableObject { get; set; }
|
private DrawableHitObject drawableObject { get; set; }
|
||||||
@ -54,7 +53,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
|||||||
|
|
||||||
accentColour.BindTo(drawableObject.AccentColour);
|
accentColour.BindTo(drawableObject.AccentColour);
|
||||||
indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable);
|
indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable);
|
||||||
armedState.BindTo(drawableObject.State);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
@ -70,19 +68,18 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
|||||||
|
|
||||||
indexInCurrentCombo.BindValueChanged(index => number.Text = (index.NewValue + 1).ToString(), true);
|
indexInCurrentCombo.BindValueChanged(index => number.Text = (index.NewValue + 1).ToString(), true);
|
||||||
|
|
||||||
armedState.BindValueChanged(animate, true);
|
drawableObject.ApplyCustomUpdateState += updateStateTransforms;
|
||||||
|
updateStateTransforms(drawableObject, drawableObject.State.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void animate(ValueChangedEvent<ArmedState> state)
|
private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state)
|
||||||
{
|
{
|
||||||
ClearTransforms(true);
|
|
||||||
|
|
||||||
using (BeginAbsoluteSequence(drawableObject.StateUpdateTime))
|
using (BeginAbsoluteSequence(drawableObject.StateUpdateTime))
|
||||||
glow.FadeOut(400);
|
glow.FadeOut(400);
|
||||||
|
|
||||||
using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime))
|
using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime))
|
||||||
{
|
{
|
||||||
switch (state.NewValue)
|
switch (state)
|
||||||
{
|
{
|
||||||
case ArmedState.Hit:
|
case ArmedState.Hit:
|
||||||
const double flash_in = 40;
|
const double flash_in = 40;
|
||||||
@ -109,5 +106,13 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
base.Dispose(isDisposing);
|
||||||
|
|
||||||
|
if (drawableObject != null)
|
||||||
|
drawableObject.ApplyCustomUpdateState -= updateStateTransforms;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,10 +11,12 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
{
|
{
|
||||||
public class LegacyCursor : OsuCursorSprite
|
public class LegacyCursor : OsuCursorSprite
|
||||||
{
|
{
|
||||||
|
private readonly ISkin skin;
|
||||||
private bool spin;
|
private bool spin;
|
||||||
|
|
||||||
public LegacyCursor()
|
public LegacyCursor(ISkin skin)
|
||||||
{
|
{
|
||||||
|
this.skin = skin;
|
||||||
Size = new Vector2(50);
|
Size = new Vector2(50);
|
||||||
|
|
||||||
Anchor = Anchor.Centre;
|
Anchor = Anchor.Centre;
|
||||||
@ -22,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(ISkinSource skin)
|
private void load()
|
||||||
{
|
{
|
||||||
bool centre = skin.GetConfig<OsuSkinConfiguration, bool>(OsuSkinConfiguration.CursorCentre)?.Value ?? true;
|
bool centre = skin.GetConfig<OsuSkinConfiguration, bool>(OsuSkinConfiguration.CursorCentre)?.Value ?? true;
|
||||||
spin = skin.GetConfig<OsuSkinConfiguration, bool>(OsuSkinConfiguration.CursorRotate)?.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
|
public class LegacyCursorTrail : CursorTrail
|
||||||
{
|
{
|
||||||
|
private readonly ISkin skin;
|
||||||
private const double disjoint_trail_time_separation = 1000 / 60.0;
|
private const double disjoint_trail_time_separation = 1000 / 60.0;
|
||||||
|
|
||||||
private bool disjointTrail;
|
private bool disjointTrail;
|
||||||
private double lastTrailTime;
|
private double lastTrailTime;
|
||||||
private IBindable<float> cursorSize;
|
private IBindable<float> cursorSize;
|
||||||
|
|
||||||
|
public LegacyCursorTrail(ISkin skin)
|
||||||
|
{
|
||||||
|
this.skin = skin;
|
||||||
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(ISkinSource skin, OsuConfigManager config)
|
private void load(OsuConfigManager config)
|
||||||
{
|
{
|
||||||
Texture = skin.GetTexture("cursortrail");
|
Texture = skin.GetTexture("cursortrail");
|
||||||
disjointTrail = skin.GetTexture("cursormiddle") == null;
|
disjointTrail = skin.GetTexture("cursormiddle") == null;
|
||||||
|
@ -20,6 +20,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
{
|
{
|
||||||
public class LegacyMainCirclePiece : CompositeDrawable
|
public class LegacyMainCirclePiece : CompositeDrawable
|
||||||
{
|
{
|
||||||
|
public override bool RemoveCompletedTransforms => false;
|
||||||
|
|
||||||
private readonly string priorityLookup;
|
private readonly string priorityLookup;
|
||||||
private readonly bool hasNumber;
|
private readonly bool hasNumber;
|
||||||
|
|
||||||
@ -39,7 +41,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
|
|
||||||
private readonly Bindable<Color4> accentColour = new Bindable<Color4>();
|
private readonly Bindable<Color4> accentColour = new Bindable<Color4>();
|
||||||
private readonly IBindable<int> indexInCurrentCombo = new Bindable<int>();
|
private readonly IBindable<int> indexInCurrentCombo = new Bindable<int>();
|
||||||
private readonly IBindable<ArmedState> armedState = new Bindable<ArmedState>();
|
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private DrawableHitObject drawableObject { get; set; }
|
private DrawableHitObject drawableObject { get; set; }
|
||||||
@ -114,7 +115,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
|
|
||||||
accentColour.BindTo(drawableObject.AccentColour);
|
accentColour.BindTo(drawableObject.AccentColour);
|
||||||
indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable);
|
indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable);
|
||||||
armedState.BindTo(drawableObject.State);
|
|
||||||
|
|
||||||
Texture getTextureWithFallback(string name)
|
Texture getTextureWithFallback(string name)
|
||||||
{
|
{
|
||||||
@ -140,18 +140,17 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
if (hasNumber)
|
if (hasNumber)
|
||||||
indexInCurrentCombo.BindValueChanged(index => hitCircleText.Text = (index.NewValue + 1).ToString(), true);
|
indexInCurrentCombo.BindValueChanged(index => hitCircleText.Text = (index.NewValue + 1).ToString(), true);
|
||||||
|
|
||||||
armedState.BindValueChanged(animate, true);
|
drawableObject.ApplyCustomUpdateState += updateStateTransforms;
|
||||||
|
updateStateTransforms(drawableObject, drawableObject.State.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void animate(ValueChangedEvent<ArmedState> state)
|
private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state)
|
||||||
{
|
{
|
||||||
const double legacy_fade_duration = 240;
|
const double legacy_fade_duration = 240;
|
||||||
|
|
||||||
ClearTransforms(true);
|
|
||||||
|
|
||||||
using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime))
|
using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime))
|
||||||
{
|
{
|
||||||
switch (state.NewValue)
|
switch (state)
|
||||||
{
|
{
|
||||||
case ArmedState.Hit:
|
case ArmedState.Hit:
|
||||||
circleSprites.FadeOut(legacy_fade_duration, Easing.Out);
|
circleSprites.FadeOut(legacy_fade_duration, Easing.Out);
|
||||||
@ -176,5 +175,13 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
base.Dispose(isDisposing);
|
||||||
|
|
||||||
|
if (drawableObject != null)
|
||||||
|
drawableObject.ApplyCustomUpdateState -= updateStateTransforms;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,18 +14,21 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
{
|
{
|
||||||
private readonly Drawable animationContent;
|
private readonly Drawable animationContent;
|
||||||
|
|
||||||
|
private readonly ISkin skin;
|
||||||
|
|
||||||
private Sprite layerNd;
|
private Sprite layerNd;
|
||||||
private Sprite layerSpec;
|
private Sprite layerSpec;
|
||||||
|
|
||||||
public LegacySliderBall(Drawable animationContent)
|
public LegacySliderBall(Drawable animationContent, ISkin skin)
|
||||||
{
|
{
|
||||||
this.animationContent = animationContent;
|
this.animationContent = animationContent;
|
||||||
|
this.skin = skin;
|
||||||
|
|
||||||
AutoSizeAxes = Axes.Both;
|
AutoSizeAxes = Axes.Both;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(ISkinSource skin)
|
private void load()
|
||||||
{
|
{
|
||||||
var ballColour = skin.GetConfig<OsuSkinColour, Color4>(OsuSkinColour.SliderBall)?.Value ?? Color4.White;
|
var ballColour = skin.GetConfig<OsuSkinColour, Color4>(OsuSkinColour.SliderBall)?.Value ?? Color4.White;
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
|
|
||||||
private void sourceChanged()
|
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)
|
public override Drawable GetDrawableComponent(ISkinComponent component)
|
||||||
@ -49,13 +49,16 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
return followCircle;
|
return followCircle;
|
||||||
|
|
||||||
case OsuSkinComponents.SliderBall:
|
case OsuSkinComponents.SliderBall:
|
||||||
var sliderBallContent = this.GetAnimation("sliderb", true, true, animationSeparator: "");
|
// specular and nd layers must come from the same source as the ball texure.
|
||||||
|
var ballProvider = Source.FindProvider(s => s.GetTexture("sliderb") != null || s.GetTexture("sliderb0") != null);
|
||||||
|
|
||||||
|
var sliderBallContent = ballProvider.GetAnimation("sliderb", true, true, animationSeparator: "");
|
||||||
|
|
||||||
// todo: slider ball has a custom frame delay based on velocity
|
// todo: slider ball has a custom frame delay based on velocity
|
||||||
// Math.Max((150 / Velocity) * GameBase.SIXTY_FRAME_TIME, GameBase.SIXTY_FRAME_TIME);
|
// Math.Max((150 / Velocity) * GameBase.SIXTY_FRAME_TIME, GameBase.SIXTY_FRAME_TIME);
|
||||||
|
|
||||||
if (sliderBallContent != null)
|
if (sliderBallContent != null)
|
||||||
return new LegacySliderBall(sliderBallContent);
|
return new LegacySliderBall(sliderBallContent, ballProvider);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@ -84,14 +87,18 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
case OsuSkinComponents.Cursor:
|
case OsuSkinComponents.Cursor:
|
||||||
if (Source.GetTexture("cursor") != null)
|
var cursorProvider = Source.FindProvider(s => s.GetTexture("cursor") != null);
|
||||||
return new LegacyCursor();
|
|
||||||
|
if (cursorProvider != null)
|
||||||
|
return new LegacyCursor(cursorProvider);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
case OsuSkinComponents.CursorTrail:
|
case OsuSkinComponents.CursorTrail:
|
||||||
if (Source.GetTexture("cursortrail") != null)
|
var trailProvider = Source.FindProvider(s => s.GetTexture("cursortrail") != null);
|
||||||
return new LegacyCursorTrail();
|
|
||||||
|
if (trailProvider != null)
|
||||||
|
return new LegacyCursorTrail(trailProvider);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
Size = new Vector2(size);
|
Size = new Vector2(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
protected override void SkinChanged(ISkinSource skin)
|
||||||
{
|
{
|
||||||
cursorExpand = skin.GetConfig<OsuSkinConfiguration, bool>(OsuSkinConfiguration.CursorExpand)?.Value ?? true;
|
cursorExpand = skin.GetConfig<OsuSkinConfiguration, bool>(OsuSkinConfiguration.CursorExpand)?.Value ?? true;
|
||||||
}
|
}
|
||||||
|
@ -152,32 +152,35 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
|
|||||||
throw new ArgumentOutOfRangeException(nameof(component), $"Invalid component type: {component}");
|
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);
|
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
|
get
|
||||||
{
|
{
|
||||||
foreach (var name in source.LookupNames)
|
foreach (var name in base.LookupNames)
|
||||||
yield return name.Insert(name.LastIndexOf('/') + 1, "taiko-");
|
yield return name.Insert(name.LastIndexOf('/') + 1, "taiko-");
|
||||||
|
|
||||||
foreach (var name in source.LookupNames)
|
foreach (var name in base.LookupNames)
|
||||||
yield return name;
|
yield return name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Volume => source.Volume;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,8 +85,12 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(ISkinSource skin)
|
private void load(ISkinSource source)
|
||||||
{
|
{
|
||||||
|
ISkin skin = source.FindProvider(s => getAnimationFrame(s, state, 0) != null);
|
||||||
|
|
||||||
|
if (skin == null) return;
|
||||||
|
|
||||||
for (int frameIndex = 0; true; frameIndex++)
|
for (int frameIndex = 0; true; frameIndex++)
|
||||||
{
|
{
|
||||||
var texture = getAnimationFrame(skin, state, frameIndex);
|
var texture = getAnimationFrame(skin, state, frameIndex);
|
||||||
@ -112,8 +116,12 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(ISkinSource skin)
|
private void load(ISkinSource source)
|
||||||
{
|
{
|
||||||
|
ISkin skin = source.FindProvider(s => getAnimationFrame(s, TaikoMascotAnimationState.Clear, 0) != null);
|
||||||
|
|
||||||
|
if (skin == null) return;
|
||||||
|
|
||||||
foreach (var frameIndex in clear_animation_sequence)
|
foreach (var frameIndex in clear_animation_sequence)
|
||||||
{
|
{
|
||||||
var texture = getAnimationFrame(skin, TaikoMascotAnimationState.Clear, frameIndex);
|
var texture = getAnimationFrame(skin, TaikoMascotAnimationState.Clear, frameIndex);
|
||||||
|
@ -123,6 +123,8 @@ namespace osu.Game.Tests.Gameplay
|
|||||||
|
|
||||||
public ISample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
|
public ISample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => null;
|
||||||
|
|
||||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
|
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
|
||||||
{
|
{
|
||||||
switch (lookup)
|
switch (lookup)
|
||||||
|
@ -61,6 +61,7 @@ namespace osu.Game.Tests.NonVisual.Skinning
|
|||||||
public Drawable GetDrawableComponent(ISkinComponent component) => throw new NotSupportedException();
|
public Drawable GetDrawableComponent(ISkinComponent component) => throw new NotSupportedException();
|
||||||
public ISample GetSample(ISampleInfo sampleInfo) => throw new NotSupportedException();
|
public ISample GetSample(ISampleInfo sampleInfo) => throw new NotSupportedException();
|
||||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => 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
|
private class TestAnimationTimeReference : IAnimationTimeReference
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -222,6 +223,8 @@ namespace osu.Game.Tests.Skins
|
|||||||
public ISample GetSample(ISampleInfo sampleInfo) => skin.GetSample(sampleInfo);
|
public ISample GetSample(ISampleInfo sampleInfo) => skin.GetSample(sampleInfo);
|
||||||
|
|
||||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => skin.GetConfig<TLookup, TValue>(lookup);
|
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => skin.GetConfig<TLookup, TValue>(lookup);
|
||||||
|
|
||||||
|
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => skin.FindProvider(lookupFunction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,133 @@
|
|||||||
|
// 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.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Screens;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Graphics.Backgrounds;
|
||||||
|
using osu.Game.Online.API;
|
||||||
|
using osu.Game.Screens;
|
||||||
|
using osu.Game.Screens.Backgrounds;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
using osu.Game.Users;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Background
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class TestSceneBackgroundScreenDefault : OsuTestScene
|
||||||
|
{
|
||||||
|
private BackgroundScreenStack stack;
|
||||||
|
private BackgroundScreenDefault screen;
|
||||||
|
|
||||||
|
private Graphics.Backgrounds.Background getCurrentBackground() => screen.ChildrenOfType<Graphics.Backgrounds.Background>().FirstOrDefault();
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private SkinManager skins { get; set; }
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OsuConfigManager config { get; set; }
|
||||||
|
|
||||||
|
[SetUpSteps]
|
||||||
|
public void SetUpSteps()
|
||||||
|
{
|
||||||
|
AddStep("create background stack", () => Child = stack = new BackgroundScreenStack());
|
||||||
|
AddStep("push default screen", () => stack.Push(screen = new BackgroundScreenDefault(false)));
|
||||||
|
AddUntilStep("wait for screen to load", () => screen.IsCurrentScreen());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestBackgroundTypeSwitch()
|
||||||
|
{
|
||||||
|
setSupporter(true);
|
||||||
|
|
||||||
|
setSourceMode(BackgroundSource.Beatmap);
|
||||||
|
AddUntilStep("is beatmap background", () => getCurrentBackground() is BeatmapBackground);
|
||||||
|
|
||||||
|
setSourceMode(BackgroundSource.BeatmapWithStoryboard);
|
||||||
|
AddUntilStep("is storyboard background", () => getCurrentBackground() is BeatmapBackgroundWithStoryboard);
|
||||||
|
|
||||||
|
setSourceMode(BackgroundSource.Skin);
|
||||||
|
AddUntilStep("is default background", () => getCurrentBackground().GetType() == typeof(Graphics.Backgrounds.Background));
|
||||||
|
|
||||||
|
setCustomSkin();
|
||||||
|
AddUntilStep("is skin background", () => getCurrentBackground() is SkinBackground);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestTogglingSupporterTogglesBeatmapBackground()
|
||||||
|
{
|
||||||
|
setSourceMode(BackgroundSource.Beatmap);
|
||||||
|
|
||||||
|
setSupporter(true);
|
||||||
|
AddUntilStep("is beatmap background", () => getCurrentBackground() is BeatmapBackground);
|
||||||
|
|
||||||
|
setSupporter(false);
|
||||||
|
AddUntilStep("is default background", () => !(getCurrentBackground() is BeatmapBackground));
|
||||||
|
|
||||||
|
setSupporter(true);
|
||||||
|
AddUntilStep("is beatmap background", () => getCurrentBackground() is BeatmapBackground);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase(BackgroundSource.Beatmap, typeof(BeatmapBackground))]
|
||||||
|
[TestCase(BackgroundSource.BeatmapWithStoryboard, typeof(BeatmapBackgroundWithStoryboard))]
|
||||||
|
[TestCase(BackgroundSource.Skin, typeof(SkinBackground))]
|
||||||
|
public void TestBackgroundDoesntReloadOnNoChange(BackgroundSource source, Type backgroundType)
|
||||||
|
{
|
||||||
|
Graphics.Backgrounds.Background last = null;
|
||||||
|
|
||||||
|
setSourceMode(source);
|
||||||
|
setSupporter(true);
|
||||||
|
if (source == BackgroundSource.Skin)
|
||||||
|
setCustomSkin();
|
||||||
|
|
||||||
|
AddUntilStep("wait for beatmap background to be loaded", () => (last = getCurrentBackground())?.GetType() == backgroundType);
|
||||||
|
AddAssert("next doesn't load new background", () => screen.Next() == false);
|
||||||
|
|
||||||
|
// doesn't really need to be checked but might as well.
|
||||||
|
AddWaitStep("wait a bit", 5);
|
||||||
|
AddUntilStep("ensure same background instance", () => last == getCurrentBackground());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestBackgroundCyclingOnDefaultSkin([Values] bool supporter)
|
||||||
|
{
|
||||||
|
Graphics.Backgrounds.Background last = null;
|
||||||
|
|
||||||
|
setSourceMode(BackgroundSource.Skin);
|
||||||
|
setSupporter(supporter);
|
||||||
|
setDefaultSkin();
|
||||||
|
|
||||||
|
AddUntilStep("wait for beatmap background to be loaded", () => (last = getCurrentBackground())?.GetType() == typeof(Graphics.Backgrounds.Background));
|
||||||
|
AddAssert("next cycles background", () => screen.Next());
|
||||||
|
|
||||||
|
// doesn't really need to be checked but might as well.
|
||||||
|
AddWaitStep("wait a bit", 5);
|
||||||
|
AddUntilStep("ensure different background instance", () => last != getCurrentBackground());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setSourceMode(BackgroundSource source) =>
|
||||||
|
AddStep($"set background mode to {source}", () => config.SetValue(OsuSetting.MenuBackgroundSource, source));
|
||||||
|
|
||||||
|
private void setSupporter(bool isSupporter) =>
|
||||||
|
AddStep($"set supporter {isSupporter}", () => ((DummyAPIAccess)API).LocalUser.Value = new User
|
||||||
|
{
|
||||||
|
IsSupporter = isSupporter,
|
||||||
|
Id = API.LocalUser.Value.Id + 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
private void setCustomSkin()
|
||||||
|
{
|
||||||
|
// feign a skin switch. this doesn't do anything except force CurrentSkin to become a LegacySkin.
|
||||||
|
AddStep("set custom skin", () => skins.CurrentSkinInfo.Value = new SkinInfo { ID = 5 });
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setDefaultSkin() => AddStep("set default skin", () => skins.CurrentSkinInfo.SetDefault());
|
||||||
|
|
||||||
|
[TearDownSteps]
|
||||||
|
public void TearDown() => setDefaultSkin();
|
||||||
|
}
|
||||||
|
}
|
@ -4,14 +4,15 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Testing;
|
||||||
using osu.Framework.Threading;
|
using osu.Framework.Threading;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Rulesets.Catch.Scoring;
|
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Mania.Scoring;
|
using osu.Game.Rulesets.Mania.Scoring;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
@ -29,15 +30,22 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
public class TestSceneHitErrorMeter : OsuTestScene
|
public class TestSceneHitErrorMeter : OsuTestScene
|
||||||
{
|
{
|
||||||
[Cached]
|
[Cached(typeof(ScoreProcessor))]
|
||||||
private ScoreProcessor scoreProcessor = new ScoreProcessor();
|
private TestScoreProcessor scoreProcessor = new TestScoreProcessor();
|
||||||
|
|
||||||
[Cached(typeof(DrawableRuleset))]
|
[Cached(typeof(DrawableRuleset))]
|
||||||
private TestDrawableRuleset drawableRuleset = new TestDrawableRuleset();
|
private TestDrawableRuleset drawableRuleset = new TestDrawableRuleset();
|
||||||
|
|
||||||
public TestSceneHitErrorMeter()
|
[SetUpSteps]
|
||||||
|
public void SetUp()
|
||||||
{
|
{
|
||||||
recreateDisplay(new OsuHitWindows(), 5);
|
AddStep("reset score processor", () => scoreProcessor.Reset());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestBasic()
|
||||||
|
{
|
||||||
|
AddStep("create display", () => recreateDisplay(new OsuHitWindows(), 5));
|
||||||
|
|
||||||
AddRepeatStep("New random judgement", () => newJudgement(), 40);
|
AddRepeatStep("New random judgement", () => newJudgement(), 40);
|
||||||
|
|
||||||
@ -45,12 +53,11 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddRepeatStep("New max positive", () => newJudgement(drawableRuleset.HitWindows.WindowFor(HitResult.Meh)), 20);
|
AddRepeatStep("New max positive", () => newJudgement(drawableRuleset.HitWindows.WindowFor(HitResult.Meh)), 20);
|
||||||
AddStep("New fixed judgement (50ms)", () => newJudgement(50));
|
AddStep("New fixed judgement (50ms)", () => newJudgement(50));
|
||||||
|
|
||||||
|
ScheduledDelegate del = null;
|
||||||
AddStep("Judgement barrage", () =>
|
AddStep("Judgement barrage", () =>
|
||||||
{
|
{
|
||||||
int runCount = 0;
|
int runCount = 0;
|
||||||
|
|
||||||
ScheduledDelegate del = null;
|
|
||||||
|
|
||||||
del = Scheduler.AddDelayed(() =>
|
del = Scheduler.AddDelayed(() =>
|
||||||
{
|
{
|
||||||
newJudgement(runCount++ / 10f);
|
newJudgement(runCount++ / 10f);
|
||||||
@ -60,6 +67,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
del?.Cancel();
|
del?.Cancel();
|
||||||
}, 10, true);
|
}, 10, true);
|
||||||
});
|
});
|
||||||
|
AddUntilStep("wait for barrage", () => del.Cancelled);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -84,10 +92,49 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestCatch()
|
public void TestEmpty()
|
||||||
{
|
{
|
||||||
AddStep("OD 1", () => recreateDisplay(new CatchHitWindows(), 1));
|
AddStep("empty windows", () => recreateDisplay(HitWindows.Empty, 5));
|
||||||
AddStep("OD 10", () => recreateDisplay(new CatchHitWindows(), 10));
|
|
||||||
|
AddStep("hit", () => newJudgement());
|
||||||
|
AddAssert("no bars added", () => !this.ChildrenOfType<BarHitErrorMeter.JudgementLine>().Any());
|
||||||
|
AddAssert("circle added", () =>
|
||||||
|
this.ChildrenOfType<ColourHitErrorMeter>().All(
|
||||||
|
meter => meter.ChildrenOfType<ColourHitErrorMeter.HitErrorCircle>().Count() == 1));
|
||||||
|
|
||||||
|
AddStep("miss", () => newJudgement(50, HitResult.Miss));
|
||||||
|
AddAssert("no bars added", () => !this.ChildrenOfType<BarHitErrorMeter.JudgementLine>().Any());
|
||||||
|
AddAssert("circle added", () =>
|
||||||
|
this.ChildrenOfType<ColourHitErrorMeter>().All(
|
||||||
|
meter => meter.ChildrenOfType<ColourHitErrorMeter.HitErrorCircle>().Count() == 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestBonus()
|
||||||
|
{
|
||||||
|
AddStep("OD 1", () => recreateDisplay(new OsuHitWindows(), 1));
|
||||||
|
|
||||||
|
AddStep("small bonus", () => newJudgement(result: HitResult.SmallBonus));
|
||||||
|
AddAssert("no bars added", () => !this.ChildrenOfType<BarHitErrorMeter.JudgementLine>().Any());
|
||||||
|
AddAssert("no circle added", () => !this.ChildrenOfType<ColourHitErrorMeter.HitErrorCircle>().Any());
|
||||||
|
|
||||||
|
AddStep("large bonus", () => newJudgement(result: HitResult.LargeBonus));
|
||||||
|
AddAssert("no bars added", () => !this.ChildrenOfType<BarHitErrorMeter.JudgementLine>().Any());
|
||||||
|
AddAssert("no circle added", () => !this.ChildrenOfType<ColourHitErrorMeter.HitErrorCircle>().Any());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestIgnore()
|
||||||
|
{
|
||||||
|
AddStep("OD 1", () => recreateDisplay(new OsuHitWindows(), 1));
|
||||||
|
|
||||||
|
AddStep("ignore hit", () => newJudgement(result: HitResult.IgnoreHit));
|
||||||
|
AddAssert("no bars added", () => !this.ChildrenOfType<BarHitErrorMeter.JudgementLine>().Any());
|
||||||
|
AddAssert("no circle added", () => !this.ChildrenOfType<ColourHitErrorMeter.HitErrorCircle>().Any());
|
||||||
|
|
||||||
|
AddStep("ignore miss", () => newJudgement(result: HitResult.IgnoreMiss));
|
||||||
|
AddAssert("no bars added", () => !this.ChildrenOfType<BarHitErrorMeter.JudgementLine>().Any());
|
||||||
|
AddAssert("no circle added", () => !this.ChildrenOfType<ColourHitErrorMeter.HitErrorCircle>().Any());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void recreateDisplay(HitWindows hitWindows, float overallDifficulty)
|
private void recreateDisplay(HitWindows hitWindows, float overallDifficulty)
|
||||||
@ -154,12 +201,12 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void newJudgement(double offset = 0)
|
private void newJudgement(double offset = 0, HitResult result = HitResult.Perfect)
|
||||||
{
|
{
|
||||||
scoreProcessor.ApplyResult(new JudgementResult(new HitCircle { HitWindows = drawableRuleset.HitWindows }, new Judgement())
|
scoreProcessor.ApplyResult(new JudgementResult(new HitCircle { HitWindows = drawableRuleset.HitWindows }, new Judgement())
|
||||||
{
|
{
|
||||||
TimeOffset = offset == 0 ? RNG.Next(-150, 150) : offset,
|
TimeOffset = offset == 0 ? RNG.Next(-150, 150) : offset,
|
||||||
Type = HitResult.Perfect,
|
Type = result,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,5 +246,10 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
public override void CancelResume() => throw new NotImplementedException();
|
public override void CancelResume() => throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class TestScoreProcessor : ScoreProcessor
|
||||||
|
{
|
||||||
|
public void Reset() => base.Reset(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,9 +42,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
Spacing = new Vector2(10),
|
Spacing = new Vector2(10),
|
||||||
Children = new[]
|
Children = new[]
|
||||||
{
|
{
|
||||||
new ExposedSkinnableDrawable("default", _ => new DefaultBox(), _ => true),
|
new ExposedSkinnableDrawable("default", _ => new DefaultBox()),
|
||||||
new ExposedSkinnableDrawable("available", _ => new DefaultBox(), _ => true),
|
new ExposedSkinnableDrawable("available", _ => new DefaultBox()),
|
||||||
new ExposedSkinnableDrawable("available", _ => new DefaultBox(), _ => true, ConfineMode.NoScaling)
|
new ExposedSkinnableDrawable("available", _ => new DefaultBox(), ConfineMode.NoScaling)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -73,9 +73,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
Spacing = new Vector2(10),
|
Spacing = new Vector2(10),
|
||||||
Children = new[]
|
Children = new[]
|
||||||
{
|
{
|
||||||
new ExposedSkinnableDrawable("default", _ => new DefaultBox(), _ => true),
|
new ExposedSkinnableDrawable("default", _ => new DefaultBox()),
|
||||||
new ExposedSkinnableDrawable("available", _ => new DefaultBox(), _ => true, ConfineMode.ScaleToFit),
|
new ExposedSkinnableDrawable("available", _ => new DefaultBox(), ConfineMode.ScaleToFit),
|
||||||
new ExposedSkinnableDrawable("available", _ => new DefaultBox(), _ => true, ConfineMode.NoScaling)
|
new ExposedSkinnableDrawable("available", _ => new DefaultBox(), ConfineMode.NoScaling)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -100,7 +100,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
Child = new SkinProvidingContainer(secondarySource)
|
Child = new SkinProvidingContainer(secondarySource)
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Child = consumer = new SkinConsumer("test", name => new NamedBox("Default Implementation"), source => true)
|
Child = consumer = new SkinConsumer("test", name => new NamedBox("Default Implementation"))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@ -129,7 +129,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
AddStep("add permissive", () => target.Add(consumer = new SkinConsumer("test", name => new NamedBox("Default Implementation"), source => true)));
|
AddStep("add permissive", () => target.Add(consumer = new SkinConsumer("test", name => new NamedBox("Default Implementation"))));
|
||||||
AddAssert("consumer using override source", () => consumer.Drawable is SecondarySourceBox);
|
AddAssert("consumer using override source", () => consumer.Drawable is SecondarySourceBox);
|
||||||
AddAssert("skinchanged only called once", () => consumer.SkinChangedCount == 1);
|
AddAssert("skinchanged only called once", () => consumer.SkinChangedCount == 1);
|
||||||
}
|
}
|
||||||
@ -152,7 +152,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
AddStep("add permissive", () => target.Add(consumer = new SkinConsumer("test", name => new NamedBox("Default Implementation"), source => true)));
|
AddStep("add permissive", () => target.Add(consumer = new SkinConsumer("test", name => new NamedBox("Default Implementation"))));
|
||||||
AddAssert("consumer using override source", () => consumer.Drawable is SecondarySourceBox);
|
AddAssert("consumer using override source", () => consumer.Drawable is SecondarySourceBox);
|
||||||
AddStep("disable", () => target.Disable());
|
AddStep("disable", () => target.Disable());
|
||||||
AddAssert("consumer using base source", () => consumer.Drawable is BaseSourceBox);
|
AddAssert("consumer using base source", () => consumer.Drawable is BaseSourceBox);
|
||||||
@ -180,9 +180,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
public new Drawable Drawable => base.Drawable;
|
public new Drawable Drawable => base.Drawable;
|
||||||
|
|
||||||
public ExposedSkinnableDrawable(string name, Func<ISkinComponent, Drawable> defaultImplementation, Func<ISkinSource, bool> allowFallback = null,
|
public ExposedSkinnableDrawable(string name, Func<ISkinComponent, Drawable> defaultImplementation, ConfineMode confineMode = ConfineMode.ScaleToFit)
|
||||||
ConfineMode confineMode = ConfineMode.ScaleToFit)
|
: base(new TestSkinComponent(name), defaultImplementation, confineMode)
|
||||||
: base(new TestSkinComponent(name), defaultImplementation, allowFallback, confineMode)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -250,14 +249,14 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
public new Drawable Drawable => base.Drawable;
|
public new Drawable Drawable => base.Drawable;
|
||||||
public int SkinChangedCount { get; private set; }
|
public int SkinChangedCount { get; private set; }
|
||||||
|
|
||||||
public SkinConsumer(string name, Func<ISkinComponent, Drawable> defaultImplementation, Func<ISkinSource, bool> allowFallback = null)
|
public SkinConsumer(string name, Func<ISkinComponent, Drawable> defaultImplementation)
|
||||||
: base(new TestSkinComponent(name), defaultImplementation, allowFallback)
|
: base(new TestSkinComponent(name), defaultImplementation)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
protected override void SkinChanged(ISkinSource skin)
|
||||||
{
|
{
|
||||||
base.SkinChanged(skin, allowFallback);
|
base.SkinChanged(skin);
|
||||||
SkinChangedCount++;
|
SkinChangedCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -301,6 +300,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
public ISample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
|
public ISample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
|
||||||
|
|
||||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => 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
|
private class SecondarySource : ISkin
|
||||||
@ -312,6 +313,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
public ISample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
|
public ISample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
|
||||||
|
|
||||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => 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))]
|
[Cached(typeof(ISkinSource))]
|
||||||
@ -325,6 +328,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => throw new NotImplementedException();
|
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
|
public event Action SourceChanged
|
||||||
{
|
{
|
||||||
add { }
|
add { }
|
||||||
|
@ -29,14 +29,13 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
AddStep("setup hierarchy", () =>
|
AddStep("setup hierarchy", () =>
|
||||||
{
|
{
|
||||||
Children = new Drawable[]
|
Child = skinSource = new TestSkinSourceContainer
|
||||||
{
|
{
|
||||||
skinSource = new TestSkinSourceContainer
|
RelativeSizeAxes = Axes.Both,
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Child = skinnableSound = new PausableSkinnableSound(new SampleInfo("Gameplay/normal-sliderslide"))
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// has to be added after the hierarchy above else the `ISkinSource` dependency won't be cached.
|
||||||
|
skinSource.Add(skinnableSound = new PausableSkinnableSound(new SampleInfo("Gameplay/normal-sliderslide")));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,6 +146,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => source?.GetTexture(componentName, wrapModeS, wrapModeT);
|
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => source?.GetTexture(componentName, wrapModeS, wrapModeT);
|
||||||
public ISample GetSample(ISampleInfo sampleInfo) => source?.GetSample(sampleInfo);
|
public ISample GetSample(ISampleInfo sampleInfo) => source?.GetSample(sampleInfo);
|
||||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => source?.GetConfig<TLookup, TValue>(lookup);
|
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()
|
public void TriggerSourceChanged()
|
||||||
{
|
{
|
||||||
|
@ -9,16 +9,19 @@ using osu.Framework.Screens;
|
|||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Online.Multiplayer;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Overlays.Mods;
|
using osu.Game.Overlays.Mods;
|
||||||
using osu.Game.Overlays.Toolbar;
|
using osu.Game.Overlays.Toolbar;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Osu.Mods;
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
|
using osu.Game.Screens.OnlinePlay.Components;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
using osu.Game.Screens.Ranking;
|
using osu.Game.Screens.Ranking;
|
||||||
using osu.Game.Screens.Select;
|
using osu.Game.Screens.Select;
|
||||||
using osu.Game.Screens.Select.Options;
|
using osu.Game.Screens.Select.Options;
|
||||||
using osu.Game.Tests.Beatmaps.IO;
|
using osu.Game.Tests.Beatmaps.IO;
|
||||||
|
using osu.Game.Tests.Visual.Multiplayer;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
|
|
||||||
@ -152,6 +155,14 @@ namespace osu.Game.Tests.Visual.Navigation
|
|||||||
AddUntilStep("wait for track", () => !Game.MusicController.CurrentTrack.IsDummyDevice && Game.MusicController.IsPlaying);
|
AddUntilStep("wait for track", () => !Game.MusicController.CurrentTrack.IsDummyDevice && Game.MusicController.IsPlaying);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestPushSongSelectAndPressBackButtonImmediately()
|
||||||
|
{
|
||||||
|
AddStep("push song select", () => Game.ScreenStack.Push(new TestPlaySongSelect()));
|
||||||
|
AddStep("press back button", () => Game.ChildrenOfType<BackButton>().First().Action());
|
||||||
|
AddWaitStep("wait two frames", 2);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestExitSongSelectWithClick()
|
public void TestExitSongSelectWithClick()
|
||||||
{
|
{
|
||||||
@ -298,6 +309,18 @@ namespace osu.Game.Tests.Visual.Navigation
|
|||||||
AddAssert("Toolbar is hidden", () => Game.Toolbar.State.Value == Visibility.Hidden);
|
AddAssert("Toolbar is hidden", () => Game.Toolbar.State.Value == Visibility.Hidden);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestPushMatchSubScreenAndPressBackButtonImmediately()
|
||||||
|
{
|
||||||
|
TestMultiplayer multiplayer = null;
|
||||||
|
|
||||||
|
PushAndConfirm(() => multiplayer = new TestMultiplayer());
|
||||||
|
|
||||||
|
AddStep("open room", () => multiplayer.OpenNewRoom());
|
||||||
|
AddStep("press back button", () => Game.ChildrenOfType<BackButton>().First().Action());
|
||||||
|
AddWaitStep("wait two frames", 2);
|
||||||
|
}
|
||||||
|
|
||||||
private void pushEscape() =>
|
private void pushEscape() =>
|
||||||
AddStep("Press escape", () => InputManager.Key(Key.Escape));
|
AddStep("Press escape", () => InputManager.Key(Key.Escape));
|
||||||
|
|
||||||
@ -322,5 +345,18 @@ namespace osu.Game.Tests.Visual.Navigation
|
|||||||
|
|
||||||
protected override bool DisplayStableImportPrompt => false;
|
protected override bool DisplayStableImportPrompt => false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class TestMultiplayer : Screens.OnlinePlay.Multiplayer.Multiplayer
|
||||||
|
{
|
||||||
|
[Cached(typeof(MultiplayerClient))]
|
||||||
|
public readonly TestMultiplayerClient Client;
|
||||||
|
|
||||||
|
public TestMultiplayer()
|
||||||
|
{
|
||||||
|
Client = new TestMultiplayerClient((TestMultiplayerRoomManager)RoomManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override RoomManager CreateRoomManager() => new TestMultiplayerRoomManager();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -325,6 +325,7 @@ namespace osu.Game.Beatmaps
|
|||||||
public ISkin Skin => skin.Value;
|
public ISkin Skin => skin.Value;
|
||||||
|
|
||||||
protected abstract ISkin GetSkin();
|
protected abstract ISkin GetSkin();
|
||||||
|
|
||||||
private readonly RecyclableLazy<ISkin> skin;
|
private readonly RecyclableLazy<ISkin> skin;
|
||||||
|
|
||||||
public abstract Stream GetStream(string storagePath);
|
public abstract Stream GetStream(string storagePath);
|
||||||
|
@ -264,14 +264,18 @@ namespace osu.Game.Collections
|
|||||||
using (var sw = new SerializationWriter(storage.GetStream(database_name, FileAccess.Write)))
|
using (var sw = new SerializationWriter(storage.GetStream(database_name, FileAccess.Write)))
|
||||||
{
|
{
|
||||||
sw.Write(database_version);
|
sw.Write(database_version);
|
||||||
sw.Write(Collections.Count);
|
|
||||||
|
|
||||||
foreach (var c in Collections)
|
var collectionsCopy = Collections.ToArray();
|
||||||
|
sw.Write(collectionsCopy.Length);
|
||||||
|
|
||||||
|
foreach (var c in collectionsCopy)
|
||||||
{
|
{
|
||||||
sw.Write(c.Name.Value);
|
sw.Write(c.Name.Value);
|
||||||
sw.Write(c.Beatmaps.Count);
|
|
||||||
|
|
||||||
foreach (var b in c.Beatmaps)
|
var beatmapsCopy = c.Beatmaps.ToArray();
|
||||||
|
sw.Write(beatmapsCopy.Length);
|
||||||
|
|
||||||
|
foreach (var b in beatmapsCopy)
|
||||||
sw.Write(b.MD5Hash);
|
sw.Write(b.MD5Hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
@ -14,7 +15,7 @@ namespace osu.Game.Graphics.Backgrounds
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A background which offers blurring via a <see cref="BufferedContainer"/> on demand.
|
/// A background which offers blurring via a <see cref="BufferedContainer"/> on demand.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Background : CompositeDrawable
|
public class Background : CompositeDrawable, IEquatable<Background>
|
||||||
{
|
{
|
||||||
private const float blur_scale = 0.5f;
|
private const float blur_scale = 0.5f;
|
||||||
|
|
||||||
@ -71,5 +72,14 @@ namespace osu.Game.Graphics.Backgrounds
|
|||||||
|
|
||||||
bufferedContainer?.BlurTo(newBlurSigma * blur_scale, duration, easing);
|
bufferedContainer?.BlurTo(newBlurSigma * blur_scale, duration, easing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual bool Equals(Background other)
|
||||||
|
{
|
||||||
|
if (ReferenceEquals(null, other)) return false;
|
||||||
|
if (ReferenceEquals(this, other)) return true;
|
||||||
|
|
||||||
|
return other.GetType() == GetType()
|
||||||
|
&& other.textureName == textureName;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,5 +24,14 @@ namespace osu.Game.Graphics.Backgrounds
|
|||||||
{
|
{
|
||||||
Sprite.Texture = Beatmap?.Background ?? textures.Get(fallbackTextureName);
|
Sprite.Texture = Beatmap?.Background ?? textures.Get(fallbackTextureName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override bool Equals(Background other)
|
||||||
|
{
|
||||||
|
if (ReferenceEquals(null, other)) return false;
|
||||||
|
if (ReferenceEquals(this, other)) return true;
|
||||||
|
|
||||||
|
return other.GetType() == GetType()
|
||||||
|
&& ((BeatmapBackground)other).Beatmap == Beatmap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
34
osu.Game/Graphics/Backgrounds/SkinBackground.cs
Normal file
34
osu.Game/Graphics/Backgrounds/SkinBackground.cs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
|
namespace osu.Game.Graphics.Backgrounds
|
||||||
|
{
|
||||||
|
internal class SkinBackground : Background
|
||||||
|
{
|
||||||
|
private readonly Skin skin;
|
||||||
|
|
||||||
|
public SkinBackground(Skin skin, string fallbackTextureName)
|
||||||
|
: base(fallbackTextureName)
|
||||||
|
{
|
||||||
|
this.skin = skin;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
Sprite.Texture = skin.GetTexture("menu-background") ?? Sprite.Texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(Background other)
|
||||||
|
{
|
||||||
|
if (ReferenceEquals(null, other)) return false;
|
||||||
|
if (ReferenceEquals(this, other)) return true;
|
||||||
|
|
||||||
|
return other.GetType() == GetType()
|
||||||
|
&& ((SkinBackground)other).skin.SkinInfo.Equals(skin.SkinInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -57,12 +57,6 @@ namespace osu.Game.Graphics.Backgrounds
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether we want to expire triangles as they exit our draw area completely.
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Unused.")] // Can be removed 20210518
|
|
||||||
protected virtual bool ExpireOffScreenTriangles => true;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether we should create new triangles as others expire.
|
/// Whether we should create new triangles as others expire.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -21,7 +21,12 @@ namespace osu.Game.Online.API.Requests.Responses
|
|||||||
{
|
{
|
||||||
var ruleset = rulesets.GetRuleset(OnlineRulesetID);
|
var ruleset = rulesets.GetRuleset(OnlineRulesetID);
|
||||||
|
|
||||||
var mods = Mods != null ? ruleset.CreateInstance().GetAllMods().Where(mod => Mods.Contains(mod.Acronym)).ToArray() : Array.Empty<Mod>();
|
var rulesetInstance = ruleset.CreateInstance();
|
||||||
|
|
||||||
|
var mods = Mods != null ? rulesetInstance.GetAllMods().Where(mod => Mods.Contains(mod.Acronym)).ToArray() : Array.Empty<Mod>();
|
||||||
|
|
||||||
|
// all API scores provided by this class are considered to be legacy.
|
||||||
|
mods = mods.Append(rulesetInstance.GetAllMods().OfType<ModClassic>().Single()).ToArray();
|
||||||
|
|
||||||
var scoreInfo = new ScoreInfo
|
var scoreInfo = new ScoreInfo
|
||||||
{
|
{
|
||||||
@ -38,7 +43,6 @@ namespace osu.Game.Online.API.Requests.Responses
|
|||||||
Rank = Rank,
|
Rank = Rank,
|
||||||
Ruleset = ruleset,
|
Ruleset = ruleset,
|
||||||
Mods = mods,
|
Mods = mods,
|
||||||
IsLegacyScore = true
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (Statistics != null)
|
if (Statistics != null)
|
||||||
|
@ -651,9 +651,10 @@ namespace osu.Game
|
|||||||
Origin = Anchor.BottomLeft,
|
Origin = Anchor.BottomLeft,
|
||||||
Action = () =>
|
Action = () =>
|
||||||
{
|
{
|
||||||
var currentScreen = ScreenStack.CurrentScreen as IOsuScreen;
|
if (!(ScreenStack.CurrentScreen is IOsuScreen currentScreen))
|
||||||
|
return;
|
||||||
|
|
||||||
if (currentScreen?.AllowBackButton == true && !currentScreen.OnBackButton())
|
if (!((Drawable)currentScreen).IsLoaded || (currentScreen.AllowBackButton && !currentScreen.OnBackButton()))
|
||||||
ScreenStack.Exit();
|
ScreenStack.Exit();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
@ -32,18 +31,6 @@ namespace osu.Game.Rulesets.Judgements
|
|||||||
|
|
||||||
private readonly Container aboveHitObjectsContent;
|
private readonly Container aboveHitObjectsContent;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Duration of initial fade in.
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Apply any animations manually via ApplyHitAnimations / ApplyMissAnimations. Defaults were moved inside skinned components.")]
|
|
||||||
protected virtual double FadeInDuration => 100;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Duration to wait until fade out begins. Defaults to <see cref="FadeInDuration"/>.
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Apply any animations manually via ApplyHitAnimations / ApplyMissAnimations. Defaults were moved inside skinned components.")]
|
|
||||||
protected virtual double FadeOutDelay => FadeInDuration;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a drawable which visualises a <see cref="Judgements.Judgement"/>.
|
/// Creates a drawable which visualises a <see cref="Judgements.Judgement"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
|
||||||
@ -69,14 +68,6 @@ namespace osu.Game.Rulesets.Judgements
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public double MaxHealthIncrease => HealthIncreaseFor(MaxResult);
|
public double MaxHealthIncrease => HealthIncreaseFor(MaxResult);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Retrieves the numeric score representation of a <see cref="HitResult"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="result">The <see cref="HitResult"/> to find the numeric score representation for.</param>
|
|
||||||
/// <returns>The numeric score representation of <paramref name="result"/>.</returns>
|
|
||||||
[Obsolete("Has no effect. Use ToNumericResult(HitResult) (standardised across all rulesets).")] // Can be made non-virtual 20210328
|
|
||||||
protected virtual int NumericResultFor(HitResult result) => ToNumericResult(result);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves the numeric score representation of a <see cref="JudgementResult"/>.
|
/// Retrieves the numeric score representation of a <see cref="JudgementResult"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -192,7 +192,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Applies a hit object to be represented by this <see cref="DrawableHitObject"/>.
|
/// Applies a hit object to be represented by this <see cref="DrawableHitObject"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Obsolete("Use either overload of Apply that takes a single argument of type HitObject or HitObjectLifetimeEntry")]
|
[Obsolete("Use either overload of Apply that takes a single argument of type HitObject or HitObjectLifetimeEntry")] // Can be removed 20211021.
|
||||||
public void Apply([NotNull] HitObject hitObject, [CanBeNull] HitObjectLifetimeEntry lifetimeEntry)
|
public void Apply([NotNull] HitObject hitObject, [CanBeNull] HitObjectLifetimeEntry lifetimeEntry)
|
||||||
{
|
{
|
||||||
if (lifetimeEntry != null)
|
if (lifetimeEntry != null)
|
||||||
@ -409,11 +409,6 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
|||||||
using (BeginAbsoluteSequence(StateUpdateTime, true))
|
using (BeginAbsoluteSequence(StateUpdateTime, true))
|
||||||
UpdateStartTimeStateTransforms();
|
UpdateStartTimeStateTransforms();
|
||||||
|
|
||||||
#pragma warning disable 618
|
|
||||||
using (BeginAbsoluteSequence(StateUpdateTime + (Result?.TimeOffset ?? 0), true))
|
|
||||||
UpdateStateTransforms(newState);
|
|
||||||
#pragma warning restore 618
|
|
||||||
|
|
||||||
using (BeginAbsoluteSequence(HitStateUpdateTime, true))
|
using (BeginAbsoluteSequence(HitStateUpdateTime, true))
|
||||||
UpdateHitStateTransforms(newState);
|
UpdateHitStateTransforms(newState);
|
||||||
|
|
||||||
@ -447,7 +442,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
|||||||
/// By default, this will fade in the object from zero with no duration.
|
/// By default, this will fade in the object from zero with no duration.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// This is called once before every <see cref="UpdateStateTransforms"/>. This is to ensure a good state in the case
|
/// This is called once before every <see cref="UpdateHitStateTransforms"/>. This is to ensure a good state in the case
|
||||||
/// the <see cref="JudgementResult.TimeOffset"/> was negative and potentially altered the pre-hit transforms.
|
/// the <see cref="JudgementResult.TimeOffset"/> was negative and potentially altered the pre-hit transforms.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
protected virtual void UpdateInitialTransforms()
|
protected virtual void UpdateInitialTransforms()
|
||||||
@ -455,16 +450,6 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
|||||||
this.FadeInFromZero();
|
this.FadeInFromZero();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Apply transforms based on the current <see cref="ArmedState"/>. Previous states are automatically cleared.
|
|
||||||
/// In the case of a non-idle <see cref="ArmedState"/>, and if <see cref="Drawable.LifetimeEnd"/> was not set during this call, <see cref="Drawable.Expire"/> will be invoked.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="state">The new armed state.</param>
|
|
||||||
[Obsolete("Use UpdateStartTimeStateTransforms and UpdateHitStateTransforms instead")] // Can be removed 20210504
|
|
||||||
protected virtual void UpdateStateTransforms(ArmedState state)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Apply passive transforms at the <see cref="HitObject"/>'s StartTime.
|
/// Apply passive transforms at the <see cref="HitObject"/>'s StartTime.
|
||||||
/// This is called each time <see cref="State"/> changes.
|
/// This is called each time <see cref="State"/> changes.
|
||||||
@ -520,23 +505,6 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
|||||||
AccentColour.Value = combo.GetComboColour(comboColours);
|
AccentColour.Value = combo.GetComboColour(comboColours);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Called to retrieve the combo colour. Automatically assigned to <see cref="AccentColour"/>.
|
|
||||||
/// Defaults to using <see cref="IHasComboInformation.ComboIndex"/> to decide on a colour.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// This will only be called if the <see cref="HitObject"/> implements <see cref="IHasComboInformation"/>.
|
|
||||||
/// </remarks>
|
|
||||||
/// <param name="comboColours">A list of combo colours provided by the beatmap or skin. Can be null if not available.</param>
|
|
||||||
[Obsolete("Unused. Implement IHasComboInformation and IHasComboInformation.GetComboColour() on the HitObject model instead.")] // Can be removed 20210527
|
|
||||||
protected virtual Color4 GetComboColour(IReadOnlyList<Color4> comboColours)
|
|
||||||
{
|
|
||||||
if (!(HitObject is IHasComboInformation combo))
|
|
||||||
throw new InvalidOperationException($"{nameof(HitObject)} must implement {nameof(IHasComboInformation)}");
|
|
||||||
|
|
||||||
return comboColours?.Count > 0 ? comboColours[combo.ComboIndex % comboColours.Count] : Color4.White;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called when a change is made to the skin.
|
/// Called when a change is made to the skin.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -630,7 +598,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The time at which state transforms should be applied that line up to <see cref="HitObject"/>'s StartTime.
|
/// The time at which state transforms should be applied that line up to <see cref="HitObject"/>'s StartTime.
|
||||||
/// This is used to offset calls to <see cref="UpdateStateTransforms"/>.
|
/// This is used to offset calls to <see cref="UpdateStartTimeStateTransforms"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public double StateUpdateTime => HitObject.StartTime;
|
public double StateUpdateTime => HitObject.StartTime;
|
||||||
|
|
||||||
|
@ -65,6 +65,10 @@ namespace osu.Game.Scoring.Legacy
|
|||||||
|
|
||||||
scoreInfo.Mods = currentRuleset.ConvertFromLegacyMods((LegacyMods)sr.ReadInt32()).ToArray();
|
scoreInfo.Mods = currentRuleset.ConvertFromLegacyMods((LegacyMods)sr.ReadInt32()).ToArray();
|
||||||
|
|
||||||
|
// lazer replays get a really high version number.
|
||||||
|
if (version < LegacyScoreEncoder.FIRST_LAZER_VERSION)
|
||||||
|
scoreInfo.Mods = scoreInfo.Mods.Append(currentRuleset.GetAllMods().OfType<ModClassic>().Single()).ToArray();
|
||||||
|
|
||||||
currentBeatmap = workingBeatmap.GetPlayableBeatmap(currentRuleset.RulesetInfo, scoreInfo.Mods);
|
currentBeatmap = workingBeatmap.GetPlayableBeatmap(currentRuleset.RulesetInfo, scoreInfo.Mods);
|
||||||
scoreInfo.Beatmap = currentBeatmap.BeatmapInfo;
|
scoreInfo.Beatmap = currentBeatmap.BeatmapInfo;
|
||||||
|
|
||||||
|
@ -15,7 +15,16 @@ namespace osu.Game.Scoring.Legacy
|
|||||||
{
|
{
|
||||||
public class LegacyScoreEncoder
|
public class LegacyScoreEncoder
|
||||||
{
|
{
|
||||||
public const int LATEST_VERSION = 128;
|
/// <summary>
|
||||||
|
/// Database version in stable-compatible YYYYMMDD format.
|
||||||
|
/// Should be incremented if any changes are made to the format/usage.
|
||||||
|
/// </summary>
|
||||||
|
public const int LATEST_VERSION = FIRST_LAZER_VERSION;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The first stable-compatible YYYYMMDD format version given to lazer usage of replays.
|
||||||
|
/// </summary>
|
||||||
|
public const int FIRST_LAZER_VERSION = 30000000;
|
||||||
|
|
||||||
private readonly Score score;
|
private readonly Score score;
|
||||||
private readonly IBeatmap beatmap;
|
private readonly IBeatmap beatmap;
|
||||||
|
@ -76,9 +76,6 @@ namespace osu.Game.Scoring
|
|||||||
else if (localAPIMods != null)
|
else if (localAPIMods != null)
|
||||||
scoreMods = apiMods.Select(m => m.ToMod(rulesetInstance)).ToArray();
|
scoreMods = apiMods.Select(m => m.ToMod(rulesetInstance)).ToArray();
|
||||||
|
|
||||||
if (IsLegacyScore)
|
|
||||||
scoreMods = scoreMods.Append(rulesetInstance.GetAllMods().OfType<ModClassic>().Single()).ToArray();
|
|
||||||
|
|
||||||
return scoreMods;
|
return scoreMods;
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
@ -201,33 +198,12 @@ namespace osu.Game.Scoring
|
|||||||
[JsonProperty("position")]
|
[JsonProperty("position")]
|
||||||
public int? Position { get; set; }
|
public int? Position { get; set; }
|
||||||
|
|
||||||
private bool isLegacyScore;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether this <see cref="ScoreInfo"/> represents a legacy (osu!stable) score.
|
/// Whether this <see cref="ScoreInfo"/> represents a legacy (osu!stable) score.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public bool IsLegacyScore
|
public bool IsLegacyScore => Mods.OfType<ModClassic>().Any();
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (isLegacyScore)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// The above check will catch legacy online scores that have an appropriate UserString + UserId.
|
|
||||||
// For non-online scores such as those imported in, a heuristic is used based on the following table:
|
|
||||||
//
|
|
||||||
// Mode | UserString | UserId
|
|
||||||
// --------------- | ---------- | ---------
|
|
||||||
// stable | <username> | 1
|
|
||||||
// lazer | <username> | <userid>
|
|
||||||
// lazer (offline) | Guest | 1
|
|
||||||
|
|
||||||
return ID > 0 && UserID == 1 && UserString != "Guest";
|
|
||||||
}
|
|
||||||
set => isLegacyScore = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<HitResultDisplayStatistic> GetStatisticsForDisplay()
|
public IEnumerable<HitResultDisplayStatistic> GetStatisticsForDisplay()
|
||||||
{
|
{
|
||||||
|
@ -5,8 +5,8 @@ using System.Threading;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Utils;
|
|
||||||
using osu.Framework.Threading;
|
using osu.Framework.Threading;
|
||||||
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Graphics.Backgrounds;
|
using osu.Game.Graphics.Backgrounds;
|
||||||
@ -53,14 +53,41 @@ namespace osu.Game.Screens.Backgrounds
|
|||||||
mode.ValueChanged += _ => Next();
|
mode.ValueChanged += _ => Next();
|
||||||
beatmap.ValueChanged += _ => Next();
|
beatmap.ValueChanged += _ => Next();
|
||||||
introSequence.ValueChanged += _ => Next();
|
introSequence.ValueChanged += _ => Next();
|
||||||
seasonalBackgroundLoader.SeasonalBackgroundChanged += Next;
|
seasonalBackgroundLoader.SeasonalBackgroundChanged += () => Next();
|
||||||
|
|
||||||
currentDisplay = RNG.Next(0, background_count);
|
currentDisplay = RNG.Next(0, background_count);
|
||||||
|
|
||||||
Next();
|
Next();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void display(Background newBackground)
|
private ScheduledDelegate nextTask;
|
||||||
|
private CancellationTokenSource cancellationTokenSource;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Request loading the next background.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Whether a new background was queued for load. May return false if the current background is still valid.</returns>
|
||||||
|
public bool Next()
|
||||||
|
{
|
||||||
|
var nextBackground = createBackground();
|
||||||
|
|
||||||
|
// in the case that the background hasn't changed, we want to avoid cancelling any tasks that could still be loading.
|
||||||
|
if (nextBackground == background)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
cancellationTokenSource?.Cancel();
|
||||||
|
cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
|
||||||
|
nextTask?.Cancel();
|
||||||
|
nextTask = Scheduler.AddDelayed(() =>
|
||||||
|
{
|
||||||
|
LoadComponentAsync(nextBackground, displayNext, cancellationTokenSource.Token);
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void displayNext(Background newBackground)
|
||||||
{
|
{
|
||||||
background?.FadeOut(800, Easing.InOutSine);
|
background?.FadeOut(800, Easing.InOutSine);
|
||||||
background?.Expire();
|
background?.Expire();
|
||||||
@ -69,82 +96,55 @@ namespace osu.Game.Screens.Backgrounds
|
|||||||
currentDisplay++;
|
currentDisplay++;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ScheduledDelegate nextTask;
|
|
||||||
private CancellationTokenSource cancellationTokenSource;
|
|
||||||
|
|
||||||
public void Next()
|
|
||||||
{
|
|
||||||
nextTask?.Cancel();
|
|
||||||
cancellationTokenSource?.Cancel();
|
|
||||||
cancellationTokenSource = new CancellationTokenSource();
|
|
||||||
nextTask = Scheduler.AddDelayed(() => LoadComponentAsync(createBackground(), display, cancellationTokenSource.Token), 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Background createBackground()
|
private Background createBackground()
|
||||||
{
|
{
|
||||||
Background newBackground;
|
// seasonal background loading gets highest priority.
|
||||||
string backgroundName;
|
Background newBackground = seasonalBackgroundLoader.LoadNextBackground();
|
||||||
|
|
||||||
var seasonalBackground = seasonalBackgroundLoader.LoadNextBackground();
|
if (newBackground == null && user.Value?.IsSupporter == true)
|
||||||
|
|
||||||
if (seasonalBackground != null)
|
|
||||||
{
|
|
||||||
seasonalBackground.Depth = currentDisplay;
|
|
||||||
return seasonalBackground;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (introSequence.Value)
|
|
||||||
{
|
|
||||||
case IntroSequence.Welcome:
|
|
||||||
backgroundName = "Intro/Welcome/menu-background";
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
backgroundName = $@"Menu/menu-background-{currentDisplay % background_count + 1}";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user.Value?.IsSupporter ?? false)
|
|
||||||
{
|
{
|
||||||
switch (mode.Value)
|
switch (mode.Value)
|
||||||
{
|
{
|
||||||
case BackgroundSource.Beatmap:
|
case BackgroundSource.Beatmap:
|
||||||
newBackground = new BeatmapBackground(beatmap.Value, backgroundName);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case BackgroundSource.BeatmapWithStoryboard:
|
case BackgroundSource.BeatmapWithStoryboard:
|
||||||
newBackground = AllowStoryboardBackground
|
{
|
||||||
? new BeatmapBackgroundWithStoryboard(beatmap.Value, backgroundName)
|
if (mode.Value == BackgroundSource.BeatmapWithStoryboard && AllowStoryboardBackground)
|
||||||
: new BeatmapBackground(beatmap.Value, backgroundName);
|
newBackground = new BeatmapBackgroundWithStoryboard(beatmap.Value, getBackgroundTextureName());
|
||||||
break;
|
newBackground ??= new BeatmapBackground(beatmap.Value, getBackgroundTextureName());
|
||||||
|
|
||||||
default:
|
break;
|
||||||
newBackground = new SkinnedBackground(skin.Value, backgroundName);
|
}
|
||||||
|
|
||||||
|
case BackgroundSource.Skin:
|
||||||
|
// default skins should use the default background rotation, which won't be the case if a SkinBackground is created for them.
|
||||||
|
if (skin.Value is DefaultSkin || skin.Value is DefaultLegacySkin)
|
||||||
|
break;
|
||||||
|
|
||||||
|
newBackground = new SkinBackground(skin.Value, getBackgroundTextureName());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
newBackground = new Background(backgroundName);
|
|
||||||
|
|
||||||
|
// this method is called in many cases where the background might not necessarily need to change.
|
||||||
|
// if an equivalent background is currently being shown, we don't want to load it again.
|
||||||
|
if (newBackground?.Equals(background) == true)
|
||||||
|
return background;
|
||||||
|
|
||||||
|
newBackground ??= new Background(getBackgroundTextureName());
|
||||||
newBackground.Depth = currentDisplay;
|
newBackground.Depth = currentDisplay;
|
||||||
|
|
||||||
return newBackground;
|
return newBackground;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SkinnedBackground : Background
|
private string getBackgroundTextureName()
|
||||||
{
|
{
|
||||||
private readonly Skin skin;
|
switch (introSequence.Value)
|
||||||
|
|
||||||
public SkinnedBackground(Skin skin, string fallbackTextureName)
|
|
||||||
: base(fallbackTextureName)
|
|
||||||
{
|
{
|
||||||
this.skin = skin;
|
case IntroSequence.Welcome:
|
||||||
}
|
return @"Intro/Welcome/menu-background";
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
default:
|
||||||
private void load()
|
return $@"Menu/menu-background-{currentDisplay % background_count + 1}";
|
||||||
{
|
|
||||||
Sprite.Texture = skin.GetTexture("menu-background") ?? Sprite.Texture;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,8 +67,11 @@ namespace osu.Game.Screens
|
|||||||
/// Invoked when the back button has been pressed to close any overlays before exiting this <see cref="IOsuScreen"/>.
|
/// Invoked when the back button has been pressed to close any overlays before exiting this <see cref="IOsuScreen"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
|
/// If this <see cref="IOsuScreen"/> has not yet finished loading, the exit will occur immediately without this method being invoked.
|
||||||
|
/// <para>
|
||||||
/// Return <c>true</c> to block this <see cref="IOsuScreen"/> from being exited after closing an overlay.
|
/// Return <c>true</c> to block this <see cref="IOsuScreen"/> from being exited after closing an overlay.
|
||||||
/// Return <c>false</c> if this <see cref="IOsuScreen"/> should continue exiting.
|
/// Return <c>false</c> if this <see cref="IOsuScreen"/> should continue exiting.
|
||||||
|
/// </para>
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
bool OnBackButton();
|
bool OnBackButton();
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
|||||||
// Isolates beatmap/ruleset to this screen.
|
// Isolates beatmap/ruleset to this screen.
|
||||||
public override bool DisallowExternalBeatmapRulesetChanges => true;
|
public override bool DisallowExternalBeatmapRulesetChanges => true;
|
||||||
|
|
||||||
|
// We are managing our own adjustments. For now, this happens inside the Player instances themselves.
|
||||||
|
public override bool AllowRateAdjustments => false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether all spectating players have finished loading.
|
/// Whether all spectating players have finished loading.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -253,7 +253,10 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
|
|
||||||
public override bool OnBackButton()
|
public override bool OnBackButton()
|
||||||
{
|
{
|
||||||
if ((screenStack.CurrentScreen as IOnlinePlaySubScreen)?.OnBackButton() == true)
|
if (!(screenStack.CurrentScreen is IOnlinePlaySubScreen onlineSubScreen))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (((Drawable)onlineSubScreen).IsLoaded && onlineSubScreen.AllowBackButton && onlineSubScreen.OnBackButton())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (screenStack.CurrentScreen != null && !(screenStack.CurrentScreen is LoungeSubScreen))
|
if (screenStack.CurrentScreen != null && !(screenStack.CurrentScreen is LoungeSubScreen))
|
||||||
|
@ -214,7 +214,10 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
|
|||||||
|
|
||||||
protected override void OnNewJudgement(JudgementResult judgement)
|
protected override void OnNewJudgement(JudgementResult judgement)
|
||||||
{
|
{
|
||||||
if (!judgement.IsHit)
|
if (!judgement.IsHit || judgement.HitObject.HitWindows?.WindowFor(HitResult.Miss) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!judgement.Type.IsScorable() || judgement.Type.IsBonus())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (judgementsContainer.Count > max_concurrent_judgements)
|
if (judgementsContainer.Count > max_concurrent_judgements)
|
||||||
@ -244,7 +247,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
|
|||||||
|
|
||||||
private float getRelativeJudgementPosition(double value) => Math.Clamp((float)((value / maxHitWindow) + 1) / 2, 0, 1);
|
private float getRelativeJudgementPosition(double value) => Math.Clamp((float)((value / maxHitWindow) + 1) / 2, 0, 1);
|
||||||
|
|
||||||
private class JudgementLine : CompositeDrawable
|
internal class JudgementLine : CompositeDrawable
|
||||||
{
|
{
|
||||||
private const int judgement_fade_duration = 5000;
|
private const int judgement_fade_duration = 5000;
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
@ -24,7 +25,13 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
|
|||||||
InternalChild = judgementsFlow = new JudgementFlow();
|
InternalChild = judgementsFlow = new JudgementFlow();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnNewJudgement(JudgementResult judgement) => judgementsFlow.Push(GetColourForHitResult(judgement.Type));
|
protected override void OnNewJudgement(JudgementResult judgement)
|
||||||
|
{
|
||||||
|
if (!judgement.Type.IsScorable() || judgement.Type.IsBonus())
|
||||||
|
return;
|
||||||
|
|
||||||
|
judgementsFlow.Push(GetColourForHitResult(judgement.Type));
|
||||||
|
}
|
||||||
|
|
||||||
private class JudgementFlow : FillFlowContainer<HitErrorCircle>
|
private class JudgementFlow : FillFlowContainer<HitErrorCircle>
|
||||||
{
|
{
|
||||||
@ -53,7 +60,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class HitErrorCircle : Container
|
internal class HitErrorCircle : Container
|
||||||
{
|
{
|
||||||
public bool IsRemoved { get; private set; }
|
public bool IsRemoved { get; private set; }
|
||||||
|
|
||||||
|
@ -32,15 +32,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
|
|||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
processor.NewJudgement += onNewJudgement;
|
processor.NewJudgement += OnNewJudgement;
|
||||||
}
|
|
||||||
|
|
||||||
private void onNewJudgement(JudgementResult result)
|
|
||||||
{
|
|
||||||
if (result.HitObject.HitWindows?.WindowFor(HitResult.Miss) == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
OnNewJudgement(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void OnNewJudgement(JudgementResult judgement);
|
protected abstract void OnNewJudgement(JudgementResult judgement);
|
||||||
@ -49,6 +41,8 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
|
|||||||
{
|
{
|
||||||
switch (result)
|
switch (result)
|
||||||
{
|
{
|
||||||
|
case HitResult.SmallTickMiss:
|
||||||
|
case HitResult.LargeTickMiss:
|
||||||
case HitResult.Miss:
|
case HitResult.Miss:
|
||||||
return colours.Red;
|
return colours.Red;
|
||||||
|
|
||||||
@ -61,6 +55,8 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
|
|||||||
case HitResult.Good:
|
case HitResult.Good:
|
||||||
return colours.GreenLight;
|
return colours.GreenLight;
|
||||||
|
|
||||||
|
case HitResult.SmallTickHit:
|
||||||
|
case HitResult.LargeTickHit:
|
||||||
case HitResult.Great:
|
case HitResult.Great:
|
||||||
return colours.Blue;
|
return colours.Blue;
|
||||||
|
|
||||||
@ -74,7 +70,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
|
|||||||
base.Dispose(isDisposing);
|
base.Dispose(isDisposing);
|
||||||
|
|
||||||
if (processor != null)
|
if (processor != null)
|
||||||
processor.NewJudgement -= onNewJudgement;
|
processor.NewJudgement -= OnNewJudgement;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,8 @@ namespace osu.Game.Skinning
|
|||||||
{
|
{
|
||||||
public class DefaultSkin : Skin
|
public class DefaultSkin : Skin
|
||||||
{
|
{
|
||||||
|
private readonly IStorageResourceProvider resources;
|
||||||
|
|
||||||
public DefaultSkin(IStorageResourceProvider resources)
|
public DefaultSkin(IStorageResourceProvider resources)
|
||||||
: this(SkinInfo.Default, resources)
|
: this(SkinInfo.Default, resources)
|
||||||
{
|
{
|
||||||
@ -31,12 +33,23 @@ namespace osu.Game.Skinning
|
|||||||
public DefaultSkin(SkinInfo skin, IStorageResourceProvider resources)
|
public DefaultSkin(SkinInfo skin, IStorageResourceProvider resources)
|
||||||
: base(skin, resources)
|
: base(skin, resources)
|
||||||
{
|
{
|
||||||
|
this.resources = resources;
|
||||||
Configuration = new DefaultSkinConfiguration();
|
Configuration = new DefaultSkinConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => null;
|
public override Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => null;
|
||||||
|
|
||||||
public override ISample GetSample(ISampleInfo sampleInfo) => null;
|
public override ISample GetSample(ISampleInfo sampleInfo)
|
||||||
|
{
|
||||||
|
foreach (var lookup in sampleInfo.LookupNames)
|
||||||
|
{
|
||||||
|
var sample = resources.AudioManager.Samples.Get(lookup);
|
||||||
|
if (sample != null)
|
||||||
|
return sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public override Drawable GetDrawableComponent(ISkinComponent component)
|
public override Drawable GetDrawableComponent(ISkinComponent component)
|
||||||
{
|
{
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
|
||||||
namespace osu.Game.Skinning
|
namespace osu.Game.Skinning
|
||||||
{
|
{
|
||||||
@ -11,5 +12,13 @@ namespace osu.Game.Skinning
|
|||||||
public interface ISkinSource : ISkin
|
public interface ISkinSource : ISkin
|
||||||
{
|
{
|
||||||
event Action SourceChanged;
|
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;
|
private const double epic_cutoff = 0.5;
|
||||||
|
|
||||||
[Resolved]
|
|
||||||
private ISkinSource skin { get; set; }
|
|
||||||
|
|
||||||
private LegacyHealthPiece fill;
|
private LegacyHealthPiece fill;
|
||||||
private LegacyHealthPiece marker;
|
private LegacyHealthPiece marker;
|
||||||
|
|
||||||
@ -31,10 +28,13 @@ namespace osu.Game.Skinning
|
|||||||
private bool isNewStyle;
|
private bool isNewStyle;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load(ISkinSource source)
|
||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.Both;
|
AutoSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
var 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;
|
isNewStyle = getTexture(skin, "marker") != null;
|
||||||
|
|
||||||
// background implementation is the same for both versions.
|
// 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);
|
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)
|
private static Color4 getFillColour(double hp)
|
||||||
{
|
{
|
||||||
@ -95,7 +95,7 @@ namespace osu.Game.Skinning
|
|||||||
private readonly Texture dangerTexture;
|
private readonly Texture dangerTexture;
|
||||||
private readonly Texture superDangerTexture;
|
private readonly Texture superDangerTexture;
|
||||||
|
|
||||||
public LegacyOldStyleMarker(ISkinSource skin)
|
public LegacyOldStyleMarker(ISkin skin)
|
||||||
{
|
{
|
||||||
normalTexture = getTexture(skin, "ki");
|
normalTexture = getTexture(skin, "ki");
|
||||||
dangerTexture = getTexture(skin, "kidanger");
|
dangerTexture = getTexture(skin, "kidanger");
|
||||||
@ -126,9 +126,9 @@ namespace osu.Game.Skinning
|
|||||||
|
|
||||||
public class LegacyNewStyleMarker : LegacyMarker
|
public class LegacyNewStyleMarker : LegacyMarker
|
||||||
{
|
{
|
||||||
private readonly ISkinSource skin;
|
private readonly ISkin skin;
|
||||||
|
|
||||||
public LegacyNewStyleMarker(ISkinSource skin)
|
public LegacyNewStyleMarker(ISkin skin)
|
||||||
{
|
{
|
||||||
this.skin = skin;
|
this.skin = skin;
|
||||||
}
|
}
|
||||||
@ -150,7 +150,7 @@ namespace osu.Game.Skinning
|
|||||||
|
|
||||||
internal class LegacyOldStyleFill : LegacyHealthPiece
|
internal class LegacyOldStyleFill : LegacyHealthPiece
|
||||||
{
|
{
|
||||||
public LegacyOldStyleFill(ISkinSource skin)
|
public LegacyOldStyleFill(ISkin skin)
|
||||||
{
|
{
|
||||||
// required for sizing correctly..
|
// required for sizing correctly..
|
||||||
var firstFrame = getTexture(skin, "colour-0");
|
var firstFrame = getTexture(skin, "colour-0");
|
||||||
@ -173,7 +173,7 @@ namespace osu.Game.Skinning
|
|||||||
|
|
||||||
internal class LegacyNewStyleFill : LegacyHealthPiece
|
internal class LegacyNewStyleFill : LegacyHealthPiece
|
||||||
{
|
{
|
||||||
public LegacyNewStyleFill(ISkinSource skin)
|
public LegacyNewStyleFill(ISkin skin)
|
||||||
{
|
{
|
||||||
InternalChild = new Sprite
|
InternalChild = new Sprite
|
||||||
{
|
{
|
||||||
|
@ -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)
|
: base(skin, resources)
|
||||||
{
|
{
|
||||||
using (var stream = storage?.GetStream(filename))
|
using (var stream = storage?.GetStream(configurationFilename))
|
||||||
{
|
{
|
||||||
if (stream != null)
|
if (stream != null)
|
||||||
{
|
{
|
||||||
@ -127,7 +134,7 @@ namespace osu.Game.Skinning
|
|||||||
|
|
||||||
case LegacyManiaSkinConfigurationLookup maniaLookup:
|
case LegacyManiaSkinConfigurationLookup maniaLookup:
|
||||||
if (!AllowManiaSkin)
|
if (!AllowManiaSkin)
|
||||||
return null;
|
break;
|
||||||
|
|
||||||
var result = lookupForMania<TValue>(maniaLookup);
|
var result = lookupForMania<TValue>(maniaLookup);
|
||||||
if (result != null)
|
if (result != null)
|
||||||
@ -390,6 +397,7 @@ namespace osu.Game.Skinning
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
case GameplaySkinComponent<HitResult> resultComponent:
|
case GameplaySkinComponent<HitResult> resultComponent:
|
||||||
|
// TODO: this should be inside the judgement pieces.
|
||||||
Func<Drawable> createDrawable = () => getJudgementAnimation(resultComponent.Component);
|
Func<Drawable> createDrawable = () => getJudgementAnimation(resultComponent.Component);
|
||||||
|
|
||||||
// kind of wasteful that we throw this away, but should do for now.
|
// 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);
|
var sample = Samples?.Get(lookup);
|
||||||
|
|
||||||
if (sample != null)
|
if (sample != null)
|
||||||
|
{
|
||||||
return sample;
|
return sample;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@ -518,8 +528,7 @@ namespace osu.Game.Skinning
|
|||||||
yield return componentName;
|
yield return componentName;
|
||||||
|
|
||||||
// Fall back to using the last piece for components coming from lazer (e.g. "Gameplay/osu/approachcircle" -> "approachcircle").
|
// 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.Split('/').Last();
|
||||||
yield return componentName.StartsWith("Gameplay/taiko/", StringComparison.Ordinal) ? "taiko-" + lastPiece : lastPiece;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
|
@ -27,6 +27,18 @@ namespace osu.Game.Skinning
|
|||||||
{
|
{
|
||||||
Texture texture;
|
Texture texture;
|
||||||
|
|
||||||
|
// find the first source which provides either the animated or non-animated version.
|
||||||
|
ISkin skin = (source as ISkinSource)?.FindProvider(s =>
|
||||||
|
{
|
||||||
|
if (animatable && s.GetTexture(getFrameName(0)) != null)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return s.GetTexture(componentName, wrapModeS, wrapModeT) != null;
|
||||||
|
}) ?? source;
|
||||||
|
|
||||||
|
if (skin == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
if (animatable)
|
if (animatable)
|
||||||
{
|
{
|
||||||
var textures = getTextures().ToArray();
|
var textures = getTextures().ToArray();
|
||||||
@ -35,7 +47,7 @@ namespace osu.Game.Skinning
|
|||||||
{
|
{
|
||||||
var animation = new SkinnableTextureAnimation(startAtCurrentTime)
|
var animation = new SkinnableTextureAnimation(startAtCurrentTime)
|
||||||
{
|
{
|
||||||
DefaultFrameLength = frameLength ?? getFrameLength(source, applyConfigFrameRate, textures),
|
DefaultFrameLength = frameLength ?? getFrameLength(skin, applyConfigFrameRate, textures),
|
||||||
Loop = looping,
|
Loop = looping,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -47,7 +59,7 @@ namespace osu.Game.Skinning
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if an animation was not allowed or not found, fall back to a sprite retrieval.
|
// if an animation was not allowed or not found, fall back to a sprite retrieval.
|
||||||
if ((texture = source.GetTexture(componentName, wrapModeS, wrapModeT)) != null)
|
if ((texture = skin.GetTexture(componentName, wrapModeS, wrapModeT)) != null)
|
||||||
return new Sprite { Texture = texture };
|
return new Sprite { Texture = texture };
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@ -56,12 +68,14 @@ namespace osu.Game.Skinning
|
|||||||
{
|
{
|
||||||
for (int i = 0; true; i++)
|
for (int i = 0; true; i++)
|
||||||
{
|
{
|
||||||
if ((texture = source.GetTexture($"{componentName}{animationSeparator}{i}", wrapModeS, wrapModeT)) == null)
|
if ((texture = skin.GetTexture(getFrameName(i), wrapModeS, wrapModeT)) == null)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
yield return texture;
|
yield return texture;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string getFrameName(int frameIndex) => $"{componentName}{animationSeparator}{frameIndex}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool HasFont(this ISkin source, LegacyFont font)
|
public static bool HasFont(this ISkin source, LegacyFont font)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using osu.Framework.Audio.Sample;
|
using osu.Framework.Audio.Sample;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -15,7 +16,7 @@ namespace osu.Game.Skinning
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Transformer used to handle support of legacy features for individual rulesets.
|
/// Transformer used to handle support of legacy features for individual rulesets.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class LegacySkinTransformer : ISkin
|
public abstract class LegacySkinTransformer : ISkinSource
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Source of the <see cref="ISkin"/> which is being transformed.
|
/// Source of the <see cref="ISkin"/> which is being transformed.
|
||||||
@ -47,5 +48,13 @@ namespace osu.Game.Skinning
|
|||||||
}
|
}
|
||||||
|
|
||||||
public abstract IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup);
|
public abstract IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup);
|
||||||
|
|
||||||
|
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => Source.FindProvider(lookupFunction);
|
||||||
|
|
||||||
|
public event Action SourceChanged
|
||||||
|
{
|
||||||
|
add { throw new NotSupportedException(); }
|
||||||
|
remove { }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
@ -70,33 +71,50 @@ namespace osu.Game.Skinning
|
|||||||
updateSample();
|
updateSample();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.SkinChanged(skin, allowFallback);
|
base.LoadComplete();
|
||||||
|
|
||||||
|
CurrentSkin.SourceChanged += skinChangedImmediate;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void skinChangedImmediate()
|
||||||
|
{
|
||||||
|
// Clean up the previous sample immediately on a source change.
|
||||||
|
// This avoids a potential call to Play() of an already disposed sample (samples are disposed along with the skin, but SkinChanged is scheduled).
|
||||||
|
clearPreviousSamples();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void SkinChanged(ISkinSource skin)
|
||||||
|
{
|
||||||
|
base.SkinChanged(skin);
|
||||||
updateSample();
|
updateSample();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether this sample was playing before a skin source change.
|
||||||
|
/// </summary>
|
||||||
|
private bool wasPlaying;
|
||||||
|
|
||||||
|
private void clearPreviousSamples()
|
||||||
|
{
|
||||||
|
// only run if the samples aren't already cleared.
|
||||||
|
// this ensures the "wasPlaying" state is stored correctly even if multiple clear calls are executed.
|
||||||
|
if (!sampleContainer.Any()) return;
|
||||||
|
|
||||||
|
wasPlaying = Playing;
|
||||||
|
|
||||||
|
sampleContainer.Clear();
|
||||||
|
Sample = null;
|
||||||
|
}
|
||||||
|
|
||||||
private void updateSample()
|
private void updateSample()
|
||||||
{
|
{
|
||||||
if (sampleInfo == null)
|
if (sampleInfo == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
bool wasPlaying = Playing;
|
|
||||||
|
|
||||||
sampleContainer.Clear();
|
|
||||||
Sample = null;
|
|
||||||
|
|
||||||
var sample = CurrentSkin.GetSample(sampleInfo);
|
var sample = CurrentSkin.GetSample(sampleInfo);
|
||||||
|
|
||||||
if (sample == null && AllowDefaultFallback)
|
|
||||||
{
|
|
||||||
foreach (var lookup in sampleInfo.LookupNames)
|
|
||||||
{
|
|
||||||
if ((sample = sampleStore.Get(lookup)) != null)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sample == null)
|
if (sample == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -155,6 +173,14 @@ namespace osu.Game.Skinning
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
base.Dispose(isDisposing);
|
||||||
|
|
||||||
|
if (CurrentSkin != null)
|
||||||
|
CurrentSkin.SourceChanged -= skinChangedImmediate;
|
||||||
|
}
|
||||||
|
|
||||||
#region Re-expose AudioContainer
|
#region Re-expose AudioContainer
|
||||||
|
|
||||||
public BindableNumber<double> Volume => sampleContainer.Volume;
|
public BindableNumber<double> Volume => sampleContainer.Volume;
|
||||||
|
@ -39,7 +39,7 @@ namespace osu.Game.Skinning
|
|||||||
|
|
||||||
private readonly IResourceStore<byte[]> resources;
|
private readonly IResourceStore<byte[]> resources;
|
||||||
|
|
||||||
public readonly Bindable<Skin> CurrentSkin = new Bindable<Skin>(new DefaultSkin(null));
|
public readonly Bindable<Skin> CurrentSkin = new Bindable<Skin>();
|
||||||
public readonly Bindable<SkinInfo> CurrentSkinInfo = new Bindable<SkinInfo>(SkinInfo.Default) { Default = SkinInfo.Default };
|
public readonly Bindable<SkinInfo> CurrentSkinInfo = new Bindable<SkinInfo>(SkinInfo.Default) { Default = SkinInfo.Default };
|
||||||
|
|
||||||
public override IEnumerable<string> HandledExtensions => new[] { ".osk" };
|
public override IEnumerable<string> HandledExtensions => new[] { ".osk" };
|
||||||
@ -48,6 +48,8 @@ namespace osu.Game.Skinning
|
|||||||
|
|
||||||
protected override string ImportFromStablePath => "Skins";
|
protected override string ImportFromStablePath => "Skins";
|
||||||
|
|
||||||
|
private readonly Skin defaultLegacySkin;
|
||||||
|
|
||||||
public SkinManager(Storage storage, DatabaseContextFactory contextFactory, GameHost host, IResourceStore<byte[]> resources, AudioManager audio)
|
public SkinManager(Storage storage, DatabaseContextFactory contextFactory, GameHost host, IResourceStore<byte[]> resources, AudioManager audio)
|
||||||
: base(storage, contextFactory, new SkinStore(contextFactory, storage), host)
|
: base(storage, contextFactory, new SkinStore(contextFactory, storage), host)
|
||||||
{
|
{
|
||||||
@ -55,7 +57,11 @@ namespace osu.Game.Skinning
|
|||||||
this.host = host;
|
this.host = host;
|
||||||
this.resources = resources;
|
this.resources = resources;
|
||||||
|
|
||||||
|
defaultLegacySkin = new DefaultLegacySkin(this);
|
||||||
|
|
||||||
CurrentSkinInfo.ValueChanged += skin => CurrentSkin.Value = GetSkin(skin.NewValue);
|
CurrentSkinInfo.ValueChanged += skin => CurrentSkin.Value = GetSkin(skin.NewValue);
|
||||||
|
|
||||||
|
CurrentSkin.Value = new DefaultSkin(this);
|
||||||
CurrentSkin.ValueChanged += skin =>
|
CurrentSkin.ValueChanged += skin =>
|
||||||
{
|
{
|
||||||
if (skin.NewValue.SkinInfo != CurrentSkinInfo.Value)
|
if (skin.NewValue.SkinInfo != CurrentSkinInfo.Value)
|
||||||
@ -204,13 +210,41 @@ namespace osu.Game.Skinning
|
|||||||
|
|
||||||
public event Action SourceChanged;
|
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
|
#region IResourceStorageProvider
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio.Sample;
|
using osu.Framework.Audio.Sample;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
@ -20,8 +21,10 @@ namespace osu.Game.Skinning
|
|||||||
{
|
{
|
||||||
public event Action SourceChanged;
|
public event Action SourceChanged;
|
||||||
|
|
||||||
|
[CanBeNull]
|
||||||
private readonly ISkin skin;
|
private readonly ISkin skin;
|
||||||
|
|
||||||
|
[CanBeNull]
|
||||||
private ISkinSource fallbackSource;
|
private ISkinSource fallbackSource;
|
||||||
|
|
||||||
protected virtual bool AllowDrawableLookup(ISkinComponent component) => true;
|
protected virtual bool AllowDrawableLookup(ISkinComponent component) => true;
|
||||||
@ -41,6 +44,22 @@ namespace osu.Game.Skinning
|
|||||||
RelativeSizeAxes = Axes.Both;
|
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)
|
public Drawable GetDrawableComponent(ISkinComponent component)
|
||||||
{
|
{
|
||||||
Drawable sourceDrawable;
|
Drawable sourceDrawable;
|
||||||
@ -85,7 +104,7 @@ namespace osu.Game.Skinning
|
|||||||
{
|
{
|
||||||
if (canUseSkinLookup)
|
if (canUseSkinLookup)
|
||||||
{
|
{
|
||||||
var bindable = skin.GetConfig<TLookup, TValue>(lookup);
|
var bindable = skin?.GetConfig<TLookup, TValue>(lookup);
|
||||||
if (bindable != null)
|
if (bindable != null)
|
||||||
return bindable;
|
return bindable;
|
||||||
}
|
}
|
||||||
|
@ -22,22 +22,6 @@ namespace osu.Game.Skinning
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected ISkinSource CurrentSkin { get; private set; }
|
protected ISkinSource CurrentSkin { get; private set; }
|
||||||
|
|
||||||
private readonly Func<ISkinSource, bool> allowFallback;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether fallback to default skin should be allowed if the custom skin is missing this resource.
|
|
||||||
/// </summary>
|
|
||||||
protected bool AllowDefaultFallback => allowFallback == null || allowFallback.Invoke(CurrentSkin);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a new <see cref="SkinReloadableDrawable"/>
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="allowFallback">A conditional to decide whether to allow fallback to the default implementation if a skinned element is not present.</param>
|
|
||||||
protected SkinReloadableDrawable(Func<ISkinSource, bool> allowFallback = null)
|
|
||||||
{
|
|
||||||
this.allowFallback = allowFallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(ISkinSource source)
|
private void load(ISkinSource source)
|
||||||
{
|
{
|
||||||
@ -58,7 +42,7 @@ namespace osu.Game.Skinning
|
|||||||
|
|
||||||
private void skinChanged()
|
private void skinChanged()
|
||||||
{
|
{
|
||||||
SkinChanged(CurrentSkin, AllowDefaultFallback);
|
SkinChanged(CurrentSkin);
|
||||||
OnSkinChanged?.Invoke();
|
OnSkinChanged?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,8 +50,7 @@ namespace osu.Game.Skinning
|
|||||||
/// Called when a change is made to the skin.
|
/// Called when a change is made to the skin.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="skin">The new skin.</param>
|
/// <param name="skin">The new skin.</param>
|
||||||
/// <param name="allowFallback">Whether fallback to default skin should be allowed if the custom skin is missing this resource.</param>
|
protected virtual void SkinChanged(ISkinSource skin)
|
||||||
protected virtual void SkinChanged(ISkinSource skin, bool allowFallback)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,17 +40,14 @@ namespace osu.Game.Skinning
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="component">The namespace-complete resource name for this skinnable element.</param>
|
/// <param name="component">The namespace-complete resource name for this skinnable element.</param>
|
||||||
/// <param name="defaultImplementation">A function to create the default skin implementation of this element.</param>
|
/// <param name="defaultImplementation">A function to create the default skin implementation of this element.</param>
|
||||||
/// <param name="allowFallback">A conditional to decide whether to allow fallback to the default implementation if a skinned element is not present.</param>
|
|
||||||
/// <param name="confineMode">How (if at all) the <see cref="Drawable"/> should be resize to fit within our own bounds.</param>
|
/// <param name="confineMode">How (if at all) the <see cref="Drawable"/> should be resize to fit within our own bounds.</param>
|
||||||
public SkinnableDrawable(ISkinComponent component, Func<ISkinComponent, Drawable> defaultImplementation = null, Func<ISkinSource, bool> allowFallback = null,
|
public SkinnableDrawable(ISkinComponent component, Func<ISkinComponent, Drawable> defaultImplementation = null, ConfineMode confineMode = ConfineMode.NoScaling)
|
||||||
ConfineMode confineMode = ConfineMode.NoScaling)
|
: this(component, confineMode)
|
||||||
: this(component, allowFallback, confineMode)
|
|
||||||
{
|
{
|
||||||
createDefault = defaultImplementation;
|
createDefault = defaultImplementation;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected SkinnableDrawable(ISkinComponent component, Func<ISkinSource, bool> allowFallback = null, ConfineMode confineMode = ConfineMode.NoScaling)
|
protected SkinnableDrawable(ISkinComponent component, ConfineMode confineMode = ConfineMode.NoScaling)
|
||||||
: base(allowFallback)
|
|
||||||
{
|
{
|
||||||
this.component = component;
|
this.component = component;
|
||||||
this.confineMode = confineMode;
|
this.confineMode = confineMode;
|
||||||
@ -76,13 +73,13 @@ namespace osu.Game.Skinning
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual bool ApplySizeRestrictionsToDefault => false;
|
protected virtual bool ApplySizeRestrictionsToDefault => false;
|
||||||
|
|
||||||
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
protected override void SkinChanged(ISkinSource skin)
|
||||||
{
|
{
|
||||||
Drawable = skin.GetDrawableComponent(component);
|
Drawable = skin.GetDrawableComponent(component);
|
||||||
|
|
||||||
isDefault = false;
|
isDefault = false;
|
||||||
|
|
||||||
if (Drawable == null && allowFallback)
|
if (Drawable == null)
|
||||||
{
|
{
|
||||||
Drawable = CreateDefault(component);
|
Drawable = CreateDefault(component);
|
||||||
isDefault = true;
|
isDefault = true;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
@ -19,8 +18,8 @@ namespace osu.Game.Skinning
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private TextureStore textures { get; set; }
|
private TextureStore textures { get; set; }
|
||||||
|
|
||||||
public SkinnableSprite(string textureName, Func<ISkinSource, bool> allowFallback = null, ConfineMode confineMode = ConfineMode.NoScaling)
|
public SkinnableSprite(string textureName, ConfineMode confineMode = ConfineMode.NoScaling)
|
||||||
: base(new SpriteComponent(textureName), allowFallback, confineMode)
|
: base(new SpriteComponent(textureName), confineMode)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,14 +9,14 @@ namespace osu.Game.Skinning
|
|||||||
{
|
{
|
||||||
public class SkinnableSpriteText : SkinnableDrawable, IHasText
|
public class SkinnableSpriteText : SkinnableDrawable, IHasText
|
||||||
{
|
{
|
||||||
public SkinnableSpriteText(ISkinComponent component, Func<ISkinComponent, SpriteText> defaultImplementation, Func<ISkinSource, bool> allowFallback = null, ConfineMode confineMode = ConfineMode.NoScaling)
|
public SkinnableSpriteText(ISkinComponent component, Func<ISkinComponent, SpriteText> defaultImplementation, ConfineMode confineMode = ConfineMode.NoScaling)
|
||||||
: base(component, defaultImplementation, allowFallback, confineMode)
|
: base(component, defaultImplementation, confineMode)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
protected override void SkinChanged(ISkinSource skin)
|
||||||
{
|
{
|
||||||
base.SkinChanged(skin, allowFallback);
|
base.SkinChanged(skin);
|
||||||
|
|
||||||
if (Drawable is IHasText textDrawable)
|
if (Drawable is IHasText textDrawable)
|
||||||
textDrawable.Text = Text;
|
textDrawable.Text = Text;
|
||||||
|
@ -73,9 +73,9 @@ namespace osu.Game.Skinning
|
|||||||
components.Remove(component);
|
components.Remove(component);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
protected override void SkinChanged(ISkinSource skin)
|
||||||
{
|
{
|
||||||
base.SkinChanged(skin, allowFallback);
|
base.SkinChanged(skin);
|
||||||
|
|
||||||
Reload();
|
Reload();
|
||||||
}
|
}
|
||||||
|
@ -31,9 +31,9 @@ namespace osu.Game.Storyboards.Drawables
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private IBindable<IReadOnlyList<Mod>> mods { get; set; }
|
private IBindable<IReadOnlyList<Mod>> mods { get; set; }
|
||||||
|
|
||||||
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
protected override void SkinChanged(ISkinSource skin)
|
||||||
{
|
{
|
||||||
base.SkinChanged(skin, allowFallback);
|
base.SkinChanged(skin);
|
||||||
|
|
||||||
foreach (var mod in mods.Value.OfType<IApplicableToSample>())
|
foreach (var mod in mods.Value.OfType<IApplicableToSample>())
|
||||||
{
|
{
|
||||||
|
@ -158,6 +158,8 @@ namespace osu.Game.Tests.Beatmaps
|
|||||||
add { }
|
add { }
|
||||||
remove { }
|
remove { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2021.607.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2021.608.0" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.525.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.525.0" />
|
||||||
<PackageReference Include="Sentry" Version="3.4.0" />
|
<PackageReference Include="Sentry" Version="3.4.0" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.28.2" />
|
<PackageReference Include="SharpCompress" Version="0.28.2" />
|
||||||
|
@ -70,7 +70,7 @@
|
|||||||
<Reference Include="System.Net.Http" />
|
<Reference Include="System.Net.Http" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2021.607.0" />
|
<PackageReference Include="ppy.osu.Framework.iOS" Version="2021.608.0" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.525.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.525.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net5.0 / net6.0) -->
|
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net5.0 / net6.0) -->
|
||||||
@ -93,7 +93,7 @@
|
|||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2021.607.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2021.608.0" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.28.2" />
|
<PackageReference Include="SharpCompress" Version="0.28.2" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
||||||
|
Reference in New Issue
Block a user