mirror of
https://github.com/ppy/osu.git
synced 2025-01-19 17:03:02 +08:00
Merge branch 'master' into osu-target-mod
This commit is contained in:
commit
50e316fca4
@ -157,7 +157,7 @@ csharp_style_unused_value_assignment_preference = discard_variable:warning
|
|||||||
|
|
||||||
#Style - variable declaration
|
#Style - variable declaration
|
||||||
csharp_style_inlined_variable_declaration = true:warning
|
csharp_style_inlined_variable_declaration = true:warning
|
||||||
csharp_style_deconstructed_variable_declaration = true:warning
|
csharp_style_deconstructed_variable_declaration = false:silent
|
||||||
|
|
||||||
#Style - other C# 7.x features
|
#Style - other C# 7.x features
|
||||||
dotnet_style_prefer_inferred_tuple_names = true:warning
|
dotnet_style_prefer_inferred_tuple_names = true:warning
|
||||||
@ -168,8 +168,8 @@ dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent
|
|||||||
#Style - C# 8 features
|
#Style - C# 8 features
|
||||||
csharp_prefer_static_local_function = true:warning
|
csharp_prefer_static_local_function = true:warning
|
||||||
csharp_prefer_simple_using_statement = true:silent
|
csharp_prefer_simple_using_statement = true:silent
|
||||||
csharp_style_prefer_index_operator = true:warning
|
csharp_style_prefer_index_operator = false:silent
|
||||||
csharp_style_prefer_range_operator = true:warning
|
csharp_style_prefer_range_operator = false:silent
|
||||||
csharp_style_prefer_switch_expression = false:none
|
csharp_style_prefer_switch_expression = false:none
|
||||||
|
|
||||||
#Supressing roslyn built-in analyzers
|
#Supressing roslyn built-in analyzers
|
||||||
|
@ -5,8 +5,8 @@
|
|||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<Description>A free-to-win rhythm game. Rhythm is just a *click* away!</Description>
|
<Description>A free-to-win rhythm game. Rhythm is just a *click* away!</Description>
|
||||||
<AssemblyName>osu!</AssemblyName>
|
<AssemblyName>osu!</AssemblyName>
|
||||||
<Title>osu!lazer</Title>
|
<Title>osu!</Title>
|
||||||
<Product>osu!lazer</Product>
|
<Product>osu!</Product>
|
||||||
<ApplicationIcon>lazer.ico</ApplicationIcon>
|
<ApplicationIcon>lazer.ico</ApplicationIcon>
|
||||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||||
<Version>0.0.0</Version>
|
<Version>0.0.0</Version>
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<metadata>
|
<metadata>
|
||||||
<id>osulazer</id>
|
<id>osulazer</id>
|
||||||
<version>0.0.0</version>
|
<version>0.0.0</version>
|
||||||
<title>osu!lazer</title>
|
<title>osu!</title>
|
||||||
<authors>ppy Pty Ltd</authors>
|
<authors>ppy Pty Ltd</authors>
|
||||||
<owners>Dean Herbert</owners>
|
<owners>Dean Herbert</owners>
|
||||||
<projectUrl>https://osu.ppy.sh/</projectUrl>
|
<projectUrl>https://osu.ppy.sh/</projectUrl>
|
||||||
@ -20,4 +20,3 @@
|
|||||||
<file src="**.config" target="lib\net45\"/>
|
<file src="**.config" target="lib\net45\"/>
|
||||||
</files>
|
</files>
|
||||||
</package>
|
</package>
|
||||||
|
|
||||||
|
114
osu.Game.Rulesets.Catch.Tests/TestSceneCatchSkinConfiguration.cs
Normal file
114
osu.Game.Rulesets.Catch.Tests/TestSceneCatchSkinConfiguration.cs
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
// 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.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Rulesets.Catch.Judgements;
|
||||||
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
|
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Catch.Skinning;
|
||||||
|
using osu.Game.Rulesets.Catch.UI;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
using Direction = osu.Game.Rulesets.Catch.UI.Direction;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Catch.Tests
|
||||||
|
{
|
||||||
|
public class TestSceneCatchSkinConfiguration : OsuTestScene
|
||||||
|
{
|
||||||
|
[Cached]
|
||||||
|
private readonly DroppedObjectContainer droppedObjectContainer;
|
||||||
|
|
||||||
|
private Catcher catcher;
|
||||||
|
|
||||||
|
private readonly Container container;
|
||||||
|
|
||||||
|
public TestSceneCatchSkinConfiguration()
|
||||||
|
{
|
||||||
|
Add(droppedObjectContainer = new DroppedObjectContainer());
|
||||||
|
Add(container = new Container { RelativeSizeAxes = Axes.Both });
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase(false)]
|
||||||
|
[TestCase(true)]
|
||||||
|
public void TestCatcherPlateFlipping(bool flip)
|
||||||
|
{
|
||||||
|
AddStep("setup catcher", () =>
|
||||||
|
{
|
||||||
|
var skin = new TestSkin { FlipCatcherPlate = flip };
|
||||||
|
container.Child = new SkinProvidingContainer(skin)
|
||||||
|
{
|
||||||
|
Child = catcher = new Catcher(new Container())
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
Fruit fruit = new Fruit();
|
||||||
|
|
||||||
|
AddStep("catch fruit", () => catchFruit(fruit, 20));
|
||||||
|
|
||||||
|
float position = 0;
|
||||||
|
|
||||||
|
AddStep("record fruit position", () => position = getCaughtObjectPosition(fruit));
|
||||||
|
|
||||||
|
AddStep("face left", () => catcher.VisualDirection = Direction.Left);
|
||||||
|
|
||||||
|
if (flip)
|
||||||
|
AddAssert("fruit position changed", () => !Precision.AlmostEquals(getCaughtObjectPosition(fruit), position));
|
||||||
|
else
|
||||||
|
AddAssert("fruit position unchanged", () => Precision.AlmostEquals(getCaughtObjectPosition(fruit), position));
|
||||||
|
|
||||||
|
AddStep("face right", () => catcher.VisualDirection = Direction.Right);
|
||||||
|
|
||||||
|
AddAssert("fruit position restored", () => Precision.AlmostEquals(getCaughtObjectPosition(fruit), position));
|
||||||
|
}
|
||||||
|
|
||||||
|
private float getCaughtObjectPosition(Fruit fruit)
|
||||||
|
{
|
||||||
|
var caughtObject = catcher.ChildrenOfType<CaughtObject>().Single(c => c.HitObject == fruit);
|
||||||
|
return caughtObject.Parent.ToSpaceOfOtherDrawable(caughtObject.Position, catcher).X;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void catchFruit(Fruit fruit, float x)
|
||||||
|
{
|
||||||
|
fruit.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||||
|
var drawableFruit = new DrawableFruit(fruit) { X = x };
|
||||||
|
var judgement = fruit.CreateJudgement();
|
||||||
|
catcher.OnNewResult(drawableFruit, new CatchJudgementResult(fruit, judgement)
|
||||||
|
{
|
||||||
|
Type = judgement.MaxResult
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestSkin : DefaultSkin
|
||||||
|
{
|
||||||
|
public bool FlipCatcherPlate { get; set; }
|
||||||
|
|
||||||
|
public TestSkin()
|
||||||
|
: base(null)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
|
||||||
|
{
|
||||||
|
if (lookup is CatchSkinConfiguration config)
|
||||||
|
{
|
||||||
|
if (config == CatchSkinConfiguration.FlipCatcherPlate)
|
||||||
|
return SkinUtils.As<TValue>(new Bindable<bool>(FlipCatcherPlate));
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.GetConfig<TLookup, TValue>(lookup);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -194,9 +194,9 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
AddStep("catch more fruits", () => attemptCatch(() => new Fruit(), 9));
|
AddStep("catch more fruits", () => attemptCatch(() => new Fruit(), 9));
|
||||||
checkPlate(10);
|
checkPlate(10);
|
||||||
AddAssert("caught objects are stacked", () =>
|
AddAssert("caught objects are stacked", () =>
|
||||||
catcher.CaughtObjects.All(obj => obj.Y <= Catcher.CAUGHT_FRUIT_VERTICAL_OFFSET) &&
|
catcher.CaughtObjects.All(obj => obj.Y <= 0) &&
|
||||||
catcher.CaughtObjects.Any(obj => obj.Y == Catcher.CAUGHT_FRUIT_VERTICAL_OFFSET) &&
|
catcher.CaughtObjects.Any(obj => obj.Y == 0) &&
|
||||||
catcher.CaughtObjects.Any(obj => obj.Y < -25));
|
catcher.CaughtObjects.Any(obj => obj.Y < 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
13
osu.Game.Rulesets.Catch/Skinning/CatchSkinConfiguration.cs
Normal file
13
osu.Game.Rulesets.Catch/Skinning/CatchSkinConfiguration.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Catch.Skinning
|
||||||
|
{
|
||||||
|
public enum CatchSkinConfiguration
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the contents of the catcher plate should be visually flipped when the catcher direction is changed.
|
||||||
|
/// </summary>
|
||||||
|
FlipCatcherPlate
|
||||||
|
}
|
||||||
|
}
|
@ -103,6 +103,19 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
|||||||
|
|
||||||
result.Value = LegacyColourCompatibility.DisallowZeroAlpha(result.Value);
|
result.Value = LegacyColourCompatibility.DisallowZeroAlpha(result.Value);
|
||||||
return (IBindable<TValue>)result;
|
return (IBindable<TValue>)result;
|
||||||
|
|
||||||
|
case CatchSkinConfiguration config:
|
||||||
|
switch (config)
|
||||||
|
{
|
||||||
|
case CatchSkinConfiguration.FlipCatcherPlate:
|
||||||
|
// Don't flip catcher plate contents if the catcher is provided by this legacy skin.
|
||||||
|
if (GetDrawableComponent(new CatchSkinComponent(CatchSkinComponents.Catcher)) != null)
|
||||||
|
return (IBindable<TValue>)new Bindable<bool>();
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return base.GetConfig<TLookup, TValue>(lookup);
|
return base.GetConfig<TLookup, TValue>(lookup);
|
||||||
|
@ -56,11 +56,6 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public double Speed => (Dashing ? 1 : 0.5) * BASE_SPEED * hyperDashModifier;
|
public double Speed => (Dashing ? 1 : 0.5) * BASE_SPEED * hyperDashModifier;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The amount by which caught fruit should be offset from the plate surface to make them look visually "caught".
|
|
||||||
/// </summary>
|
|
||||||
public const float CAUGHT_FRUIT_VERTICAL_OFFSET = -5;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The amount by which caught fruit should be scaled down to fit on the plate.
|
/// The amount by which caught fruit should be scaled down to fit on the plate.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -84,8 +79,8 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
public CatcherAnimationState CurrentState
|
public CatcherAnimationState CurrentState
|
||||||
{
|
{
|
||||||
get => body.AnimationState.Value;
|
get => Body.AnimationState.Value;
|
||||||
private set => body.AnimationState.Value = value;
|
private set => Body.AnimationState.Value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -108,18 +103,22 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Direction VisualDirection
|
/// <summary>
|
||||||
{
|
/// The currently facing direction.
|
||||||
get => Scale.X > 0 ? Direction.Right : Direction.Left;
|
/// </summary>
|
||||||
set => Scale = new Vector2((value == Direction.Right ? 1 : -1) * Math.Abs(Scale.X), Scale.Y);
|
public Direction VisualDirection { get; set; } = Direction.Right;
|
||||||
}
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the contents of the catcher plate should be visually flipped when the catcher direction is changed.
|
||||||
|
/// </summary>
|
||||||
|
private bool flipCatcherPlate;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Width of the area that can be used to attempt catches during gameplay.
|
/// Width of the area that can be used to attempt catches during gameplay.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly float catchWidth;
|
private readonly float catchWidth;
|
||||||
|
|
||||||
private readonly SkinnableCatcher body;
|
internal readonly SkinnableCatcher Body;
|
||||||
|
|
||||||
private Color4 hyperDashColour = DEFAULT_HYPER_DASH_COLOUR;
|
private Color4 hyperDashColour = DEFAULT_HYPER_DASH_COLOUR;
|
||||||
private Color4 hyperDashEndGlowColour = DEFAULT_HYPER_DASH_COLOUR;
|
private Color4 hyperDashEndGlowColour = DEFAULT_HYPER_DASH_COLOUR;
|
||||||
@ -157,8 +156,10 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
{
|
{
|
||||||
Anchor = Anchor.TopCentre,
|
Anchor = Anchor.TopCentre,
|
||||||
Origin = Anchor.BottomCentre,
|
Origin = Anchor.BottomCentre,
|
||||||
|
// offset fruit vertically to better place "above" the plate.
|
||||||
|
Y = -5
|
||||||
},
|
},
|
||||||
body = new SkinnableCatcher(),
|
Body = new SkinnableCatcher(),
|
||||||
hitExplosionContainer = new HitExplosionContainer
|
hitExplosionContainer = new HitExplosionContainer
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopCentre,
|
Anchor = Anchor.TopCentre,
|
||||||
@ -347,6 +348,8 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
trails.HyperDashTrailsColour = hyperDashColour;
|
trails.HyperDashTrailsColour = hyperDashColour;
|
||||||
trails.EndGlowSpritesColour = hyperDashEndGlowColour;
|
trails.EndGlowSpritesColour = hyperDashEndGlowColour;
|
||||||
|
|
||||||
|
flipCatcherPlate = skin.GetConfig<CatchSkinConfiguration, bool>(CatchSkinConfiguration.FlipCatcherPlate)?.Value ?? true;
|
||||||
|
|
||||||
runHyperDashStateTransition(HyperDashing);
|
runHyperDashStateTransition(HyperDashing);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -354,6 +357,10 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
|
var scaleFromDirection = new Vector2((int)VisualDirection, 1);
|
||||||
|
Body.Scale = scaleFromDirection;
|
||||||
|
caughtObjectContainer.Scale = hitExplosionContainer.Scale = flipCatcherPlate ? scaleFromDirection : Vector2.One;
|
||||||
|
|
||||||
// Correct overshooting.
|
// Correct overshooting.
|
||||||
if ((hyperDashDirection > 0 && hyperDashTargetPosition < X) ||
|
if ((hyperDashDirection > 0 && hyperDashTargetPosition < X) ||
|
||||||
(hyperDashDirection < 0 && hyperDashTargetPosition > X))
|
(hyperDashDirection < 0 && hyperDashTargetPosition > X))
|
||||||
@ -388,9 +395,6 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
float adjustedRadius = displayRadius * lenience_adjust;
|
float adjustedRadius = displayRadius * lenience_adjust;
|
||||||
float checkDistance = MathF.Pow(adjustedRadius, 2);
|
float checkDistance = MathF.Pow(adjustedRadius, 2);
|
||||||
|
|
||||||
// offset fruit vertically to better place "above" the plate.
|
|
||||||
position.Y += CAUGHT_FRUIT_VERTICAL_OFFSET;
|
|
||||||
|
|
||||||
while (caughtObjectContainer.Any(f => Vector2Extensions.DistanceSquared(f.Position, position) < checkDistance))
|
while (caughtObjectContainer.Any(f => Vector2Extensions.DistanceSquared(f.Position, position) < checkDistance))
|
||||||
{
|
{
|
||||||
position.X += RNG.NextSingle(-adjustedRadius, adjustedRadius);
|
position.X += RNG.NextSingle(-adjustedRadius, adjustedRadius);
|
||||||
@ -465,7 +469,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case DroppedObjectAnimation.Explode:
|
case DroppedObjectAnimation.Explode:
|
||||||
var originalX = droppedObjectTarget.ToSpaceOfOtherDrawable(d.DrawPosition, caughtObjectContainer).X * Scale.X;
|
float originalX = droppedObjectTarget.ToSpaceOfOtherDrawable(d.DrawPosition, caughtObjectContainer).X * caughtObjectContainer.Scale.X;
|
||||||
d.MoveToY(d.Y - 50, 250, Easing.OutSine).Then().MoveToY(d.Y + 50, 500, Easing.InSine);
|
d.MoveToY(d.Y - 50, 250, Easing.OutSine).Then().MoveToY(d.Y + 50, 500, Easing.InSine);
|
||||||
d.MoveToX(d.X + originalX * 6, 1000);
|
d.MoveToX(d.X + originalX * 6, 1000);
|
||||||
d.FadeOut(750);
|
d.FadeOut(750);
|
||||||
|
@ -121,7 +121,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
CatcherTrail sprite = trailPool.Get();
|
CatcherTrail sprite = trailPool.Get();
|
||||||
|
|
||||||
sprite.AnimationState = catcher.CurrentState;
|
sprite.AnimationState = catcher.CurrentState;
|
||||||
sprite.Scale = catcher.Scale;
|
sprite.Scale = catcher.Scale * catcher.Body.Scale;
|
||||||
sprite.Position = catcher.Position;
|
sprite.Position = catcher.Position;
|
||||||
|
|
||||||
target.Add(sprite);
|
target.Add(sprite);
|
||||||
|
@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
|
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// All constants are in osu!stable's gamefield space, which is shifted 16px downwards.
|
/// All constants are in osu!stable's gamefield space, which is shifted 16px downwards.
|
||||||
/// This offset is negated in both osu!stable and osu!lazer to bring all constants into window-space.
|
/// This offset is negated to bring all constants into window-space.
|
||||||
/// Note: SPINNER_Y_CENTRE + SPINNER_TOP_OFFSET - Position.Y = 240 (=480/2, or half the window-space in osu!stable)
|
/// Note: SPINNER_Y_CENTRE + SPINNER_TOP_OFFSET - Position.Y = 240 (=480/2, or half the window-space in osu!stable)
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
protected const float SPINNER_TOP_OFFSET = 45f - 16f;
|
protected const float SPINNER_TOP_OFFSET = 45f - 16f;
|
||||||
|
@ -38,19 +38,28 @@ namespace osu.Game.Tests.Database
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestDefaultsPopulationAndQuery()
|
public void TestDefaultsPopulationAndQuery()
|
||||||
{
|
{
|
||||||
Assert.That(query().Count, Is.EqualTo(0));
|
Assert.That(queryCount(), Is.EqualTo(0));
|
||||||
|
|
||||||
KeyBindingContainer testContainer = new TestKeyBindingContainer();
|
KeyBindingContainer testContainer = new TestKeyBindingContainer();
|
||||||
|
|
||||||
keyBindingStore.Register(testContainer);
|
keyBindingStore.Register(testContainer);
|
||||||
|
|
||||||
Assert.That(query().Count, Is.EqualTo(3));
|
Assert.That(queryCount(), Is.EqualTo(3));
|
||||||
|
|
||||||
Assert.That(query().Where(k => k.ActionInt == (int)GlobalAction.Back).Count, Is.EqualTo(1));
|
Assert.That(queryCount(GlobalAction.Back), Is.EqualTo(1));
|
||||||
Assert.That(query().Where(k => k.ActionInt == (int)GlobalAction.Select).Count, Is.EqualTo(2));
|
Assert.That(queryCount(GlobalAction.Select), Is.EqualTo(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
private IQueryable<RealmKeyBinding> query() => realmContextFactory.Context.All<RealmKeyBinding>();
|
private int queryCount(GlobalAction? match = null)
|
||||||
|
{
|
||||||
|
using (var usage = realmContextFactory.GetForRead())
|
||||||
|
{
|
||||||
|
var results = usage.Realm.All<RealmKeyBinding>();
|
||||||
|
if (match.HasValue)
|
||||||
|
results = results.Where(k => k.ActionInt == (int)match.Value);
|
||||||
|
return results.Count();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestUpdateViaQueriedReference()
|
public void TestUpdateViaQueriedReference()
|
||||||
@ -59,7 +68,9 @@ namespace osu.Game.Tests.Database
|
|||||||
|
|
||||||
keyBindingStore.Register(testContainer);
|
keyBindingStore.Register(testContainer);
|
||||||
|
|
||||||
var backBinding = query().Single(k => k.ActionInt == (int)GlobalAction.Back);
|
using (var primaryUsage = realmContextFactory.GetForRead())
|
||||||
|
{
|
||||||
|
var backBinding = primaryUsage.Realm.All<RealmKeyBinding>().Single(k => k.ActionInt == (int)GlobalAction.Back);
|
||||||
|
|
||||||
Assert.That(backBinding.KeyCombination.Keys, Is.EquivalentTo(new[] { InputKey.Escape }));
|
Assert.That(backBinding.KeyCombination.Keys, Is.EquivalentTo(new[] { InputKey.Escape }));
|
||||||
|
|
||||||
@ -76,9 +87,10 @@ namespace osu.Game.Tests.Database
|
|||||||
Assert.That(backBinding.KeyCombination.Keys, Is.EquivalentTo(new[] { InputKey.BackSpace }));
|
Assert.That(backBinding.KeyCombination.Keys, Is.EquivalentTo(new[] { InputKey.BackSpace }));
|
||||||
|
|
||||||
// check still correct after re-query.
|
// check still correct after re-query.
|
||||||
backBinding = query().Single(k => k.ActionInt == (int)GlobalAction.Back);
|
backBinding = primaryUsage.Realm.All<RealmKeyBinding>().Single(k => k.ActionInt == (int)GlobalAction.Back);
|
||||||
Assert.That(backBinding.KeyCombination.Keys, Is.EquivalentTo(new[] { InputKey.BackSpace }));
|
Assert.That(backBinding.KeyCombination.Keys, Is.EquivalentTo(new[] { InputKey.BackSpace }));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[TearDown]
|
[TearDown]
|
||||||
public void TearDown()
|
public void TearDown()
|
||||||
|
@ -0,0 +1,41 @@
|
|||||||
|
// 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 NUnit.Framework;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Localisation
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class BeatmapMetadataRomanisationTest
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void TestRomanisation()
|
||||||
|
{
|
||||||
|
var metadata = new BeatmapMetadata
|
||||||
|
{
|
||||||
|
Artist = "Romanised Artist",
|
||||||
|
ArtistUnicode = "Unicode Artist",
|
||||||
|
Title = "Romanised title",
|
||||||
|
TitleUnicode = "Unicode Title"
|
||||||
|
};
|
||||||
|
var romanisableString = metadata.ToRomanisableString();
|
||||||
|
|
||||||
|
Assert.AreEqual(metadata.ToString(), romanisableString.Romanised);
|
||||||
|
Assert.AreEqual($"{metadata.ArtistUnicode} - {metadata.TitleUnicode}", romanisableString.Original);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestRomanisationNoUnicode()
|
||||||
|
{
|
||||||
|
var metadata = new BeatmapMetadata
|
||||||
|
{
|
||||||
|
Artist = "Romanised Artist",
|
||||||
|
Title = "Romanised title"
|
||||||
|
};
|
||||||
|
var romanisableString = metadata.ToRomanisableString();
|
||||||
|
|
||||||
|
Assert.AreEqual(romanisableString.Romanised, romanisableString.Original);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
123
osu.Game.Tests/NonVisual/FirstAvailableHitWindowsTest.cs
Normal file
123
osu.Game.Tests/NonVisual/FirstAvailableHitWindowsTest.cs
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Rulesets.Judgements;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osu.Game.Scoring;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.NonVisual
|
||||||
|
{
|
||||||
|
public class FirstAvailableHitWindowsTest
|
||||||
|
{
|
||||||
|
private TestDrawableRuleset testDrawableRuleset;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
testDrawableRuleset = new TestDrawableRuleset();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestResultIfOnlyParentHitWindowIsEmpty()
|
||||||
|
{
|
||||||
|
var testObject = new TestHitObject(HitWindows.Empty);
|
||||||
|
HitObject nested = new TestHitObject(new HitWindows());
|
||||||
|
testObject.AddNested(nested);
|
||||||
|
testDrawableRuleset.HitObjects = new List<HitObject> { testObject };
|
||||||
|
|
||||||
|
Assert.AreSame(testDrawableRuleset.FirstAvailableHitWindows, nested.HitWindows);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestResultIfParentHitWindowsIsNotEmpty()
|
||||||
|
{
|
||||||
|
var testObject = new TestHitObject(new HitWindows());
|
||||||
|
HitObject nested = new TestHitObject(new HitWindows());
|
||||||
|
testObject.AddNested(nested);
|
||||||
|
testDrawableRuleset.HitObjects = new List<HitObject> { testObject };
|
||||||
|
|
||||||
|
Assert.AreSame(testDrawableRuleset.FirstAvailableHitWindows, testObject.HitWindows);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestResultIfParentAndChildHitWindowsAreEmpty()
|
||||||
|
{
|
||||||
|
var firstObject = new TestHitObject(HitWindows.Empty);
|
||||||
|
HitObject nested = new TestHitObject(HitWindows.Empty);
|
||||||
|
firstObject.AddNested(nested);
|
||||||
|
|
||||||
|
var secondObject = new TestHitObject(new HitWindows());
|
||||||
|
testDrawableRuleset.HitObjects = new List<HitObject> { firstObject, secondObject };
|
||||||
|
|
||||||
|
Assert.AreSame(testDrawableRuleset.FirstAvailableHitWindows, secondObject.HitWindows);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestResultIfAllHitWindowsAreEmpty()
|
||||||
|
{
|
||||||
|
var firstObject = new TestHitObject(HitWindows.Empty);
|
||||||
|
HitObject nested = new TestHitObject(HitWindows.Empty);
|
||||||
|
firstObject.AddNested(nested);
|
||||||
|
|
||||||
|
testDrawableRuleset.HitObjects = new List<HitObject> { firstObject };
|
||||||
|
|
||||||
|
Assert.IsNull(testDrawableRuleset.FirstAvailableHitWindows);
|
||||||
|
}
|
||||||
|
|
||||||
|
[SuppressMessage("ReSharper", "UnassignedGetOnlyAutoProperty")]
|
||||||
|
private class TestDrawableRuleset : DrawableRuleset
|
||||||
|
{
|
||||||
|
public List<HitObject> HitObjects;
|
||||||
|
public override IEnumerable<HitObject> Objects => HitObjects;
|
||||||
|
|
||||||
|
public override event Action<JudgementResult> NewResult;
|
||||||
|
public override event Action<JudgementResult> RevertResult;
|
||||||
|
|
||||||
|
public override Playfield Playfield { get; }
|
||||||
|
public override Container Overlays { get; }
|
||||||
|
public override Container FrameStableComponents { get; }
|
||||||
|
public override IFrameStableClock FrameStableClock { get; }
|
||||||
|
internal override bool FrameStablePlayback { get; set; }
|
||||||
|
public override IReadOnlyList<Mod> Mods { get; }
|
||||||
|
|
||||||
|
public override double GameplayStartTime { get; }
|
||||||
|
public override GameplayCursorContainer Cursor { get; }
|
||||||
|
|
||||||
|
public TestDrawableRuleset()
|
||||||
|
: base(new OsuRuleset())
|
||||||
|
{
|
||||||
|
// won't compile without this.
|
||||||
|
NewResult?.Invoke(null);
|
||||||
|
RevertResult?.Invoke(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SetReplayScore(Score replayScore) => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public override void SetRecordTarget(Score score) => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public override void RequestResume(Action continueResume) => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public override void CancelResume() => throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TestHitObject : HitObject
|
||||||
|
{
|
||||||
|
public TestHitObject(HitWindows hitWindows)
|
||||||
|
{
|
||||||
|
HitWindows = hitWindows;
|
||||||
|
HitWindows.SetDifficulty(0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
public new void AddNested(HitObject nested) => base.AddNested(nested);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,8 @@ using osu.Game.Online.Rooms;
|
|||||||
using osu.Game.Online.Solo;
|
using osu.Game.Online.Solo;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.Judgements;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Screens.Ranking;
|
using osu.Game.Screens.Ranking;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Gameplay
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
@ -35,6 +37,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddUntilStep("wait for token request", () => Player.TokenCreationRequested);
|
AddUntilStep("wait for token request", () => Player.TokenCreationRequested);
|
||||||
|
|
||||||
AddUntilStep("wait for track to start running", () => Beatmap.Value.Track.IsRunning);
|
AddUntilStep("wait for track to start running", () => Beatmap.Value.Track.IsRunning);
|
||||||
|
|
||||||
|
addFakeHit();
|
||||||
|
|
||||||
AddStep("seek to completion", () => Player.GameplayClockContainer.Seek(Player.DrawableRuleset.Objects.Last().GetEndTime()));
|
AddStep("seek to completion", () => Player.GameplayClockContainer.Seek(Player.DrawableRuleset.Objects.Last().GetEndTime()));
|
||||||
|
|
||||||
AddUntilStep("results displayed", () => Player.GetChildScreen() is ResultsScreen);
|
AddUntilStep("results displayed", () => Player.GetChildScreen() is ResultsScreen);
|
||||||
@ -52,6 +57,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddUntilStep("wait for token request", () => Player.TokenCreationRequested);
|
AddUntilStep("wait for token request", () => Player.TokenCreationRequested);
|
||||||
|
|
||||||
AddUntilStep("wait for track to start running", () => Beatmap.Value.Track.IsRunning);
|
AddUntilStep("wait for track to start running", () => Beatmap.Value.Track.IsRunning);
|
||||||
|
|
||||||
|
addFakeHit();
|
||||||
|
|
||||||
AddStep("seek to completion", () => Player.GameplayClockContainer.Seek(Player.DrawableRuleset.Objects.Last().GetEndTime()));
|
AddStep("seek to completion", () => Player.GameplayClockContainer.Seek(Player.DrawableRuleset.Objects.Last().GetEndTime()));
|
||||||
|
|
||||||
AddUntilStep("results displayed", () => Player.GetChildScreen() is ResultsScreen);
|
AddUntilStep("results displayed", () => Player.GetChildScreen() is ResultsScreen);
|
||||||
@ -67,10 +75,29 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
AddUntilStep("wait for token request", () => Player.TokenCreationRequested);
|
AddUntilStep("wait for token request", () => Player.TokenCreationRequested);
|
||||||
|
|
||||||
|
AddUntilStep("wait for track to start running", () => Beatmap.Value.Track.IsRunning);
|
||||||
|
|
||||||
|
addFakeHit();
|
||||||
|
|
||||||
AddStep("exit", () => Player.Exit());
|
AddStep("exit", () => Player.Exit());
|
||||||
AddAssert("ensure no submission", () => Player.SubmittedScore == null);
|
AddAssert("ensure no submission", () => Player.SubmittedScore == null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestNoSubmissionOnEmptyFail()
|
||||||
|
{
|
||||||
|
prepareTokenResponse(true);
|
||||||
|
|
||||||
|
CreateTest(() => allowFail = true);
|
||||||
|
|
||||||
|
AddUntilStep("wait for token request", () => Player.TokenCreationRequested);
|
||||||
|
|
||||||
|
AddUntilStep("wait for fail", () => Player.HasFailed);
|
||||||
|
AddStep("exit", () => Player.Exit());
|
||||||
|
|
||||||
|
AddAssert("ensure no submission", () => Player.SubmittedScore == null);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestSubmissionOnFail()
|
public void TestSubmissionOnFail()
|
||||||
{
|
{
|
||||||
@ -79,12 +106,28 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
CreateTest(() => allowFail = true);
|
CreateTest(() => allowFail = true);
|
||||||
|
|
||||||
AddUntilStep("wait for token request", () => Player.TokenCreationRequested);
|
AddUntilStep("wait for token request", () => Player.TokenCreationRequested);
|
||||||
|
|
||||||
|
addFakeHit();
|
||||||
|
|
||||||
AddUntilStep("wait for fail", () => Player.HasFailed);
|
AddUntilStep("wait for fail", () => Player.HasFailed);
|
||||||
AddStep("exit", () => Player.Exit());
|
AddStep("exit", () => Player.Exit());
|
||||||
|
|
||||||
AddAssert("ensure failing submission", () => Player.SubmittedScore?.ScoreInfo.Passed == false);
|
AddAssert("ensure failing submission", () => Player.SubmittedScore?.ScoreInfo.Passed == false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestNoSubmissionOnEmptyExit()
|
||||||
|
{
|
||||||
|
prepareTokenResponse(true);
|
||||||
|
|
||||||
|
CreateTest(() => allowFail = false);
|
||||||
|
|
||||||
|
AddUntilStep("wait for token request", () => Player.TokenCreationRequested);
|
||||||
|
|
||||||
|
AddStep("exit", () => Player.Exit());
|
||||||
|
AddAssert("ensure no submission", () => Player.SubmittedScore == null);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestSubmissionOnExit()
|
public void TestSubmissionOnExit()
|
||||||
{
|
{
|
||||||
@ -93,10 +136,27 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
CreateTest(() => allowFail = false);
|
CreateTest(() => allowFail = false);
|
||||||
|
|
||||||
AddUntilStep("wait for token request", () => Player.TokenCreationRequested);
|
AddUntilStep("wait for token request", () => Player.TokenCreationRequested);
|
||||||
|
|
||||||
|
addFakeHit();
|
||||||
|
|
||||||
AddStep("exit", () => Player.Exit());
|
AddStep("exit", () => Player.Exit());
|
||||||
AddAssert("ensure failing submission", () => Player.SubmittedScore?.ScoreInfo.Passed == false);
|
AddAssert("ensure failing submission", () => Player.SubmittedScore?.ScoreInfo.Passed == false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addFakeHit()
|
||||||
|
{
|
||||||
|
AddUntilStep("wait for first result", () => Player.Results.Count > 0);
|
||||||
|
|
||||||
|
AddStep("force successfuly hit", () =>
|
||||||
|
{
|
||||||
|
Player.ScoreProcessor.RevertResult(Player.Results.First());
|
||||||
|
Player.ScoreProcessor.ApplyResult(new OsuJudgementResult(Beatmap.Value.Beatmap.HitObjects.First(), new OsuJudgement())
|
||||||
|
{
|
||||||
|
Type = HitResult.Great,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void prepareTokenResponse(bool validToken)
|
private void prepareTokenResponse(bool validToken)
|
||||||
{
|
{
|
||||||
AddStep("Prepare test API", () =>
|
AddStep("Prepare test API", () =>
|
||||||
|
@ -39,7 +39,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
Version = "All Metrics",
|
Version = "All Metrics",
|
||||||
Metadata = new BeatmapMetadata
|
Metadata = new BeatmapMetadata
|
||||||
{
|
{
|
||||||
Source = "osu!lazer",
|
Source = "osu!",
|
||||||
Tags = "this beatmap has all the metrics",
|
Tags = "this beatmap has all the metrics",
|
||||||
},
|
},
|
||||||
BaseDifficulty = new BeatmapDifficulty
|
BaseDifficulty = new BeatmapDifficulty
|
||||||
@ -100,7 +100,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
Version = "Only Ratings",
|
Version = "Only Ratings",
|
||||||
Metadata = new BeatmapMetadata
|
Metadata = new BeatmapMetadata
|
||||||
{
|
{
|
||||||
Source = "osu!lazer",
|
Source = "osu!",
|
||||||
Tags = "this beatmap has ratings metrics but not retries or fails",
|
Tags = "this beatmap has ratings metrics but not retries or fails",
|
||||||
},
|
},
|
||||||
BaseDifficulty = new BeatmapDifficulty
|
BaseDifficulty = new BeatmapDifficulty
|
||||||
@ -122,7 +122,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
Version = "Only Retries and Fails",
|
Version = "Only Retries and Fails",
|
||||||
Metadata = new BeatmapMetadata
|
Metadata = new BeatmapMetadata
|
||||||
{
|
{
|
||||||
Source = "osu!lazer",
|
Source = "osu!",
|
||||||
Tags = "this beatmap has retries and fails but no ratings",
|
Tags = "this beatmap has retries and fails but no ratings",
|
||||||
},
|
},
|
||||||
BaseDifficulty = new BeatmapDifficulty
|
BaseDifficulty = new BeatmapDifficulty
|
||||||
@ -149,7 +149,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
Version = "No Metrics",
|
Version = "No Metrics",
|
||||||
Metadata = new BeatmapMetadata
|
Metadata = new BeatmapMetadata
|
||||||
{
|
{
|
||||||
Source = "osu!lazer",
|
Source = "osu!",
|
||||||
Tags = "this beatmap has no metrics",
|
Tags = "this beatmap has no metrics",
|
||||||
},
|
},
|
||||||
BaseDifficulty = new BeatmapDifficulty
|
BaseDifficulty = new BeatmapDifficulty
|
||||||
|
@ -103,14 +103,11 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
|
|
||||||
var easierMods = osu.GetModsFor(ModType.DifficultyReduction);
|
var easierMods = osu.GetModsFor(ModType.DifficultyReduction);
|
||||||
var harderMods = osu.GetModsFor(ModType.DifficultyIncrease);
|
var harderMods = osu.GetModsFor(ModType.DifficultyIncrease);
|
||||||
var conversionMods = osu.GetModsFor(ModType.Conversion);
|
|
||||||
|
|
||||||
var noFailMod = osu.GetModsFor(ModType.DifficultyReduction).FirstOrDefault(m => m is OsuModNoFail);
|
var noFailMod = osu.GetModsFor(ModType.DifficultyReduction).FirstOrDefault(m => m is OsuModNoFail);
|
||||||
|
|
||||||
var doubleTimeMod = harderMods.OfType<MultiMod>().FirstOrDefault(m => m.Mods.Any(a => a is OsuModDoubleTime));
|
var doubleTimeMod = harderMods.OfType<MultiMod>().FirstOrDefault(m => m.Mods.Any(a => a is OsuModDoubleTime));
|
||||||
|
|
||||||
var targetMod = conversionMods.FirstOrDefault(m => m is OsuModTarget);
|
|
||||||
|
|
||||||
var easy = easierMods.FirstOrDefault(m => m is OsuModEasy);
|
var easy = easierMods.FirstOrDefault(m => m is OsuModEasy);
|
||||||
var hardRock = harderMods.FirstOrDefault(m => m is OsuModHardRock);
|
var hardRock = harderMods.FirstOrDefault(m => m is OsuModHardRock);
|
||||||
|
|
||||||
@ -118,8 +115,6 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
testMultiMod(doubleTimeMod);
|
testMultiMod(doubleTimeMod);
|
||||||
testIncompatibleMods(easy, hardRock);
|
testIncompatibleMods(easy, hardRock);
|
||||||
testDeselectAll(easierMods.Where(m => !(m is MultiMod)));
|
testDeselectAll(easierMods.Where(m => !(m is MultiMod)));
|
||||||
|
|
||||||
testUnimplementedMod(targetMod);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -249,6 +244,19 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
AddAssert("DT + HD still selected", () => modSelect.ChildrenOfType<ModButton>().Count(b => b.Selected) == 2);
|
AddAssert("DT + HD still selected", () => modSelect.ChildrenOfType<ModButton>().Count(b => b.Selected) == 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestUnimplementedModIsUnselectable()
|
||||||
|
{
|
||||||
|
var testRuleset = new TestUnimplementedModOsuRuleset();
|
||||||
|
changeTestRuleset(testRuleset.RulesetInfo);
|
||||||
|
|
||||||
|
var conversionMods = testRuleset.GetModsFor(ModType.Conversion);
|
||||||
|
|
||||||
|
var unimplementedMod = conversionMods.FirstOrDefault(m => m is TestUnimplementedMod);
|
||||||
|
|
||||||
|
testUnimplementedMod(unimplementedMod);
|
||||||
|
}
|
||||||
|
|
||||||
private void testSingleMod(Mod mod)
|
private void testSingleMod(Mod mod)
|
||||||
{
|
{
|
||||||
selectNext(mod);
|
selectNext(mod);
|
||||||
@ -343,6 +351,12 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
waitForLoad();
|
waitForLoad();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void changeTestRuleset(RulesetInfo rulesetInfo)
|
||||||
|
{
|
||||||
|
AddStep($"change ruleset to {rulesetInfo.Name}", () => { Ruleset.Value = rulesetInfo; });
|
||||||
|
waitForLoad();
|
||||||
|
}
|
||||||
|
|
||||||
private void waitForLoad() =>
|
private void waitForLoad() =>
|
||||||
AddUntilStep("wait for icons to load", () => modSelect.AllLoaded);
|
AddUntilStep("wait for icons to load", () => modSelect.AllLoaded);
|
||||||
|
|
||||||
@ -401,5 +415,24 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
{
|
{
|
||||||
protected override bool Stacked => false;
|
protected override bool Stacked => false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class TestUnimplementedMod : Mod
|
||||||
|
{
|
||||||
|
public override string Name => "Unimplemented mod";
|
||||||
|
public override string Acronym => "UM";
|
||||||
|
public override string Description => "A mod that is not implemented.";
|
||||||
|
public override double ScoreMultiplier => 1;
|
||||||
|
public override ModType Type => ModType.Conversion;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestUnimplementedModOsuRuleset : OsuRuleset
|
||||||
|
{
|
||||||
|
public override IEnumerable<Mod> GetModsFor(ModType type)
|
||||||
|
{
|
||||||
|
if (type == ModType.Conversion) return base.GetModsFor(type).Concat(new[] { new TestUnimplementedMod() });
|
||||||
|
|
||||||
|
return base.GetModsFor(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -324,7 +324,7 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
protected override bool CanSkipImport(BeatmapSetInfo existing, BeatmapSetInfo import)
|
protected override bool CanSkipImport(BeatmapSetInfo existing, BeatmapSetInfo import)
|
||||||
{
|
{
|
||||||
if (!base.CanReuseExisting(existing, import))
|
if (!base.CanSkipImport(existing, import))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return existing.Beatmaps.Any(b => b.OnlineBeatmapID != null);
|
return existing.Beatmaps.Any(b => b.OnlineBeatmapID != null);
|
||||||
|
@ -94,7 +94,10 @@ namespace osu.Game.Beatmaps
|
|||||||
public RomanisableString ToRomanisableString()
|
public RomanisableString ToRomanisableString()
|
||||||
{
|
{
|
||||||
string author = Author == null ? string.Empty : $"({Author})";
|
string author = Author == null ? string.Empty : $"({Author})";
|
||||||
return new RomanisableString($"{ArtistUnicode} - {TitleUnicode} {author}".Trim(), $"{Artist} - {Title} {author}".Trim());
|
var artistUnicode = string.IsNullOrEmpty(ArtistUnicode) ? Artist : ArtistUnicode;
|
||||||
|
var titleUnicode = string.IsNullOrEmpty(TitleUnicode) ? Title : TitleUnicode;
|
||||||
|
|
||||||
|
return new RomanisableString($"{artistUnicode} - {titleUnicode} {author}".Trim(), $"{Artist} - {Title} {author}".Trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
|
@ -353,8 +353,6 @@ namespace osu.Game.Database
|
|||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
delayEvents();
|
|
||||||
|
|
||||||
bool checkedExisting = false;
|
bool checkedExisting = false;
|
||||||
TModel existing = null;
|
TModel existing = null;
|
||||||
|
|
||||||
@ -394,6 +392,8 @@ namespace osu.Game.Database
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
delayEvents();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
LogForModel(item, @"Beginning import...");
|
LogForModel(item, @"Beginning import...");
|
||||||
|
@ -9,6 +9,7 @@ namespace osu.Game.Database
|
|||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The main realm context, bound to the update thread.
|
/// The main realm context, bound to the update thread.
|
||||||
|
/// If querying from a non-update thread is needed, use <see cref="GetForRead"/> or <see cref="GetForWrite"/> to receive a context instead.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Realm Context { get; }
|
Realm Context { get; }
|
||||||
|
|
||||||
|
@ -77,6 +77,9 @@ namespace osu.Game.Database
|
|||||||
{
|
{
|
||||||
cmd.CommandText = "PRAGMA journal_mode=WAL;";
|
cmd.CommandText = "PRAGMA journal_mode=WAL;";
|
||||||
cmd.ExecuteNonQuery();
|
cmd.ExecuteNonQuery();
|
||||||
|
|
||||||
|
cmd.CommandText = "PRAGMA foreign_keys=OFF;";
|
||||||
|
cmd.ExecuteNonQuery();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
|
@ -2,8 +2,10 @@
|
|||||||
// 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.Diagnostics;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Development;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
@ -38,11 +40,18 @@ namespace osu.Game.Database
|
|||||||
private static readonly GlobalStatistic<int> pending_writes = GlobalStatistics.Get<int>("Realm", "Pending writes");
|
private static readonly GlobalStatistic<int> pending_writes = GlobalStatistics.Get<int>("Realm", "Pending writes");
|
||||||
private static readonly GlobalStatistic<int> active_usages = GlobalStatistics.Get<int>("Realm", "Active usages");
|
private static readonly GlobalStatistic<int> active_usages = GlobalStatistics.Get<int>("Realm", "Active usages");
|
||||||
|
|
||||||
|
private readonly object updateContextLock = new object();
|
||||||
|
|
||||||
private Realm context;
|
private Realm context;
|
||||||
|
|
||||||
public Realm Context
|
public Realm Context
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
{
|
||||||
|
if (!ThreadSafety.IsUpdateThread)
|
||||||
|
throw new InvalidOperationException($"Use {nameof(GetForRead)} or {nameof(GetForWrite)} when performing realm operations from a non-update thread");
|
||||||
|
|
||||||
|
lock (updateContextLock)
|
||||||
{
|
{
|
||||||
if (context == null)
|
if (context == null)
|
||||||
{
|
{
|
||||||
@ -55,6 +64,7 @@ namespace osu.Game.Database
|
|||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public RealmContextFactory(Storage storage)
|
public RealmContextFactory(Storage storage)
|
||||||
{
|
{
|
||||||
@ -107,9 +117,12 @@ namespace osu.Game.Database
|
|||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
|
lock (updateContextLock)
|
||||||
|
{
|
||||||
if (context?.Refresh() == true)
|
if (context?.Refresh() == true)
|
||||||
refreshes.Value++;
|
refreshes.Value++;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private Realm createContext()
|
private Realm createContext()
|
||||||
{
|
{
|
||||||
@ -154,9 +167,15 @@ namespace osu.Game.Database
|
|||||||
private void flushContexts()
|
private void flushContexts()
|
||||||
{
|
{
|
||||||
Logger.Log(@"Flushing realm contexts...", LoggingTarget.Database);
|
Logger.Log(@"Flushing realm contexts...", LoggingTarget.Database);
|
||||||
|
Debug.Assert(blockingLock.CurrentCount == 0);
|
||||||
|
|
||||||
var previousContext = context;
|
Realm previousContext;
|
||||||
|
|
||||||
|
lock (updateContextLock)
|
||||||
|
{
|
||||||
|
previousContext = context;
|
||||||
context = null;
|
context = null;
|
||||||
|
}
|
||||||
|
|
||||||
// wait for all threaded usages to finish
|
// wait for all threaded usages to finish
|
||||||
while (active_usages.Value > 0)
|
while (active_usages.Value > 0)
|
||||||
|
@ -320,6 +320,7 @@ namespace osu.Game.Online.Chat
|
|||||||
JoinMultiplayerMatch,
|
JoinMultiplayerMatch,
|
||||||
Spectate,
|
Spectate,
|
||||||
OpenUserProfile,
|
OpenUserProfile,
|
||||||
|
SearchBeatmapSet,
|
||||||
OpenWiki,
|
OpenWiki,
|
||||||
Custom,
|
Custom,
|
||||||
}
|
}
|
||||||
|
@ -305,6 +305,10 @@ namespace osu.Game
|
|||||||
ShowChannel(link.Argument);
|
ShowChannel(link.Argument);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case LinkAction.SearchBeatmapSet:
|
||||||
|
SearchBeatmapSet(link.Argument);
|
||||||
|
break;
|
||||||
|
|
||||||
case LinkAction.OpenEditorTimestamp:
|
case LinkAction.OpenEditorTimestamp:
|
||||||
case LinkAction.JoinMultiplayerMatch:
|
case LinkAction.JoinMultiplayerMatch:
|
||||||
case LinkAction.Spectate:
|
case LinkAction.Spectate:
|
||||||
@ -375,6 +379,12 @@ namespace osu.Game
|
|||||||
/// <param name="beatmapId">The beatmap to show.</param>
|
/// <param name="beatmapId">The beatmap to show.</param>
|
||||||
public void ShowBeatmap(int beatmapId) => waitForReady(() => beatmapSetOverlay, _ => beatmapSetOverlay.FetchAndShowBeatmap(beatmapId));
|
public void ShowBeatmap(int beatmapId) => waitForReady(() => beatmapSetOverlay, _ => beatmapSetOverlay.FetchAndShowBeatmap(beatmapId));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Shows the beatmap listing overlay, with the given <paramref name="query"/> in the search box.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="query">The query to search for.</param>
|
||||||
|
public void SearchBeatmapSet(string query) => waitForReady(() => beatmapListing, _ => beatmapListing.ShowWithSearch(query));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Show a wiki's page as an overlay
|
/// Show a wiki's page as an overlay
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -80,7 +80,7 @@ namespace osu.Game
|
|||||||
return @"local " + (DebugUtils.IsDebugBuild ? @"debug" : @"release");
|
return @"local " + (DebugUtils.IsDebugBuild ? @"debug" : @"release");
|
||||||
|
|
||||||
var version = AssemblyVersion;
|
var version = AssemblyVersion;
|
||||||
return $@"{version.Major}.{version.Minor}.{version.Build}";
|
return $@"{version.Major}.{version.Minor}.{version.Build}-lazer";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,7 +162,7 @@ namespace osu.Game
|
|||||||
public OsuGameBase()
|
public OsuGameBase()
|
||||||
{
|
{
|
||||||
UseDevelopmentServer = DebugUtils.IsDebugBuild;
|
UseDevelopmentServer = DebugUtils.IsDebugBuild;
|
||||||
Name = @"osu!lazer";
|
Name = @"osu!";
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
|
@ -122,6 +122,9 @@ namespace osu.Game.Overlays.BeatmapListing
|
|||||||
sortControlBackground.Colour = colourProvider.Background5;
|
sortControlBackground.Colour = colourProvider.Background5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Search(string query)
|
||||||
|
=> searchControl.Query.Value = query;
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
@ -89,6 +89,12 @@ namespace osu.Game.Overlays
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ShowWithSearch(string query)
|
||||||
|
{
|
||||||
|
filterControl.Search(query);
|
||||||
|
Show();
|
||||||
|
}
|
||||||
|
|
||||||
protected override BeatmapListingHeader CreateHeader() => new BeatmapListingHeader();
|
protected override BeatmapListingHeader CreateHeader() => new BeatmapListingHeader();
|
||||||
|
|
||||||
protected override Color4 BackgroundColour => ColourProvider.Background6;
|
protected override Color4 BackgroundColour => ColourProvider.Background6;
|
||||||
|
@ -8,15 +8,12 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osuTK;
|
|
||||||
|
|
||||||
namespace osu.Game.Overlays.BeatmapSet
|
namespace osu.Game.Overlays.BeatmapSet
|
||||||
{
|
{
|
||||||
public class Info : Container
|
public class Info : Container
|
||||||
{
|
{
|
||||||
private const float transition_duration = 250;
|
|
||||||
private const float metadata_width = 175;
|
private const float metadata_width = 175;
|
||||||
private const float spacing = 20;
|
private const float spacing = 20;
|
||||||
private const float base_height = 220;
|
private const float base_height = 220;
|
||||||
@ -60,7 +57,7 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
Child = new Container
|
Child = new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Child = new MetadataSection("Description"),
|
Child = new MetadataSection(MetadataType.Description),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
new Container
|
new Container
|
||||||
@ -78,10 +75,10 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
Direction = FillDirection.Full,
|
Direction = FillDirection.Full,
|
||||||
Children = new[]
|
Children = new[]
|
||||||
{
|
{
|
||||||
source = new MetadataSection("Source"),
|
source = new MetadataSection(MetadataType.Source),
|
||||||
genre = new MetadataSection("Genre") { Width = 0.5f },
|
genre = new MetadataSection(MetadataType.Genre) { Width = 0.5f },
|
||||||
language = new MetadataSection("Language") { Width = 0.5f },
|
language = new MetadataSection(MetadataType.Language) { Width = 0.5f },
|
||||||
tags = new MetadataSection("Tags"),
|
tags = new MetadataSection(MetadataType.Tags),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -135,48 +132,5 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
successRateBackground.Colour = colourProvider.Background4;
|
successRateBackground.Colour = colourProvider.Background4;
|
||||||
background.Colour = colourProvider.Background5;
|
background.Colour = colourProvider.Background5;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class MetadataSection : FillFlowContainer
|
|
||||||
{
|
|
||||||
private readonly TextFlowContainer textFlow;
|
|
||||||
|
|
||||||
public string Text
|
|
||||||
{
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(value))
|
|
||||||
{
|
|
||||||
Hide();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.FadeIn(transition_duration);
|
|
||||||
textFlow.Clear();
|
|
||||||
textFlow.AddText(value, s => s.Font = s.Font.With(size: 12));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public MetadataSection(string title)
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X;
|
|
||||||
AutoSizeAxes = Axes.Y;
|
|
||||||
Spacing = new Vector2(5f);
|
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
|
||||||
{
|
|
||||||
new OsuSpriteText
|
|
||||||
{
|
|
||||||
Text = title,
|
|
||||||
Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold),
|
|
||||||
Margin = new MarginPadding { Top = 15 },
|
|
||||||
},
|
|
||||||
textFlow = new OsuTextFlowContainer
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
AutoSizeAxes = Axes.Y,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
115
osu.Game/Overlays/BeatmapSet/MetadataSection.cs
Normal file
115
osu.Game/Overlays/BeatmapSet/MetadataSection.cs
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
// 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.Extensions.Color4Extensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Online.Chat;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.BeatmapSet
|
||||||
|
{
|
||||||
|
public class MetadataSection : Container
|
||||||
|
{
|
||||||
|
private readonly FillFlowContainer textContainer;
|
||||||
|
private readonly MetadataType type;
|
||||||
|
private TextFlowContainer textFlow;
|
||||||
|
|
||||||
|
private const float transition_duration = 250;
|
||||||
|
|
||||||
|
public MetadataSection(MetadataType type)
|
||||||
|
{
|
||||||
|
this.type = type;
|
||||||
|
|
||||||
|
Alpha = 0;
|
||||||
|
|
||||||
|
RelativeSizeAxes = Axes.X;
|
||||||
|
AutoSizeAxes = Axes.Y;
|
||||||
|
|
||||||
|
InternalChild = textContainer = new FillFlowContainer
|
||||||
|
{
|
||||||
|
Alpha = 0,
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
|
||||||
|
Margin = new MarginPadding { Top = 15 },
|
||||||
|
Spacing = new Vector2(5),
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Child = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Text = this.type.ToString(),
|
||||||
|
Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 14),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Text
|
||||||
|
{
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(value))
|
||||||
|
{
|
||||||
|
this.FadeOut(transition_duration);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.FadeIn(transition_duration);
|
||||||
|
|
||||||
|
setTextAsync(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setTextAsync(string text)
|
||||||
|
{
|
||||||
|
LoadComponentAsync(new LinkFlowContainer(s => s.Font = s.Font.With(size: 14))
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Colour = Color4.White.Opacity(0.75f),
|
||||||
|
}, loaded =>
|
||||||
|
{
|
||||||
|
textFlow?.Expire();
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case MetadataType.Tags:
|
||||||
|
string[] tags = text.Split(" ");
|
||||||
|
|
||||||
|
for (int i = 0; i <= tags.Length - 1; i++)
|
||||||
|
{
|
||||||
|
loaded.AddLink(tags[i], LinkAction.SearchBeatmapSet, tags[i]);
|
||||||
|
|
||||||
|
if (i != tags.Length - 1)
|
||||||
|
loaded.AddText(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MetadataType.Source:
|
||||||
|
loaded.AddLink(text, LinkAction.SearchBeatmapSet, text);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
loaded.AddText(text);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
textContainer.Add(textFlow = loaded);
|
||||||
|
|
||||||
|
// fade in if we haven't yet.
|
||||||
|
textContainer.FadeIn(transition_duration);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
osu.Game/Overlays/BeatmapSet/MetadataType.cs
Normal file
14
osu.Game/Overlays/BeatmapSet/MetadataType.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.BeatmapSet
|
||||||
|
{
|
||||||
|
public enum MetadataType
|
||||||
|
{
|
||||||
|
Tags,
|
||||||
|
Source,
|
||||||
|
Description,
|
||||||
|
Genre,
|
||||||
|
Language
|
||||||
|
}
|
||||||
|
}
|
@ -191,7 +191,7 @@ namespace osu.Game.Overlays.Volume
|
|||||||
bgProgress.Current.Value = 0.75f;
|
bgProgress.Current.Value = 0.75f;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int displayVolumeInt;
|
private int? displayVolumeInt;
|
||||||
|
|
||||||
private double displayVolume;
|
private double displayVolume;
|
||||||
|
|
||||||
@ -200,9 +200,6 @@ namespace osu.Game.Overlays.Volume
|
|||||||
get => displayVolume;
|
get => displayVolume;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (value == displayVolume)
|
|
||||||
return;
|
|
||||||
|
|
||||||
displayVolume = value;
|
displayVolume = value;
|
||||||
|
|
||||||
int intValue = (int)Math.Round(displayVolume * 100);
|
int intValue = (int)Math.Round(displayVolume * 100);
|
||||||
@ -218,7 +215,7 @@ namespace osu.Game.Overlays.Volume
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
maxGlow.EffectColour = Color4.Transparent;
|
maxGlow.EffectColour = Color4.Transparent;
|
||||||
text.Text = displayVolumeInt.ToString(CultureInfo.CurrentCulture);
|
text.Text = intValue.ToString(CultureInfo.CurrentCulture);
|
||||||
}
|
}
|
||||||
|
|
||||||
volumeCircle.Current.Value = displayVolume * 0.75f;
|
volumeCircle.Current.Value = displayVolume * 0.75f;
|
||||||
|
@ -489,15 +489,15 @@ namespace osu.Game.Rulesets.UI
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
foreach (var h in Objects)
|
foreach (var hitObject in Objects)
|
||||||
{
|
{
|
||||||
if (h.HitWindows.WindowFor(HitResult.Miss) > 0)
|
if (hitObject.HitWindows.WindowFor(HitResult.Miss) > 0)
|
||||||
return h.HitWindows;
|
return hitObject.HitWindows;
|
||||||
|
|
||||||
foreach (var n in h.NestedHitObjects)
|
foreach (var nested in hitObject.NestedHitObjects)
|
||||||
{
|
{
|
||||||
if (h.HitWindows.WindowFor(HitResult.Miss) > 0)
|
if (nested.HitWindows.WindowFor(HitResult.Miss) > 0)
|
||||||
return n.HitWindows;
|
return nested.HitWindows;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,7 +208,7 @@ namespace osu.Game.Scoring
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// This score is guaranteed to be an osu!lazer score.
|
// This is guaranteed to be a non-legacy score.
|
||||||
// The combo must be determined through the score's statistics, as both the beatmap's max combo and the difficulty calculator will provide osu!stable combo values.
|
// The combo must be determined through the score's statistics, as both the beatmap's max combo and the difficulty calculator will provide osu!stable combo values.
|
||||||
beatmapMaxCombo = Enum.GetValues(typeof(HitResult)).OfType<HitResult>().Where(r => r.AffectsCombo()).Select(r => score.Statistics.GetOrDefault(r)).Sum();
|
beatmapMaxCombo = Enum.GetValues(typeof(HitResult)).OfType<HitResult>().Where(r => r.AffectsCombo()).Select(r => score.Statistics.GetOrDefault(r)).Sum();
|
||||||
}
|
}
|
||||||
|
@ -230,7 +230,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
"New features are coming online every update. Make sure to stay up-to-date!",
|
"New features are coming online every update. Make sure to stay up-to-date!",
|
||||||
"If you find the UI too large or small, try adjusting UI scale in settings!",
|
"If you find the UI too large or small, try adjusting UI scale in settings!",
|
||||||
"Try adjusting the \"Screen Scaling\" mode to change your gameplay or UI area, even in fullscreen!",
|
"Try adjusting the \"Screen Scaling\" mode to change your gameplay or UI area, even in fullscreen!",
|
||||||
"For now, what used to be \"osu!direct\" is available to all users on lazer. You can access it anywhere using Ctrl-D!",
|
"What used to be \"osu!direct\" is available to all users just like on the website. You can access it anywhere using Ctrl-D!",
|
||||||
"Seeking in replays is available by dragging on the difficulty bar at the bottom of the screen!",
|
"Seeking in replays is available by dragging on the difficulty bar at the bottom of the screen!",
|
||||||
"Multithreading support means that even with low \"FPS\" your input and judgements will be accurate!",
|
"Multithreading support means that even with low \"FPS\" your input and judgements will be accurate!",
|
||||||
"Try scrolling down in the mod select panel to find a bunch of new fun mods!",
|
"Try scrolling down in the mod select panel to find a bunch of new fun mods!",
|
||||||
|
@ -130,6 +130,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
|||||||
// This is a quiet case in which the catchup is done by the master clock, so IsCatchingUp is not set on the player clock.
|
// This is a quiet case in which the catchup is done by the master clock, so IsCatchingUp is not set on the player clock.
|
||||||
if (timeDelta < -SYNC_TARGET)
|
if (timeDelta < -SYNC_TARGET)
|
||||||
{
|
{
|
||||||
|
// Importantly, set the clock to a non-catchup state. if this isn't done, updateMasterState may incorrectly pause the master clock
|
||||||
|
// when it is required to be running (ie. if all players are ahead of the master).
|
||||||
|
clock.IsCatchingUp = false;
|
||||||
clock.Stop();
|
clock.Stop();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ using osu.Framework.Logging;
|
|||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Online.Rooms;
|
using osu.Game.Online.Rooms;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Play
|
namespace osu.Game.Screens.Play
|
||||||
@ -144,6 +145,10 @@ namespace osu.Game.Screens.Play
|
|||||||
if (scoreSubmissionSource != null)
|
if (scoreSubmissionSource != null)
|
||||||
return scoreSubmissionSource.Task;
|
return scoreSubmissionSource.Task;
|
||||||
|
|
||||||
|
// if the user never hit anything, this score should not be counted in any way.
|
||||||
|
if (!score.ScoreInfo.Statistics.Any(s => s.Key.IsHit() && s.Value > 0))
|
||||||
|
return Task.CompletedTask;
|
||||||
|
|
||||||
scoreSubmissionSource = new TaskCompletionSource<bool>();
|
scoreSubmissionSource = new TaskCompletionSource<bool>();
|
||||||
var request = CreateSubmissionRequest(score, token.Value);
|
var request = CreateSubmissionRequest(score, token.Value);
|
||||||
|
|
||||||
|
@ -1,24 +1,25 @@
|
|||||||
// 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 osuTK;
|
using System.Linq;
|
||||||
using osuTK.Graphics;
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Graphics.Sprites;
|
|
||||||
using System.Linq;
|
|
||||||
using osu.Game.Online.API;
|
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
|
||||||
using osu.Game.Screens.Select.Details;
|
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Online.API.Requests;
|
|
||||||
using osu.Game.Rulesets;
|
|
||||||
using osu.Game.Online;
|
using osu.Game.Online;
|
||||||
|
using osu.Game.Online.API;
|
||||||
|
using osu.Game.Online.API.Requests;
|
||||||
|
using osu.Game.Overlays.BeatmapSet;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Screens.Select.Details;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Select
|
namespace osu.Game.Screens.Select
|
||||||
{
|
{
|
||||||
@ -128,13 +129,11 @@ namespace osu.Game.Screens.Select
|
|||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
LayoutDuration = transition_duration,
|
LayoutDuration = transition_duration,
|
||||||
LayoutEasing = Easing.OutQuad,
|
LayoutEasing = Easing.OutQuad,
|
||||||
Spacing = new Vector2(spacing * 2),
|
|
||||||
Margin = new MarginPadding { Top = spacing * 2 },
|
|
||||||
Children = new[]
|
Children = new[]
|
||||||
{
|
{
|
||||||
description = new MetadataSection("Description"),
|
description = new MetadataSection(MetadataType.Description),
|
||||||
source = new MetadataSection("Source"),
|
source = new MetadataSection(MetadataType.Source),
|
||||||
tags = new MetadataSection("Tags"),
|
tags = new MetadataSection(MetadataType.Tags),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -290,73 +289,5 @@ namespace osu.Game.Screens.Select
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class MetadataSection : Container
|
|
||||||
{
|
|
||||||
private readonly FillFlowContainer textContainer;
|
|
||||||
private TextFlowContainer textFlow;
|
|
||||||
|
|
||||||
public MetadataSection(string title)
|
|
||||||
{
|
|
||||||
Alpha = 0;
|
|
||||||
RelativeSizeAxes = Axes.X;
|
|
||||||
AutoSizeAxes = Axes.Y;
|
|
||||||
|
|
||||||
InternalChild = textContainer = new FillFlowContainer
|
|
||||||
{
|
|
||||||
Alpha = 0,
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
AutoSizeAxes = Axes.Y,
|
|
||||||
Spacing = new Vector2(spacing / 2),
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
new Container
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
AutoSizeAxes = Axes.Y,
|
|
||||||
Child = new OsuSpriteText
|
|
||||||
{
|
|
||||||
Text = title,
|
|
||||||
Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 14),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Text
|
|
||||||
{
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(value))
|
|
||||||
{
|
|
||||||
this.FadeOut(transition_duration);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.FadeIn(transition_duration);
|
|
||||||
|
|
||||||
setTextAsync(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setTextAsync(string text)
|
|
||||||
{
|
|
||||||
LoadComponentAsync(new OsuTextFlowContainer(s => s.Font = s.Font.With(size: 14))
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
AutoSizeAxes = Axes.Y,
|
|
||||||
Colour = Color4.White.Opacity(0.75f),
|
|
||||||
Text = text
|
|
||||||
}, loaded =>
|
|
||||||
{
|
|
||||||
textFlow?.Expire();
|
|
||||||
textContainer.Add(textFlow = loaded);
|
|
||||||
|
|
||||||
// fade in if we haven't yet.
|
|
||||||
textContainer.FadeIn(transition_duration);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ using osuTK.Graphics;
|
|||||||
namespace osu.Game.Skinning
|
namespace osu.Game.Skinning
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Compatibility methods to convert osu!stable colours to osu!lazer-compatible ones. Should be used for legacy skins only.
|
/// Compatibility methods to apply osu!stable quirks to colours. Should be used for legacy skins only.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class LegacyColourCompatibility
|
public static class LegacyColourCompatibility
|
||||||
{
|
{
|
||||||
|
@ -46,7 +46,7 @@ namespace osu.Game.Skinning
|
|||||||
public static SkinInfo Default { get; } = new SkinInfo
|
public static SkinInfo Default { get; } = new SkinInfo
|
||||||
{
|
{
|
||||||
ID = DEFAULT_SKIN,
|
ID = DEFAULT_SKIN,
|
||||||
Name = "osu!lazer",
|
Name = "osu! (triangles)",
|
||||||
Creator = "team osu!",
|
Creator = "team osu!",
|
||||||
InstantiationInfo = typeof(DefaultSkin).GetInvariantInstantiationInfo()
|
InstantiationInfo = typeof(DefaultSkin).GetInvariantInstantiationInfo()
|
||||||
};
|
};
|
||||||
|
@ -90,7 +90,7 @@ namespace osu.Game.Updater
|
|||||||
public UpdateCompleteNotification(string version)
|
public UpdateCompleteNotification(string version)
|
||||||
{
|
{
|
||||||
this.version = version;
|
this.version = version;
|
||||||
Text = $"You are now running osu!lazer {version}.\nClick to see what's new!";
|
Text = $"You are now running osu! {version}.\nClick to see what's new!";
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
|
Loading…
Reference in New Issue
Block a user