diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json
index b3f7c67c51..97fcb52ab1 100644
--- a/.config/dotnet-tools.json
+++ b/.config/dotnet-tools.json
@@ -27,7 +27,7 @@
]
},
"ppy.localisationanalyser.tools": {
- "version": "2021.608.0",
+ "version": "2021.705.0",
"commands": [
"localisation"
]
diff --git a/.editorconfig b/.editorconfig
index f4d7e08d08..19bd89c52f 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -157,7 +157,7 @@ csharp_style_unused_value_assignment_preference = discard_variable:warning
#Style - variable declaration
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
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
csharp_prefer_static_local_function = true:warning
csharp_prefer_simple_using_statement = true:silent
-csharp_style_prefer_index_operator = true:warning
-csharp_style_prefer_range_operator = true:warning
+csharp_style_prefer_index_operator = false:silent
+csharp_style_prefer_range_operator = false:silent
csharp_style_prefer_switch_expression = false:none
#Supressing roslyn built-in analyzers
diff --git a/osu.Android.props b/osu.Android.props
index 591e00fb27..9280eaf97c 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -51,8 +51,8 @@
-
-
+
+
diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj
index ad5c323e9b..53a4e5edf5 100644
--- a/osu.Desktop/osu.Desktop.csproj
+++ b/osu.Desktop/osu.Desktop.csproj
@@ -5,8 +5,8 @@
true
A free-to-win rhythm game. Rhythm is just a *click* away!
osu!
- osu!lazer
- osu!lazer
+ osu!
+ osu!
lazer.ico
app.manifest
0.0.0
diff --git a/osu.Desktop/osu.nuspec b/osu.Desktop/osu.nuspec
index fa182f8e70..1757fd7c73 100644
--- a/osu.Desktop/osu.nuspec
+++ b/osu.Desktop/osu.nuspec
@@ -3,7 +3,7 @@
osulazer
0.0.0
- osu!lazer
+ osu!
ppy Pty Ltd
Dean Herbert
https://osu.ppy.sh/
@@ -20,4 +20,3 @@
-
diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchSkinConfiguration.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchSkinConfiguration.cs
new file mode 100644
index 0000000000..ec186bcfb2
--- /dev/null
+++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchSkinConfiguration.cs
@@ -0,0 +1,114 @@
+// Copyright (c) ppy Pty Ltd . 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().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 GetConfig(TLookup lookup)
+ {
+ if (lookup is CatchSkinConfiguration config)
+ {
+ if (config == CatchSkinConfiguration.FlipCatcherPlate)
+ return SkinUtils.As(new Bindable(FlipCatcherPlate));
+ }
+
+ return base.GetConfig(lookup);
+ }
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs
index 1ad45d2f13..8359657f84 100644
--- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs
+++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs
@@ -194,9 +194,9 @@ namespace osu.Game.Rulesets.Catch.Tests
AddStep("catch more fruits", () => attemptCatch(() => new Fruit(), 9));
checkPlate(10);
AddAssert("caught objects are stacked", () =>
- catcher.CaughtObjects.All(obj => obj.Y <= Catcher.CAUGHT_FRUIT_VERTICAL_OFFSET) &&
- catcher.CaughtObjects.Any(obj => obj.Y == Catcher.CAUGHT_FRUIT_VERTICAL_OFFSET) &&
- catcher.CaughtObjects.Any(obj => obj.Y < -25));
+ catcher.CaughtObjects.All(obj => obj.Y <= 0) &&
+ catcher.CaughtObjects.Any(obj => obj.Y == 0) &&
+ catcher.CaughtObjects.Any(obj => obj.Y < 0));
}
[Test]
diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs
index 7fa981d492..e7b0259ea2 100644
--- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs
+++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs
@@ -138,7 +138,7 @@ namespace osu.Game.Rulesets.Catch.Tests
AddStep("finish hyper-dashing", () =>
{
- catcherArea.MovableCatcher.SetHyperDashState(1);
+ catcherArea.MovableCatcher.SetHyperDashState();
catcherArea.MovableCatcher.FinishTransforms();
});
diff --git a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs
index c1a491d1ce..d35d74d93d 100644
--- a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs
+++ b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs
@@ -33,11 +33,11 @@ namespace osu.Game.Rulesets.Catch.Edit
if (hitObject is BananaShower) return;
// TODO: confine in bounds
- hitObject.OriginalXBindable.Value += deltaX;
+ hitObject.OriginalX += deltaX;
// Move the nested hit objects to give an instant result before nested objects are recreated.
foreach (var nested in hitObject.NestedHitObjects.OfType())
- nested.OriginalXBindable.Value += deltaX;
+ nested.OriginalX += deltaX;
});
return true;
diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs
index ae45182960..0b8c0e28a7 100644
--- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs
+++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs
@@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using Newtonsoft.Json;
using osu.Framework.Bindables;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
@@ -20,6 +21,11 @@ namespace osu.Game.Rulesets.Catch.Objects
///
/// The horizontal position of the hit object between 0 and .
///
+ ///
+ /// Only setter is exposed.
+ /// Use or to get the horizontal position.
+ ///
+ [JsonIgnore]
public float X
{
set => OriginalXBindable.Value = value;
@@ -34,6 +40,7 @@ namespace osu.Game.Rulesets.Catch.Objects
///
public float XOffset
{
+ get => XOffsetBindable.Value;
set => XOffsetBindable.Value = value;
}
@@ -44,7 +51,11 @@ namespace osu.Game.Rulesets.Catch.Objects
/// This value is the original value specified in the beatmap, not affected by the beatmap processing.
/// Use for a gameplay.
///
- public float OriginalX => OriginalXBindable.Value;
+ public float OriginalX
+ {
+ get => OriginalXBindable.Value;
+ set => OriginalXBindable.Value = value;
+ }
///
/// The effective horizontal position of the hit object between 0 and .
@@ -53,9 +64,9 @@ namespace osu.Game.Rulesets.Catch.Objects
/// This value is the original value plus the offset applied by the beatmap processing.
/// Use if a value not affected by the offset is desired.
///
- public float EffectiveX => OriginalXBindable.Value + XOffsetBindable.Value;
+ public float EffectiveX => OriginalX + XOffset;
- public double TimePreempt = 1000;
+ public double TimePreempt { get; set; } = 1000;
public readonly Bindable IndexInBeatmapBindable = new Bindable();
diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
index 35fd58826e..3088d024d1 100644
--- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
+++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
+using Newtonsoft.Json;
using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
@@ -25,7 +26,10 @@ namespace osu.Game.Rulesets.Catch.Objects
public int RepeatCount { get; set; }
+ [JsonIgnore]
public double Velocity { get; private set; }
+
+ [JsonIgnore]
public double TickDistance { get; private set; }
///
@@ -113,6 +117,7 @@ namespace osu.Game.Rulesets.Catch.Objects
public float EndX => OriginalX + this.CurvePositionAt(1).X;
+ [JsonIgnore]
public double Duration
{
get => this.SpanCount() * Path.Distance / Velocity;
diff --git a/osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.cs
index 0cd3af01df..aa7cabf38b 100644
--- a/osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.cs
+++ b/osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
+using Newtonsoft.Json;
using osu.Framework.Bindables;
using osu.Game.Rulesets.Objects.Types;
using osuTK.Graphics;
@@ -33,6 +34,7 @@ namespace osu.Game.Rulesets.Catch.Objects
///
/// The target fruit if we are to initiate a hyperdash.
///
+ [JsonIgnore]
public CatchHitObject HyperDashTarget
{
get => hyperDashTarget;
diff --git a/osu.Game.Rulesets.Catch/Skinning/CatchSkinConfiguration.cs b/osu.Game.Rulesets.Catch/Skinning/CatchSkinConfiguration.cs
new file mode 100644
index 0000000000..ea8d742b1a
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/Skinning/CatchSkinConfiguration.cs
@@ -0,0 +1,13 @@
+// Copyright (c) ppy Pty Ltd . 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
+ {
+ ///
+ /// Whether the contents of the catcher plate should be visually flipped when the catcher direction is changed.
+ ///
+ FlipCatcherPlate
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs
index 287ed1b4c7..5e744ec001 100644
--- a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs
+++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs
@@ -103,6 +103,19 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy
result.Value = LegacyColourCompatibility.DisallowZeroAlpha(result.Value);
return (IBindable)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)new Bindable();
+
+ break;
+ }
+
+ break;
}
return base.GetConfig(lookup);
diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs
index dcab9459ee..57523d3505 100644
--- a/osu.Game.Rulesets.Catch/UI/Catcher.cs
+++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs
@@ -56,11 +56,6 @@ namespace osu.Game.Rulesets.Catch.UI
///
public double Speed => (Dashing ? 1 : 0.5) * BASE_SPEED * hyperDashModifier;
- ///
- /// The amount by which caught fruit should be offset from the plate surface to make them look visually "caught".
- ///
- public const float CAUGHT_FRUIT_VERTICAL_OFFSET = -5;
-
///
/// The amount by which caught fruit should be scaled down to fit on the plate.
///
@@ -84,8 +79,8 @@ namespace osu.Game.Rulesets.Catch.UI
public CatcherAnimationState CurrentState
{
- get => body.AnimationState.Value;
- private set => body.AnimationState.Value = value;
+ get => Body.AnimationState.Value;
+ private set => Body.AnimationState.Value = value;
}
///
@@ -108,18 +103,22 @@ namespace osu.Game.Rulesets.Catch.UI
}
}
- public Direction VisualDirection
- {
- get => Scale.X > 0 ? Direction.Right : Direction.Left;
- set => Scale = new Vector2((value == Direction.Right ? 1 : -1) * Math.Abs(Scale.X), Scale.Y);
- }
+ ///
+ /// The currently facing direction.
+ ///
+ public Direction VisualDirection { get; set; } = Direction.Right;
+
+ ///
+ /// Whether the contents of the catcher plate should be visually flipped when the catcher direction is changed.
+ ///
+ private bool flipCatcherPlate;
///
/// Width of the area that can be used to attempt catches during gameplay.
///
private readonly float catchWidth;
- private readonly SkinnableCatcher body;
+ internal readonly SkinnableCatcher Body;
private Color4 hyperDashColour = DEFAULT_HYPER_DASH_COLOUR;
private Color4 hyperDashEndGlowColour = DEFAULT_HYPER_DASH_COLOUR;
@@ -157,8 +156,10 @@ namespace osu.Game.Rulesets.Catch.UI
{
Anchor = Anchor.TopCentre,
Origin = Anchor.BottomCentre,
+ // offset fruit vertically to better place "above" the plate.
+ Y = -5
},
- body = new SkinnableCatcher(),
+ Body = new SkinnableCatcher(),
hitExplosionContainer = new HitExplosionContainer
{
Anchor = Anchor.TopCentre,
@@ -347,6 +348,8 @@ namespace osu.Game.Rulesets.Catch.UI
trails.HyperDashTrailsColour = hyperDashColour;
trails.EndGlowSpritesColour = hyperDashEndGlowColour;
+ flipCatcherPlate = skin.GetConfig(CatchSkinConfiguration.FlipCatcherPlate)?.Value ?? true;
+
runHyperDashStateTransition(HyperDashing);
}
@@ -354,6 +357,10 @@ namespace osu.Game.Rulesets.Catch.UI
{
base.Update();
+ var scaleFromDirection = new Vector2((int)VisualDirection, 1);
+ Body.Scale = scaleFromDirection;
+ caughtObjectContainer.Scale = hitExplosionContainer.Scale = flipCatcherPlate ? scaleFromDirection : Vector2.One;
+
// Correct overshooting.
if ((hyperDashDirection > 0 && hyperDashTargetPosition < X) ||
(hyperDashDirection < 0 && hyperDashTargetPosition > X))
@@ -388,9 +395,6 @@ namespace osu.Game.Rulesets.Catch.UI
float adjustedRadius = displayRadius * lenience_adjust;
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))
{
position.X += RNG.NextSingle(-adjustedRadius, adjustedRadius);
@@ -465,7 +469,7 @@ namespace osu.Game.Rulesets.Catch.UI
break;
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.MoveToX(d.X + originalX * 6, 1000);
d.FadeOut(750);
diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs
index 7e4a5b6a86..b59fabcb70 100644
--- a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs
+++ b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs
@@ -121,7 +121,7 @@ namespace osu.Game.Rulesets.Catch.UI
CatcherTrail sprite = trailPool.Get();
sprite.AnimationState = catcher.CurrentState;
- sprite.Scale = catcher.Scale;
+ sprite.Scale = catcher.Scale * catcher.Body.Scale;
sprite.Position = catcher.Position;
target.Add(sprite);
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs
index 78bb88322a..2326a0c391 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs
@@ -121,7 +121,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
case OsuSkinConfiguration osuLookup:
if (osuLookup == OsuSkinConfiguration.CursorCentre)
- return SkinUtils.As(new BindableBool(false));
+ return SkinUtils.As(new BindableBool());
break;
}
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs
index ebf6f9dda7..636cd63c69 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs
@@ -158,17 +158,17 @@ namespace osu.Game.Rulesets.Osu.Mods
var firstObj = beatmap.HitObjects[0];
var startDelay = firstObj.StartTime - firstObj.TimePreempt;
- using (BeginAbsoluteSequence(startDelay + break_close_late, true))
+ using (BeginAbsoluteSequence(startDelay + break_close_late))
leaveBreak();
foreach (var breakInfo in beatmap.Breaks)
{
if (breakInfo.HasEffect)
{
- using (BeginAbsoluteSequence(breakInfo.StartTime - break_open_early, true))
+ using (BeginAbsoluteSequence(breakInfo.StartTime - break_open_early))
{
enterBreak();
- using (BeginDelayedSequence(breakInfo.Duration + break_open_early + break_close_late, true))
+ using (BeginDelayedSequence(breakInfo.Duration + break_open_early + break_close_late))
leaveBreak();
}
}
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs b/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs
index 56c246953e..95e7d13ee7 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs
@@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Osu.Mods
switch (drawable)
{
case DrawableHitCircle circle:
- using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true))
+ using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt))
{
circle.ApproachCircle.Hide();
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs
index a05e4dea03..07ce009cf8 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs
@@ -60,7 +60,7 @@ namespace osu.Game.Rulesets.Osu.Mods
private void applyCirclePieceState(DrawableOsuHitObject hitObject, IDrawable hitCircle = null)
{
var h = hitObject.HitObject;
- using (hitObject.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true))
+ using (hitObject.BeginAbsoluteSequence(h.StartTime - h.TimePreempt))
(hitCircle ?? hitObject).Hide();
}
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs
index b5905d7015..8122ab563e 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs
@@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.Mods
double appearTime = hitObject.StartTime - hitObject.TimePreempt - 1;
double moveDuration = hitObject.TimePreempt + 1;
- using (drawable.BeginAbsoluteSequence(appearTime, true))
+ using (drawable.BeginAbsoluteSequence(appearTime))
{
drawable
.MoveToOffset(appearOffset)
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs b/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs
index a01cec4bb3..ff6ba6e121 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs
@@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Osu.Mods
for (int i = 0; i < amountWiggles; i++)
{
- using (drawable.BeginAbsoluteSequence(osuObject.StartTime - osuObject.TimePreempt + i * wiggle_duration, true))
+ using (drawable.BeginAbsoluteSequence(osuObject.StartTime - osuObject.TimePreempt + i * wiggle_duration))
wiggle();
}
@@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Osu.Mods
for (int i = 0; i < amountWiggles; i++)
{
- using (drawable.BeginAbsoluteSequence(osuObject.StartTime + i * wiggle_duration, true))
+ using (drawable.BeginAbsoluteSequence(osuObject.StartTime + i * wiggle_duration))
wiggle();
}
}
diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs
index 7b0cf651c8..b88bf9108b 100644
--- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs
+++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs
@@ -233,35 +233,43 @@ namespace osu.Game.Rulesets.Osu.Replays
// Wait until Auto could "see and react" to the next note.
double waitTime = h.StartTime - Math.Max(0.0, h.TimePreempt - getReactionTime(h.StartTime - h.TimePreempt));
+ bool hasWaited = false;
if (waitTime > lastFrame.Time)
{
lastFrame = new OsuReplayFrame(waitTime, lastFrame.Position) { Actions = lastFrame.Actions };
+ hasWaited = true;
AddFrameToReplay(lastFrame);
}
- Vector2 lastPosition = lastFrame.Position;
-
double timeDifference = ApplyModsToTimeDelta(lastFrame.Time, h.StartTime);
+ OsuReplayFrame lastLastFrame = Frames.Count >= 2 ? (OsuReplayFrame)Frames[^2] : null;
- // Only "snap" to hitcircles if they are far enough apart. As the time between hitcircles gets shorter the snapping threshold goes up.
- if (timeDifference > 0 && // Sanity checks
- ((lastPosition - targetPos).Length > h.Radius * (1.5 + 100.0 / timeDifference) || // Either the distance is big enough
- timeDifference >= 266)) // ... or the beats are slow enough to tap anyway.
+ if (timeDifference > 0)
{
- // Perform eased movement
+ // If the last frame is a key-up frame and there has been no wait period, adjust the last frame's position such that it begins eased movement instantaneously.
+ if (lastLastFrame != null && lastFrame is OsuKeyUpReplayFrame && !hasWaited)
+ {
+ // [lastLastFrame] ... [lastFrame] ... [current frame]
+ // We want to find the cursor position at lastFrame, so interpolate between lastLastFrame and the new target position.
+ lastFrame.Position = Interpolation.ValueAt(lastFrame.Time, lastFrame.Position, targetPos, lastLastFrame.Time, h.StartTime, easing);
+ }
+
+ Vector2 lastPosition = lastFrame.Position;
+
+ // Perform the rest of the eased movement until the target position is reached.
for (double time = lastFrame.Time + GetFrameDelay(lastFrame.Time); time < h.StartTime; time += GetFrameDelay(time))
{
Vector2 currentPosition = Interpolation.ValueAt(time, lastPosition, targetPos, lastFrame.Time, h.StartTime, easing);
AddFrameToReplay(new OsuReplayFrame((int)time, new Vector2(currentPosition.X, currentPosition.Y)) { Actions = lastFrame.Actions });
}
+ }
- buttonIndex = 0;
- }
- else
- {
+ // Start alternating once the time separation is too small (faster than ~225BPM).
+ if (timeDifference > 0 && timeDifference < 266)
buttonIndex++;
- }
+ else
+ buttonIndex = 0;
}
///
@@ -284,7 +292,7 @@ namespace osu.Game.Rulesets.Osu.Replays
// TODO: Why do we delay 1 ms if the object is a spinner? There already is KEY_UP_DELAY from hEndTime.
double hEndTime = h.GetEndTime() + KEY_UP_DELAY;
int endDelay = h is Spinner ? 1 : 0;
- var endFrame = new OsuReplayFrame(hEndTime + endDelay, new Vector2(h.StackedEndPosition.X, h.StackedEndPosition.Y));
+ var endFrame = new OsuKeyUpReplayFrame(hEndTime + endDelay, new Vector2(h.StackedEndPosition.X, h.StackedEndPosition.Y));
// Decrement because we want the previous frame, not the next one
int index = FindInsertionIndex(startFrame) - 1;
@@ -381,5 +389,13 @@ namespace osu.Game.Rulesets.Osu.Replays
}
#endregion
+
+ private class OsuKeyUpReplayFrame : OsuReplayFrame
+ {
+ public OsuKeyUpReplayFrame(double time, Vector2 position)
+ : base(time, position)
+ {
+ }
+ }
}
}
diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSpinnerDisc.cs b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSpinnerDisc.cs
index 542f3eff0d..4ea0831627 100644
--- a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSpinnerDisc.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSpinnerDisc.cs
@@ -130,18 +130,18 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
Spinner spinner = drawableSpinner.HitObject;
- using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true))
+ using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt))
{
this.ScaleTo(initial_scale);
this.RotateTo(0);
- using (BeginDelayedSequence(spinner.TimePreempt / 2, true))
+ using (BeginDelayedSequence(spinner.TimePreempt / 2))
{
// constant ambient rotation to give the spinner "spinning" character.
this.RotateTo((float)(25 * spinner.Duration / 2000), spinner.TimePreempt + spinner.Duration);
}
- using (BeginDelayedSequence(spinner.TimePreempt + spinner.Duration + drawableHitObject.Result.TimeOffset, true))
+ using (BeginDelayedSequence(spinner.TimePreempt + spinner.Duration + drawableHitObject.Result.TimeOffset))
{
switch (state)
{
@@ -157,17 +157,17 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
}
}
- using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true))
+ using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt))
{
centre.ScaleTo(0);
mainContainer.ScaleTo(0);
- using (BeginDelayedSequence(spinner.TimePreempt / 2, true))
+ using (BeginDelayedSequence(spinner.TimePreempt / 2))
{
centre.ScaleTo(0.3f, spinner.TimePreempt / 4, Easing.OutQuint);
mainContainer.ScaleTo(0.2f, spinner.TimePreempt / 4, Easing.OutQuint);
- using (BeginDelayedSequence(spinner.TimePreempt / 2, true))
+ using (BeginDelayedSequence(spinner.TimePreempt / 2))
{
centre.ScaleTo(0.5f, spinner.TimePreempt / 2, Easing.OutQuint);
mainContainer.ScaleTo(1, spinner.TimePreempt / 2, Easing.OutQuint);
@@ -176,7 +176,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
}
// transforms we have from completing the spinner will be rolled back, so reapply immediately.
- using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true))
+ using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt))
updateComplete(state == ArmedState.Hit, 0);
}
diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/SliderBall.cs b/osu.Game.Rulesets.Osu/Skinning/Default/SliderBall.cs
index 8feeca56e8..8943a91076 100644
--- a/osu.Game.Rulesets.Osu/Skinning/Default/SliderBall.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Default/SliderBall.cs
@@ -86,6 +86,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
public override void ApplyTransformsAt(double time, bool propagateChildren = false)
{
// For the same reasons as above w.r.t rewinding, we shouldn't propagate to children here either.
+ // ReSharper disable once RedundantArgumentDefaultValue - removing the "redundant" default value triggers BaseMethodCallWithDefaultParameter
base.ApplyTransformsAt(time, false);
}
diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyNewStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyNewStyleSpinner.cs
index ae8d6a61f8..1e170036e4 100644
--- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyNewStyleSpinner.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyNewStyleSpinner.cs
@@ -100,17 +100,17 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
case DrawableSpinner d:
Spinner spinner = d.HitObject;
- using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true))
+ using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt))
this.FadeOut();
- using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimeFadeIn / 2, true))
+ using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimeFadeIn / 2))
this.FadeInFromZero(spinner.TimeFadeIn / 2);
- using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true))
+ using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt))
{
fixedMiddle.FadeColour(Color4.White);
- using (BeginDelayedSequence(spinner.TimePreempt, true))
+ using (BeginDelayedSequence(spinner.TimePreempt))
fixedMiddle.FadeColour(Color4.Red, spinner.Duration);
}
diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyOldStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyOldStyleSpinner.cs
index cbe721d21d..e3e8f3ce88 100644
--- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyOldStyleSpinner.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyOldStyleSpinner.cs
@@ -89,10 +89,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
Spinner spinner = d.HitObject;
- using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true))
+ using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt))
this.FadeOut();
- using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimeFadeIn / 2, true))
+ using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimeFadeIn / 2))
this.FadeInFromZero(spinner.TimeFadeIn / 2);
}
diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySpinner.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySpinner.cs
index 317649785e..93aba608e6 100644
--- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySpinner.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySpinner.cs
@@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
///
/// 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)
///
protected const float SPINNER_TOP_OFFSET = 45f - 16f;
@@ -140,7 +140,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
{
double startTime = Math.Min(Time.Current, DrawableSpinner.HitStateUpdateTime - 400);
- using (BeginAbsoluteSequence(startTime, true))
+ using (BeginAbsoluteSequence(startTime))
{
clear.FadeInFromZero(400, Easing.Out);
@@ -150,7 +150,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
}
const double fade_out_duration = 50;
- using (BeginAbsoluteSequence(DrawableSpinner.HitStateUpdateTime - fade_out_duration, true))
+ using (BeginAbsoluteSequence(DrawableSpinner.HitStateUpdateTime - fade_out_duration))
clear.FadeOut(fade_out_duration);
}
else
@@ -182,14 +182,14 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
double spinFadeOutLength = Math.Min(400, d.HitObject.Duration);
- using (BeginAbsoluteSequence(drawableHitObject.HitStateUpdateTime - spinFadeOutLength, true))
+ using (BeginAbsoluteSequence(drawableHitObject.HitStateUpdateTime - spinFadeOutLength))
spin.FadeOutFromOne(spinFadeOutLength);
break;
case DrawableSpinnerTick d:
if (state == ArmedState.Hit)
{
- using (BeginAbsoluteSequence(d.HitStateUpdateTime, true))
+ using (BeginAbsoluteSequence(d.HitStateUpdateTime))
spin.FadeOut(300);
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs
index 60f9521996..888f47d341 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs
@@ -227,7 +227,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
base.UpdateStartTimeStateTransforms();
- using (BeginDelayedSequence(-ring_appear_offset, true))
+ using (BeginDelayedSequence(-ring_appear_offset))
targetRing.ScaleTo(target_ring_scale, 400, Easing.OutQuint);
}
diff --git a/osu.Game.Tests/Beatmaps/SliderEventGenerationTest.cs b/osu.Game.Tests/Beatmaps/SliderEventGenerationTest.cs
index 6c8133660f..9fba0f1668 100644
--- a/osu.Game.Tests/Beatmaps/SliderEventGenerationTest.cs
+++ b/osu.Game.Tests/Beatmaps/SliderEventGenerationTest.cs
@@ -16,7 +16,7 @@ namespace osu.Game.Tests.Beatmaps
[Test]
public void TestSingleSpan()
{
- var events = SliderEventGenerator.Generate(start_time, span_duration, 1, span_duration / 2, span_duration, 1, null, default).ToArray();
+ var events = SliderEventGenerator.Generate(start_time, span_duration, 1, span_duration / 2, span_duration, 1, null).ToArray();
Assert.That(events[0].Type, Is.EqualTo(SliderEventType.Head));
Assert.That(events[0].Time, Is.EqualTo(start_time));
@@ -31,7 +31,7 @@ namespace osu.Game.Tests.Beatmaps
[Test]
public void TestRepeat()
{
- var events = SliderEventGenerator.Generate(start_time, span_duration, 1, span_duration / 2, span_duration, 2, null, default).ToArray();
+ var events = SliderEventGenerator.Generate(start_time, span_duration, 1, span_duration / 2, span_duration, 2, null).ToArray();
Assert.That(events[0].Type, Is.EqualTo(SliderEventType.Head));
Assert.That(events[0].Time, Is.EqualTo(start_time));
@@ -52,7 +52,7 @@ namespace osu.Game.Tests.Beatmaps
[Test]
public void TestNonEvenTicks()
{
- var events = SliderEventGenerator.Generate(start_time, span_duration, 1, 300, span_duration, 2, null, default).ToArray();
+ var events = SliderEventGenerator.Generate(start_time, span_duration, 1, 300, span_duration, 2, null).ToArray();
Assert.That(events[0].Type, Is.EqualTo(SliderEventType.Head));
Assert.That(events[0].Time, Is.EqualTo(start_time));
@@ -85,7 +85,7 @@ namespace osu.Game.Tests.Beatmaps
[Test]
public void TestLegacyLastTickOffset()
{
- var events = SliderEventGenerator.Generate(start_time, span_duration, 1, span_duration / 2, span_duration, 1, 100, default).ToArray();
+ var events = SliderEventGenerator.Generate(start_time, span_duration, 1, span_duration / 2, span_duration, 1, 100).ToArray();
Assert.That(events[2].Type, Is.EqualTo(SliderEventType.LegacyLastTick));
Assert.That(events[2].Time, Is.EqualTo(900));
@@ -97,7 +97,7 @@ namespace osu.Game.Tests.Beatmaps
const double velocity = 5;
const double min_distance = velocity * 10;
- var events = SliderEventGenerator.Generate(start_time, span_duration, velocity, velocity, span_duration, 2, 0, default).ToArray();
+ var events = SliderEventGenerator.Generate(start_time, span_duration, velocity, velocity, span_duration, 2, 0).ToArray();
Assert.Multiple(() =>
{
diff --git a/osu.Game.Tests/Database/TestRealmKeyBindingStore.cs b/osu.Game.Tests/Database/TestRealmKeyBindingStore.cs
index cac331451b..642ecf00b8 100644
--- a/osu.Game.Tests/Database/TestRealmKeyBindingStore.cs
+++ b/osu.Game.Tests/Database/TestRealmKeyBindingStore.cs
@@ -38,19 +38,28 @@ namespace osu.Game.Tests.Database
[Test]
public void TestDefaultsPopulationAndQuery()
{
- Assert.That(query().Count, Is.EqualTo(0));
+ Assert.That(queryCount(), Is.EqualTo(0));
KeyBindingContainer testContainer = new TestKeyBindingContainer();
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(query().Where(k => k.ActionInt == (int)GlobalAction.Select).Count, Is.EqualTo(2));
+ Assert.That(queryCount(GlobalAction.Back), Is.EqualTo(1));
+ Assert.That(queryCount(GlobalAction.Select), Is.EqualTo(2));
}
- private IQueryable query() => realmContextFactory.Context.All();
+ private int queryCount(GlobalAction? match = null)
+ {
+ using (var usage = realmContextFactory.GetForRead())
+ {
+ var results = usage.Realm.All();
+ if (match.HasValue)
+ results = results.Where(k => k.ActionInt == (int)match.Value);
+ return results.Count();
+ }
+ }
[Test]
public void TestUpdateViaQueriedReference()
@@ -59,25 +68,28 @@ namespace osu.Game.Tests.Database
keyBindingStore.Register(testContainer);
- var backBinding = query().Single(k => k.ActionInt == (int)GlobalAction.Back);
-
- Assert.That(backBinding.KeyCombination.Keys, Is.EquivalentTo(new[] { InputKey.Escape }));
-
- var tsr = ThreadSafeReference.Create(backBinding);
-
- using (var usage = realmContextFactory.GetForWrite())
+ using (var primaryUsage = realmContextFactory.GetForRead())
{
- var binding = usage.Realm.ResolveReference(tsr);
- binding.KeyCombination = new KeyCombination(InputKey.BackSpace);
+ var backBinding = primaryUsage.Realm.All().Single(k => k.ActionInt == (int)GlobalAction.Back);
- usage.Commit();
+ Assert.That(backBinding.KeyCombination.Keys, Is.EquivalentTo(new[] { InputKey.Escape }));
+
+ var tsr = ThreadSafeReference.Create(backBinding);
+
+ using (var usage = realmContextFactory.GetForWrite())
+ {
+ var binding = usage.Realm.ResolveReference(tsr);
+ binding.KeyCombination = new KeyCombination(InputKey.BackSpace);
+
+ usage.Commit();
+ }
+
+ Assert.That(backBinding.KeyCombination.Keys, Is.EquivalentTo(new[] { InputKey.BackSpace }));
+
+ // check still correct after re-query.
+ backBinding = primaryUsage.Realm.All().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 }));
-
- // check still correct after re-query.
- backBinding = query().Single(k => k.ActionInt == (int)GlobalAction.Back);
- Assert.That(backBinding.KeyCombination.Keys, Is.EquivalentTo(new[] { InputKey.BackSpace }));
}
[TearDown]
diff --git a/osu.Game.Tests/Localisation/BeatmapMetadataRomanisationTest.cs b/osu.Game.Tests/Localisation/BeatmapMetadataRomanisationTest.cs
new file mode 100644
index 0000000000..dab4825919
--- /dev/null
+++ b/osu.Game.Tests/Localisation/BeatmapMetadataRomanisationTest.cs
@@ -0,0 +1,41 @@
+// Copyright (c) ppy Pty Ltd . 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);
+ }
+ }
+}
diff --git a/osu.Game.Tests/NonVisual/FirstAvailableHitWindowsTest.cs b/osu.Game.Tests/NonVisual/FirstAvailableHitWindowsTest.cs
new file mode 100644
index 0000000000..97105b6b6a
--- /dev/null
+++ b/osu.Game.Tests/NonVisual/FirstAvailableHitWindowsTest.cs
@@ -0,0 +1,123 @@
+// Copyright (c) ppy Pty Ltd . 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 { 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 { 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 { 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 { firstObject };
+
+ Assert.IsNull(testDrawableRuleset.FirstAvailableHitWindows);
+ }
+
+ [SuppressMessage("ReSharper", "UnassignedGetOnlyAutoProperty")]
+ private class TestDrawableRuleset : DrawableRuleset
+ {
+ public List HitObjects;
+ public override IEnumerable Objects => HitObjects;
+
+ public override event Action NewResult;
+ public override event Action 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 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);
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs
index 4ee48fd853..11bd701e19 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs
@@ -114,11 +114,6 @@ namespace osu.Game.Tests.Visual.Gameplay
{
public bool ResultsCreated { get; private set; }
- public FakeRankingPushPlayer()
- : base(true, true)
- {
- }
-
protected override ResultsScreen CreateResults(ScoreInfo score)
{
var results = base.CreateResults(score);
diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs
index d80fbfe309..d9c0544d3c 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs
@@ -9,6 +9,8 @@ using osu.Game.Online.Rooms;
using osu.Game.Online.Solo;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Osu.Judgements;
+using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Ranking;
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 track to start running", () => Beatmap.Value.Track.IsRunning);
+
+ addFakeHit();
+
AddStep("seek to completion", () => Player.GameplayClockContainer.Seek(Player.DrawableRuleset.Objects.Last().GetEndTime()));
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 track to start running", () => Beatmap.Value.Track.IsRunning);
+
+ addFakeHit();
+
AddStep("seek to completion", () => Player.GameplayClockContainer.Seek(Player.DrawableRuleset.Objects.Last().GetEndTime()));
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 track to start running", () => Beatmap.Value.Track.IsRunning);
+
+ addFakeHit();
+
AddStep("exit", () => Player.Exit());
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]
public void TestSubmissionOnFail()
{
@@ -79,12 +106,28 @@ namespace osu.Game.Tests.Visual.Gameplay
CreateTest(() => allowFail = true);
AddUntilStep("wait for token request", () => Player.TokenCreationRequested);
+
+ addFakeHit();
+
AddUntilStep("wait for fail", () => Player.HasFailed);
AddStep("exit", () => Player.Exit());
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]
public void TestSubmissionOnExit()
{
@@ -93,10 +136,27 @@ namespace osu.Game.Tests.Visual.Gameplay
CreateTest(() => allowFail = false);
AddUntilStep("wait for token request", () => Player.TokenCreationRequested);
+
+ addFakeHit();
+
AddStep("exit", () => Player.Exit());
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)
{
AddStep("Prepare test API", () =>
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs
index f29fbbf52b..02b1959dab 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs
@@ -75,7 +75,7 @@ namespace osu.Game.Tests.Visual.Gameplay
Children = new[]
{
new ExposedSkinnableDrawable("default", _ => new DefaultBox()),
- new ExposedSkinnableDrawable("available", _ => new DefaultBox(), ConfineMode.ScaleToFit),
+ new ExposedSkinnableDrawable("available", _ => new DefaultBox()),
new ExposedSkinnableDrawable("available", _ => new DefaultBox(), ConfineMode.NoScaling)
}
},
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs
index 469f594fdc..bb577886cc 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs
@@ -179,7 +179,7 @@ namespace osu.Game.Tests.Visual.Gameplay
foreach (var legacyFrame in frames.Frames)
{
var frame = new TestReplayFrame();
- frame.FromLegacy(legacyFrame, null, null);
+ frame.FromLegacy(legacyFrame, null);
replay.Frames.Add(frame);
}
}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs
index b8db4067fb..072e32370d 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs
@@ -137,7 +137,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
// Send initial frames for both players. A few more for player 1.
sendFrames(PLAYER_1_ID, 20);
- sendFrames(PLAYER_2_ID, 10);
+ sendFrames(PLAYER_2_ID);
checkPausedInstant(PLAYER_1_ID, false);
checkPausedInstant(PLAYER_2_ID, false);
@@ -194,7 +194,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
assertMuted(PLAYER_1_ID, true);
assertMuted(PLAYER_2_ID, true);
- sendFrames(PLAYER_1_ID, 10);
+ sendFrames(PLAYER_1_ID);
sendFrames(PLAYER_2_ID, 20);
checkPaused(PLAYER_1_ID, false);
assertOneNotMuted();
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs
index c93640e7b5..2bb3129f68 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs
@@ -11,6 +11,7 @@ using osu.Framework.Platform;
using osu.Framework.Screens;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
+using osu.Game.Database;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Rooms;
@@ -39,10 +40,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
private TestMultiplayer multiplayerScreen;
private TestMultiplayerClient client;
- public TestSceneMultiplayer()
- {
- loadMultiplayer();
- }
+ [Cached(typeof(UserLookupCache))]
+ private UserLookupCache lookupCache = new TestUserLookupCache();
[BackgroundDependencyLoader]
private void load(GameHost host, AudioManager audio)
@@ -51,18 +50,43 @@ namespace osu.Game.Tests.Visual.Multiplayer
Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default));
}
- [SetUp]
- public void Setup() => Schedule(() =>
+ public override void SetUpSteps()
{
- beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).Wait();
- importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First();
- });
+ base.SetUpSteps();
+
+ AddStep("import beatmap", () =>
+ {
+ beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).Wait();
+ importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First();
+ });
+
+ AddStep("create multiplayer screen", () => multiplayerScreen = new TestMultiplayer());
+
+ AddStep("load dependencies", () =>
+ {
+ client = new TestMultiplayerClient(multiplayerScreen.RoomManager);
+
+ // The screen gets suspended so it stops receiving updates.
+ Child = client;
+
+ LoadScreen(dependenciesScreen = new DependenciesScreen(client));
+ });
+
+ AddUntilStep("wait for dependencies to load", () => dependenciesScreen.IsLoaded);
+
+ AddStep("load multiplayer", () => LoadScreen(multiplayerScreen));
+ AddUntilStep("wait for multiplayer to load", () => multiplayerScreen.IsLoaded);
+ }
+
+ [Test]
+ public void TestEmpty()
+ {
+ // used to test the flow of multiplayer from visual tests.
+ }
[Test]
public void TestUserSetToIdleWhenBeatmapDeleted()
{
- loadMultiplayer();
-
createRoom(() => new Room
{
Name = { Value = "Test Room" },
@@ -85,8 +109,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Test]
public void TestLocalPlayDoesNotStartWhileSpectatingWithNoBeatmap()
{
- loadMultiplayer();
-
createRoom(() => new Room
{
Name = { Value = "Test Room" },
@@ -123,8 +145,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Test]
public void TestLocalPlayStartsWhileSpectatingWhenBeatmapBecomesAvailable()
{
- loadMultiplayer();
-
createRoom(() => new Room
{
Name = { Value = "Test Room" },
@@ -167,8 +187,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Test]
public void TestLeaveNavigation()
{
- loadMultiplayer();
-
createRoom(() => new Room
{
Name = { Value = "Test Room" },
@@ -227,26 +245,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddUntilStep("wait for join", () => client.Room != null);
}
- private void loadMultiplayer()
- {
- AddStep("create multiplayer screen", () => multiplayerScreen = new TestMultiplayer());
-
- AddStep("load dependencies", () =>
- {
- client = new TestMultiplayerClient(multiplayerScreen.RoomManager);
-
- // The screen gets suspended so it stops receiving updates.
- Child = client;
-
- LoadScreen(dependenciesScreen = new DependenciesScreen(client));
- });
-
- AddUntilStep("wait for dependencies to load", () => dependenciesScreen.IsLoaded);
-
- AddStep("load multiplayer", () => LoadScreen(multiplayerScreen));
- AddUntilStep("wait for multiplayer to load", () => multiplayerScreen.IsLoaded);
- }
-
///
/// Used for the sole purpose of adding as a resolvable dependency.
///
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs
index 0e036e8868..820b403a10 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs
@@ -113,10 +113,10 @@ namespace osu.Game.Tests.Visual.Multiplayer
});
addClickButtonStep();
- AddAssert("user is ready", () => Client.Room?.Users[0].State == MultiplayerUserState.Ready);
+ AddUntilStep("user is ready", () => Client.Room?.Users[0].State == MultiplayerUserState.Ready);
addClickButtonStep();
- AddAssert("user is idle", () => Client.Room?.Users[0].State == MultiplayerUserState.Idle);
+ AddUntilStep("user is idle", () => Client.Room?.Users[0].State == MultiplayerUserState.Idle);
}
[TestCase(true)]
@@ -132,7 +132,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
});
addClickButtonStep();
- AddAssert("user is ready", () => Client.Room?.Users[0].State == MultiplayerUserState.Ready);
+ AddUntilStep("user is ready", () => Client.Room?.Users[0].State == MultiplayerUserState.Ready);
verifyGameplayStartFlow();
}
@@ -206,8 +206,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
private void verifyGameplayStartFlow()
{
+ AddUntilStep("user is ready", () => Client.Room?.Users[0].State == MultiplayerUserState.Ready);
addClickButtonStep();
- AddAssert("user waiting for load", () => Client.Room?.Users[0].State == MultiplayerUserState.WaitingForLoad);
+ AddUntilStep("user waiting for load", () => Client.Room?.Users[0].State == MultiplayerUserState.WaitingForLoad);
AddAssert("ready button disabled", () => !button.ChildrenOfType().Single().Enabled.Value);
AddStep("transitioned to gameplay", () => readyClickOperation.Dispose());
@@ -218,7 +219,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
Client.ChangeUserState(Client.Room?.Users[0].UserID ?? 0, MultiplayerUserState.FinishedPlay);
});
- AddAssert("ready button enabled", () => button.ChildrenOfType().Single().Enabled.Value);
+ AddUntilStep("ready button enabled", () => button.ChildrenOfType().Single().Enabled.Value);
}
}
}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs
index 4966dfbe50..3d08d5da9e 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs
@@ -122,10 +122,10 @@ namespace osu.Game.Tests.Visual.Multiplayer
public void TestToggleWhenIdle(MultiplayerUserState initialState)
{
addClickSpectateButtonStep();
- AddAssert("user is spectating", () => Client.Room?.Users[0].State == MultiplayerUserState.Spectating);
+ AddUntilStep("user is spectating", () => Client.Room?.Users[0].State == MultiplayerUserState.Spectating);
addClickSpectateButtonStep();
- AddAssert("user is idle", () => Client.Room?.Users[0].State == MultiplayerUserState.Idle);
+ AddUntilStep("user is idle", () => Client.Room?.Users[0].State == MultiplayerUserState.Idle);
}
[TestCase(MultiplayerRoomState.Closed)]
@@ -174,9 +174,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
});
private void assertSpectateButtonEnablement(bool shouldBeEnabled)
- => AddAssert($"spectate button {(shouldBeEnabled ? "is" : "is not")} enabled", () => spectateButton.ChildrenOfType().Single().Enabled.Value == shouldBeEnabled);
+ => AddUntilStep($"spectate button {(shouldBeEnabled ? "is" : "is not")} enabled", () => spectateButton.ChildrenOfType().Single().Enabled.Value == shouldBeEnabled);
private void assertReadyButtonEnablement(bool shouldBeEnabled)
- => AddAssert($"ready button {(shouldBeEnabled ? "is" : "is not")} enabled", () => readyButton.ChildrenOfType().Single().Enabled.Value == shouldBeEnabled);
+ => AddUntilStep($"ready button {(shouldBeEnabled ? "is" : "is not")} enabled", () => readyButton.ChildrenOfType().Single().Enabled.Value == shouldBeEnabled);
}
}
diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs
index 06572f66bf..b4544fbc85 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs
@@ -39,7 +39,7 @@ namespace osu.Game.Tests.Visual.SongSelect
Version = "All Metrics",
Metadata = new BeatmapMetadata
{
- Source = "osu!lazer",
+ Source = "osu!",
Tags = "this beatmap has all the metrics",
},
BaseDifficulty = new BeatmapDifficulty
@@ -100,7 +100,7 @@ namespace osu.Game.Tests.Visual.SongSelect
Version = "Only Ratings",
Metadata = new BeatmapMetadata
{
- Source = "osu!lazer",
+ Source = "osu!",
Tags = "this beatmap has ratings metrics but not retries or fails",
},
BaseDifficulty = new BeatmapDifficulty
@@ -122,7 +122,7 @@ namespace osu.Game.Tests.Visual.SongSelect
Version = "Only Retries and Fails",
Metadata = new BeatmapMetadata
{
- Source = "osu!lazer",
+ Source = "osu!",
Tags = "this beatmap has retries and fails but no ratings",
},
BaseDifficulty = new BeatmapDifficulty
@@ -149,7 +149,7 @@ namespace osu.Game.Tests.Visual.SongSelect
Version = "No Metrics",
Metadata = new BeatmapMetadata
{
- Source = "osu!lazer",
+ Source = "osu!",
Tags = "this beatmap has no metrics",
},
BaseDifficulty = new BeatmapDifficulty
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs
index df8ef92a05..4a1d90d871 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs
@@ -103,14 +103,11 @@ namespace osu.Game.Tests.Visual.UserInterface
var easierMods = osu.GetModsFor(ModType.DifficultyReduction);
var harderMods = osu.GetModsFor(ModType.DifficultyIncrease);
- var conversionMods = osu.GetModsFor(ModType.Conversion);
var noFailMod = osu.GetModsFor(ModType.DifficultyReduction).FirstOrDefault(m => m is OsuModNoFail);
var doubleTimeMod = harderMods.OfType().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 hardRock = harderMods.FirstOrDefault(m => m is OsuModHardRock);
@@ -118,8 +115,6 @@ namespace osu.Game.Tests.Visual.UserInterface
testMultiMod(doubleTimeMod);
testIncompatibleMods(easy, hardRock);
testDeselectAll(easierMods.Where(m => !(m is MultiMod)));
-
- testUnimplementedMod(targetMod);
}
[Test]
@@ -249,6 +244,19 @@ namespace osu.Game.Tests.Visual.UserInterface
AddAssert("DT + HD still selected", () => modSelect.ChildrenOfType().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)
{
selectNext(mod);
@@ -343,6 +351,12 @@ namespace osu.Game.Tests.Visual.UserInterface
waitForLoad();
}
+ private void changeTestRuleset(RulesetInfo rulesetInfo)
+ {
+ AddStep($"change ruleset to {rulesetInfo.Name}", () => { Ruleset.Value = rulesetInfo; });
+ waitForLoad();
+ }
+
private void waitForLoad() =>
AddUntilStep("wait for icons to load", () => modSelect.AllLoaded);
@@ -401,5 +415,24 @@ namespace osu.Game.Tests.Visual.UserInterface
{
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 GetModsFor(ModType type)
+ {
+ if (type == ModType.Conversion) return base.GetModsFor(type).Concat(new[] { new TestUnimplementedMod() });
+
+ return base.GetModsFor(type);
+ }
+ }
}
}
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneProfileSubsectionHeader.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneProfileSubsectionHeader.cs
index cd226662d7..4ce684d5af 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneProfileSubsectionHeader.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneProfileSubsectionHeader.cs
@@ -57,7 +57,7 @@ namespace osu.Game.Tests.Visual.UserInterface
[Test]
public void TestInitialVisibility()
{
- AddStep("Create header with 0 value", () => createHeader("Header with visible when zero counter", CounterVisibilityState.VisibleWhenZero, 0));
+ AddStep("Create header with 0 value", () => createHeader("Header with visible when zero counter", CounterVisibilityState.VisibleWhenZero));
AddAssert("Value is 0", () => header.Current.Value == 0);
AddAssert("Counter is visible", () => header.ChildrenOfType().First().Alpha == 1);
diff --git a/osu.Game.Tournament.Tests/NonVisual/CustomTourneyDirectoryTest.cs b/osu.Game.Tournament.Tests/NonVisual/CustomTourneyDirectoryTest.cs
index 61f8511e3c..d2369056e1 100644
--- a/osu.Game.Tournament.Tests/NonVisual/CustomTourneyDirectoryTest.cs
+++ b/osu.Game.Tournament.Tests/NonVisual/CustomTourneyDirectoryTest.cs
@@ -20,7 +20,7 @@ namespace osu.Game.Tournament.Tests.NonVisual
[Test]
public void TestDefaultDirectory()
{
- using (HeadlessGameHost host = new CleanRunHeadlessGameHost())
+ using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestDefaultDirectory)))
{
try
{
@@ -139,8 +139,13 @@ namespace osu.Game.Tournament.Tests.NonVisual
}
finally
{
- host.Storage.Delete("tournament.ini");
- host.Storage.DeleteDirectory("tournaments");
+ try
+ {
+ host.Storage.Delete("tournament.ini");
+ host.Storage.DeleteDirectory("tournaments");
+ }
+ catch { }
+
host.Exit();
}
}
diff --git a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs
index e4ec45c00e..6e4c6784c8 100644
--- a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs
+++ b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs
@@ -172,7 +172,7 @@ namespace osu.Game.Tournament.Screens.Gameplay
{
chat?.Contract();
- using (BeginDelayedSequence(300, true))
+ using (BeginDelayedSequence(300))
{
scoreDisplay.FadeIn(100);
SongBar.Expanded = true;
diff --git a/osu.Game/.editorconfig b/osu.Game/.editorconfig
new file mode 100644
index 0000000000..46a3dafd04
--- /dev/null
+++ b/osu.Game/.editorconfig
@@ -0,0 +1,2 @@
+[*.cs]
+dotnet_diagnostic.OLOC001.prefix_namespace = osu.Game.Resources.Localisation
\ No newline at end of file
diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs
index 14bddb6319..0d16294c68 100644
--- a/osu.Game/Beatmaps/BeatmapManager.cs
+++ b/osu.Game/Beatmaps/BeatmapManager.cs
@@ -324,7 +324,7 @@ namespace osu.Game.Beatmaps
protected override bool CanSkipImport(BeatmapSetInfo existing, BeatmapSetInfo import)
{
- if (!base.CanReuseExisting(existing, import))
+ if (!base.CanSkipImport(existing, import))
return false;
return existing.Beatmaps.Any(b => b.OnlineBeatmapID != null);
diff --git a/osu.Game/Beatmaps/BeatmapMetadata.cs b/osu.Game/Beatmaps/BeatmapMetadata.cs
index bfc0236db3..713f80d1fe 100644
--- a/osu.Game/Beatmaps/BeatmapMetadata.cs
+++ b/osu.Game/Beatmaps/BeatmapMetadata.cs
@@ -94,7 +94,10 @@ namespace osu.Game.Beatmaps
public RomanisableString ToRomanisableString()
{
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]
diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs
index f72a43fa01..87bf54f981 100644
--- a/osu.Game/Database/ArchiveModelManager.cs
+++ b/osu.Game/Database/ArchiveModelManager.cs
@@ -353,8 +353,6 @@ namespace osu.Game.Database
{
cancellationToken.ThrowIfCancellationRequested();
- delayEvents();
-
bool checkedExisting = false;
TModel existing = null;
@@ -394,6 +392,8 @@ namespace osu.Game.Database
}
}
+ delayEvents();
+
try
{
LogForModel(item, @"Beginning import...");
diff --git a/osu.Game/Database/IRealmFactory.cs b/osu.Game/Database/IRealmFactory.cs
index c79442134c..0e93e5bf4f 100644
--- a/osu.Game/Database/IRealmFactory.cs
+++ b/osu.Game/Database/IRealmFactory.cs
@@ -9,6 +9,7 @@ namespace osu.Game.Database
{
///
/// The main realm context, bound to the update thread.
+ /// If querying from a non-update thread is needed, use or to receive a context instead.
///
Realm Context { get; }
diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs
index e0c0f56cb3..68d186c65d 100644
--- a/osu.Game/Database/OsuDbContext.cs
+++ b/osu.Game/Database/OsuDbContext.cs
@@ -77,6 +77,9 @@ namespace osu.Game.Database
{
cmd.CommandText = "PRAGMA journal_mode=WAL;";
cmd.ExecuteNonQuery();
+
+ cmd.CommandText = "PRAGMA foreign_keys=OFF;";
+ cmd.ExecuteNonQuery();
}
}
catch
diff --git a/osu.Game/Database/RealmContextFactory.cs b/osu.Game/Database/RealmContextFactory.cs
index fb5e2faff8..ed3dc01f15 100644
--- a/osu.Game/Database/RealmContextFactory.cs
+++ b/osu.Game/Database/RealmContextFactory.cs
@@ -2,8 +2,10 @@
// See the LICENCE file in the repository root for full licence text.
using System;
+using System.Diagnostics;
using System.Threading;
using osu.Framework.Allocation;
+using osu.Framework.Development;
using osu.Framework.Graphics;
using osu.Framework.Logging;
using osu.Framework.Platform;
@@ -38,21 +40,29 @@ namespace osu.Game.Database
private static readonly GlobalStatistic pending_writes = GlobalStatistics.Get("Realm", "Pending writes");
private static readonly GlobalStatistic active_usages = GlobalStatistics.Get("Realm", "Active usages");
+ private readonly object updateContextLock = new object();
+
private Realm context;
public Realm Context
{
get
{
- if (context == null)
+ if (!ThreadSafety.IsUpdateThread)
+ throw new InvalidOperationException($"Use {nameof(GetForRead)} or {nameof(GetForWrite)} when performing realm operations from a non-update thread");
+
+ lock (updateContextLock)
{
- context = createContext();
- Logger.Log($"Opened realm \"{context.Config.DatabasePath}\" at version {context.Config.SchemaVersion}");
+ if (context == null)
+ {
+ context = createContext();
+ Logger.Log($"Opened realm \"{context.Config.DatabasePath}\" at version {context.Config.SchemaVersion}");
+ }
+
+ // creating a context will ensure our schema is up-to-date and migrated.
+
+ return context;
}
-
- // creating a context will ensure our schema is up-to-date and migrated.
-
- return context;
}
}
@@ -107,8 +117,11 @@ namespace osu.Game.Database
{
base.Update();
- if (context?.Refresh() == true)
- refreshes.Value++;
+ lock (updateContextLock)
+ {
+ if (context?.Refresh() == true)
+ refreshes.Value++;
+ }
}
private Realm createContext()
@@ -154,9 +167,15 @@ namespace osu.Game.Database
private void flushContexts()
{
Logger.Log(@"Flushing realm contexts...", LoggingTarget.Database);
+ Debug.Assert(blockingLock.CurrentCount == 0);
- var previousContext = context;
- context = null;
+ Realm previousContext;
+
+ lock (updateContextLock)
+ {
+ previousContext = context;
+ context = null;
+ }
// wait for all threaded usages to finish
while (active_usages.Value > 0)
diff --git a/osu.Game/Database/UserLookupCache.cs b/osu.Game/Database/UserLookupCache.cs
index 19cc211709..13c37ddfe9 100644
--- a/osu.Game/Database/UserLookupCache.cs
+++ b/osu.Game/Database/UserLookupCache.cs
@@ -27,6 +27,30 @@ namespace osu.Game.Database
[ItemCanBeNull]
public Task GetUserAsync(int userId, CancellationToken token = default) => GetAsync(userId, token);
+ ///
+ /// Perform an API lookup on the specified users, populating a model.
+ ///
+ /// The users to lookup.
+ /// An optional cancellation token.
+ /// The populated users. May include null results for failed retrievals.
+ public Task GetUsersAsync(int[] userIds, CancellationToken token = default)
+ {
+ var userLookupTasks = new List>();
+
+ foreach (var u in userIds)
+ {
+ userLookupTasks.Add(GetUserAsync(u, token).ContinueWith(task =>
+ {
+ if (!task.IsCompletedSuccessfully)
+ return null;
+
+ return task.Result;
+ }, token));
+ }
+
+ return Task.WhenAll(userLookupTasks);
+ }
+
protected override async Task ComputeValueAsync(int lookup, CancellationToken token = default)
=> await queryUser(lookup).ConfigureAwait(false);
diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs
index 1c9cdc174a..e2a0c09a6b 100644
--- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs
+++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs
@@ -97,7 +97,7 @@ namespace osu.Game.Graphics.Containers
if (timingPoint == lastTimingPoint && beatIndex == lastBeat)
return;
- using (BeginDelayedSequence(-TimeSinceLastBeat, true))
+ using (BeginDelayedSequence(-TimeSinceLastBeat))
OnNewBeat(beatIndex, timingPoint, effectPoint, track?.CurrentAmplitudes ?? ChannelAmplitudes.Empty);
lastBeat = beatIndex;
diff --git a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs
index ae16169123..f85f9327fa 100644
--- a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs
+++ b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs
@@ -15,6 +15,7 @@ using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events;
using osu.Framework.Localisation;
+using osu.Framework.Utils;
namespace osu.Game.Graphics.UserInterface
{
@@ -99,7 +100,7 @@ namespace osu.Game.Graphics.UserInterface
[BackgroundDependencyLoader]
private void load(AudioManager audio, OsuColour colours)
{
- sample = audio.Samples.Get(@"UI/sliderbar-notch");
+ sample = audio.Samples.Get(@"UI/notch-tick");
AccentColour = colours.Pink;
}
@@ -149,7 +150,7 @@ namespace osu.Game.Graphics.UserInterface
private void playSample(T value)
{
- if (Clock == null || Clock.CurrentTime - lastSampleTime <= 50)
+ if (Clock == null || Clock.CurrentTime - lastSampleTime <= 30)
return;
if (value.Equals(lastSampleValue))
@@ -158,13 +159,15 @@ namespace osu.Game.Graphics.UserInterface
lastSampleValue = value;
lastSampleTime = Clock.CurrentTime;
- var channel = sample.Play();
+ var channel = sample.GetChannel();
- channel.Frequency.Value = 1 + NormalizedValue * 0.2f;
- if (NormalizedValue == 0)
- channel.Frequency.Value -= 0.4f;
- else if (NormalizedValue == 1)
- channel.Frequency.Value += 0.4f;
+ channel.Frequency.Value = 0.99f + RNG.NextDouble(0.02f) + NormalizedValue * 0.2f;
+
+ // intentionally pitched down, even when hitting max.
+ if (NormalizedValue == 0 || NormalizedValue == 1)
+ channel.Frequency.Value -= 0.5f;
+
+ channel.Play();
}
private void updateTooltipText(T value)
diff --git a/osu.Game/Graphics/UserInterface/StarCounter.cs b/osu.Game/Graphics/UserInterface/StarCounter.cs
index 894a21fcf3..32b788b5dc 100644
--- a/osu.Game/Graphics/UserInterface/StarCounter.cs
+++ b/osu.Game/Graphics/UserInterface/StarCounter.cs
@@ -113,7 +113,7 @@ namespace osu.Game.Graphics.UserInterface
double delay = (current <= newValue ? Math.Max(i - current, 0) : Math.Max(current - 1 - i, 0)) * AnimationDelay;
- using (star.BeginDelayedSequence(delay, true))
+ using (star.BeginDelayedSequence(delay))
star.DisplayAt(getStarScale(i, newValue));
}
}
diff --git a/osu.Game/Localisation/ButtonSystem.ja.resx b/osu.Game/Localisation/ButtonSystem.ja.resx
deleted file mode 100644
index 02f3e7ce2f..0000000000
--- a/osu.Game/Localisation/ButtonSystem.ja.resx
+++ /dev/null
@@ -1,38 +0,0 @@
-
-
- text/microsoft-resx
-
-
- 1.3
-
-
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- ソロ
-
-
- プレイリスト
-
-
- 遊ぶ
-
-
- マルチ
-
-
- エディット
-
-
- ブラウズ
-
-
- 閉じる
-
-
- 設定
-
-
diff --git a/osu.Game/Localisation/ButtonSystem.resx b/osu.Game/Localisation/ButtonSystem.resx
deleted file mode 100644
index d72ffff8be..0000000000
--- a/osu.Game/Localisation/ButtonSystem.resx
+++ /dev/null
@@ -1,88 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- text/microsoft-resx
-
-
- 2.0
-
-
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- solo
-
-
- multi
-
-
- playlists
-
-
- play
-
-
- edit
-
-
- browse
-
-
- settings
-
-
- back
-
-
- exit
-
-
\ No newline at end of file
diff --git a/osu.Game/Localisation/ButtonSystemStrings.cs b/osu.Game/Localisation/ButtonSystemStrings.cs
index 8083f80782..ba4abf63a6 100644
--- a/osu.Game/Localisation/ButtonSystemStrings.cs
+++ b/osu.Game/Localisation/ButtonSystemStrings.cs
@@ -7,7 +7,7 @@ namespace osu.Game.Localisation
{
public static class ButtonSystemStrings
{
- private const string prefix = @"osu.Game.Localisation.ButtonSystem";
+ private const string prefix = @"osu.Game.Resources.Localisation.ButtonSystem";
///
/// "solo"
@@ -56,4 +56,4 @@ namespace osu.Game.Localisation
private static string getKey(string key) => $@"{prefix}:{key}";
}
-}
\ No newline at end of file
+}
diff --git a/osu.Game/Localisation/Chat.resx b/osu.Game/Localisation/Chat.resx
deleted file mode 100644
index 055e351463..0000000000
--- a/osu.Game/Localisation/Chat.resx
+++ /dev/null
@@ -1,67 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- text/microsoft-resx
-
-
- 2.0
-
-
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- chat
-
-
- join the real-time discussion
-
-
\ No newline at end of file
diff --git a/osu.Game/Localisation/ChatStrings.cs b/osu.Game/Localisation/ChatStrings.cs
index 636351470b..7bd284a94e 100644
--- a/osu.Game/Localisation/ChatStrings.cs
+++ b/osu.Game/Localisation/ChatStrings.cs
@@ -7,7 +7,7 @@ namespace osu.Game.Localisation
{
public static class ChatStrings
{
- private const string prefix = @"osu.Game.Localisation.Chat";
+ private const string prefix = @"osu.Game.Resources.Localisation.Chat";
///
/// "chat"
diff --git a/osu.Game/Localisation/Common.resx b/osu.Game/Localisation/Common.resx
deleted file mode 100644
index 59de16a037..0000000000
--- a/osu.Game/Localisation/Common.resx
+++ /dev/null
@@ -1,64 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- text/microsoft-resx
-
-
- 2.0
-
-
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- Cancel
-
-
\ No newline at end of file
diff --git a/osu.Game/Localisation/CommonStrings.cs b/osu.Game/Localisation/CommonStrings.cs
index ced0d80955..50e01f06fc 100644
--- a/osu.Game/Localisation/CommonStrings.cs
+++ b/osu.Game/Localisation/CommonStrings.cs
@@ -7,7 +7,7 @@ namespace osu.Game.Localisation
{
public static class CommonStrings
{
- private const string prefix = @"osu.Game.Localisation.Common";
+ private const string prefix = @"osu.Game.Resources.Localisation.Common";
///
/// "Cancel"
diff --git a/osu.Game/Localisation/Notifications.resx b/osu.Game/Localisation/Notifications.resx
deleted file mode 100644
index 08db240ba2..0000000000
--- a/osu.Game/Localisation/Notifications.resx
+++ /dev/null
@@ -1,67 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- text/microsoft-resx
-
-
- 2.0
-
-
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- notifications
-
-
- waiting for 'ya
-
-
\ No newline at end of file
diff --git a/osu.Game/Localisation/NotificationsStrings.cs b/osu.Game/Localisation/NotificationsStrings.cs
index ba28ef5560..382e0d81f4 100644
--- a/osu.Game/Localisation/NotificationsStrings.cs
+++ b/osu.Game/Localisation/NotificationsStrings.cs
@@ -7,7 +7,7 @@ namespace osu.Game.Localisation
{
public static class NotificationsStrings
{
- private const string prefix = @"osu.Game.Localisation.Notifications";
+ private const string prefix = @"osu.Game.Resources.Localisation.Notifications";
///
/// "notifications"
diff --git a/osu.Game/Localisation/NowPlaying.resx b/osu.Game/Localisation/NowPlaying.resx
deleted file mode 100644
index 40fda3e25b..0000000000
--- a/osu.Game/Localisation/NowPlaying.resx
+++ /dev/null
@@ -1,67 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- text/microsoft-resx
-
-
- 2.0
-
-
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- now playing
-
-
- manage the currently playing track
-
-
\ No newline at end of file
diff --git a/osu.Game/Localisation/NowPlayingStrings.cs b/osu.Game/Localisation/NowPlayingStrings.cs
index 47646b0f68..f334637338 100644
--- a/osu.Game/Localisation/NowPlayingStrings.cs
+++ b/osu.Game/Localisation/NowPlayingStrings.cs
@@ -7,7 +7,7 @@ namespace osu.Game.Localisation
{
public static class NowPlayingStrings
{
- private const string prefix = @"osu.Game.Localisation.NowPlaying";
+ private const string prefix = @"osu.Game.Resources.Localisation.NowPlaying";
///
/// "now playing"
diff --git a/osu.Game/Localisation/Settings.resx b/osu.Game/Localisation/Settings.resx
deleted file mode 100644
index 85c224cedf..0000000000
--- a/osu.Game/Localisation/Settings.resx
+++ /dev/null
@@ -1,67 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- text/microsoft-resx
-
-
- 2.0
-
-
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- settings
-
-
- change the way osu! behaves
-
-
\ No newline at end of file
diff --git a/osu.Game/Localisation/SettingsStrings.cs b/osu.Game/Localisation/SettingsStrings.cs
index f4b417fa28..aa2e2740eb 100644
--- a/osu.Game/Localisation/SettingsStrings.cs
+++ b/osu.Game/Localisation/SettingsStrings.cs
@@ -7,7 +7,7 @@ namespace osu.Game.Localisation
{
public static class SettingsStrings
{
- private const string prefix = @"osu.Game.Localisation.Settings";
+ private const string prefix = @"osu.Game.Resources.Localisation.Settings";
///
/// "settings"
diff --git a/osu.Game/Online/API/Requests/GetSpotlightRankingsRequest.cs b/osu.Game/Online/API/Requests/GetSpotlightRankingsRequest.cs
index 25e6b3f1af..20856c2768 100644
--- a/osu.Game/Online/API/Requests/GetSpotlightRankingsRequest.cs
+++ b/osu.Game/Online/API/Requests/GetSpotlightRankingsRequest.cs
@@ -13,7 +13,7 @@ namespace osu.Game.Online.API.Requests
private readonly RankingsSortCriteria sort;
public GetSpotlightRankingsRequest(RulesetInfo ruleset, int spotlight, RankingsSortCriteria sort)
- : base(ruleset, 1)
+ : base(ruleset)
{
this.spotlight = spotlight;
this.sort = sort;
diff --git a/osu.Game/Online/Chat/MessageFormatter.cs b/osu.Game/Online/Chat/MessageFormatter.cs
index faee08742b..ae9199c428 100644
--- a/osu.Game/Online/Chat/MessageFormatter.cs
+++ b/osu.Game/Online/Chat/MessageFormatter.cs
@@ -320,6 +320,7 @@ namespace osu.Game.Online.Chat
JoinMultiplayerMatch,
Spectate,
OpenUserProfile,
+ SearchBeatmapSet,
OpenWiki,
Custom,
}
diff --git a/osu.Game/Online/Leaderboards/Leaderboard.cs b/osu.Game/Online/Leaderboards/Leaderboard.cs
index 70e38e421d..4f8b27602b 100644
--- a/osu.Game/Online/Leaderboards/Leaderboard.cs
+++ b/osu.Game/Online/Leaderboards/Leaderboard.cs
@@ -82,7 +82,7 @@ namespace osu.Game.Online.Leaderboards
foreach (var s in scrollFlow.Children)
{
- using (s.BeginDelayedSequence(i++ * 50, true))
+ using (s.BeginDelayedSequence(i++ * 50))
s.Show();
}
diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs
index e35d3d6461..7108a23e44 100644
--- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs
+++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs
@@ -248,7 +248,7 @@ namespace osu.Game.Online.Leaderboards
this.FadeIn(200);
content.MoveToY(0, 800, Easing.OutQuint);
- using (BeginDelayedSequence(100, true))
+ using (BeginDelayedSequence(100))
{
avatar.FadeIn(300, Easing.OutQuint);
nameLabel.FadeIn(350, Easing.OutQuint);
@@ -256,12 +256,12 @@ namespace osu.Game.Online.Leaderboards
avatar.MoveToX(0, 300, Easing.OutQuint);
nameLabel.MoveToX(0, 350, Easing.OutQuint);
- using (BeginDelayedSequence(250, true))
+ using (BeginDelayedSequence(250))
{
scoreLabel.FadeIn(200);
scoreRank.FadeIn(200);
- using (BeginDelayedSequence(50, true))
+ using (BeginDelayedSequence(50))
{
var drawables = new Drawable[] { flagBadgeContainer, modsContainer }.Concat(statisticsLabels).ToArray();
for (int i = 0; i < drawables.Length; i++)
diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs
index dcd2d68b43..c25b520892 100644
--- a/osu.Game/OsuGame.cs
+++ b/osu.Game/OsuGame.cs
@@ -305,6 +305,10 @@ namespace osu.Game
ShowChannel(link.Argument);
break;
+ case LinkAction.SearchBeatmapSet:
+ SearchBeatmapSet(link.Argument);
+ break;
+
case LinkAction.OpenEditorTimestamp:
case LinkAction.JoinMultiplayerMatch:
case LinkAction.Spectate:
@@ -375,6 +379,12 @@ namespace osu.Game
/// The beatmap to show.
public void ShowBeatmap(int beatmapId) => waitForReady(() => beatmapSetOverlay, _ => beatmapSetOverlay.FetchAndShowBeatmap(beatmapId));
+ ///
+ /// Shows the beatmap listing overlay, with the given in the search box.
+ ///
+ /// The query to search for.
+ public void SearchBeatmapSet(string query) => waitForReady(() => beatmapListing, _ => beatmapListing.ShowWithSearch(query));
+
///
/// Show a wiki's page as an overlay
///
diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs
index 7954eafdca..5878727ad8 100644
--- a/osu.Game/OsuGameBase.cs
+++ b/osu.Game/OsuGameBase.cs
@@ -80,7 +80,7 @@ namespace osu.Game
return @"local " + (DebugUtils.IsDebugBuild ? @"debug" : @"release");
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()
{
UseDevelopmentServer = DebugUtils.IsDebugBuild;
- Name = @"osu!lazer";
+ Name = @"osu!";
}
[BackgroundDependencyLoader]
diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs
index d80ef075e9..650d105911 100644
--- a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs
+++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs
@@ -122,6 +122,9 @@ namespace osu.Game.Overlays.BeatmapListing
sortControlBackground.Colour = colourProvider.Background5;
}
+ public void Search(string query)
+ => searchControl.Query.Value = query;
+
protected override void LoadComplete()
{
base.LoadComplete();
diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs
index 460b4ba4c9..6861d17f26 100644
--- a/osu.Game/Overlays/BeatmapListingOverlay.cs
+++ b/osu.Game/Overlays/BeatmapListingOverlay.cs
@@ -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 Color4 BackgroundColour => ColourProvider.Background6;
diff --git a/osu.Game/Overlays/BeatmapSet/Info.cs b/osu.Game/Overlays/BeatmapSet/Info.cs
index dbe01ad27f..f9b8de9dba 100644
--- a/osu.Game/Overlays/BeatmapSet/Info.cs
+++ b/osu.Game/Overlays/BeatmapSet/Info.cs
@@ -8,15 +8,12 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
-using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
-using osuTK;
namespace osu.Game.Overlays.BeatmapSet
{
public class Info : Container
{
- private const float transition_duration = 250;
private const float metadata_width = 175;
private const float spacing = 20;
private const float base_height = 220;
@@ -60,7 +57,7 @@ namespace osu.Game.Overlays.BeatmapSet
Child = new Container
{
RelativeSizeAxes = Axes.Both,
- Child = new MetadataSection("Description"),
+ Child = new MetadataSection(MetadataType.Description),
},
},
new Container
@@ -78,10 +75,10 @@ namespace osu.Game.Overlays.BeatmapSet
Direction = FillDirection.Full,
Children = new[]
{
- source = new MetadataSection("Source"),
- genre = new MetadataSection("Genre") { Width = 0.5f },
- language = new MetadataSection("Language") { Width = 0.5f },
- tags = new MetadataSection("Tags"),
+ source = new MetadataSection(MetadataType.Source),
+ genre = new MetadataSection(MetadataType.Genre) { Width = 0.5f },
+ language = new MetadataSection(MetadataType.Language) { Width = 0.5f },
+ tags = new MetadataSection(MetadataType.Tags),
},
},
},
@@ -135,48 +132,5 @@ namespace osu.Game.Overlays.BeatmapSet
successRateBackground.Colour = colourProvider.Background4;
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,
- },
- };
- }
- }
}
}
diff --git a/osu.Game/Overlays/BeatmapSet/MetadataSection.cs b/osu.Game/Overlays/BeatmapSet/MetadataSection.cs
new file mode 100644
index 0000000000..3648c55714
--- /dev/null
+++ b/osu.Game/Overlays/BeatmapSet/MetadataSection.cs
@@ -0,0 +1,115 @@
+// Copyright (c) ppy Pty Ltd . 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);
+ });
+ }
+ }
+}
diff --git a/osu.Game/Overlays/BeatmapSet/MetadataType.cs b/osu.Game/Overlays/BeatmapSet/MetadataType.cs
new file mode 100644
index 0000000000..1ab4c6887e
--- /dev/null
+++ b/osu.Game/Overlays/BeatmapSet/MetadataType.cs
@@ -0,0 +1,14 @@
+// Copyright (c) ppy Pty Ltd . 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
+ }
+}
diff --git a/osu.Game/Overlays/MedalOverlay.cs b/osu.Game/Overlays/MedalOverlay.cs
index 0feae16b68..e15625a4b3 100644
--- a/osu.Game/Overlays/MedalOverlay.cs
+++ b/osu.Game/Overlays/MedalOverlay.cs
@@ -213,7 +213,7 @@ namespace osu.Game.Overlays
innerSpin.Spin(20000, RotationDirection.Clockwise);
outerSpin.Spin(40000, RotationDirection.Clockwise);
- using (BeginDelayedSequence(200, true))
+ using (BeginDelayedSequence(200))
{
disc.FadeIn(initial_duration)
.ScaleTo(1f, initial_duration * 2, Easing.OutElastic);
@@ -221,7 +221,7 @@ namespace osu.Game.Overlays
particleContainer.FadeIn(initial_duration);
outerSpin.FadeTo(0.1f, initial_duration * 2);
- using (BeginDelayedSequence(initial_duration + 200, true))
+ using (BeginDelayedSequence(initial_duration + 200))
{
backgroundStrip.FadeIn(step_duration);
leftStrip.ResizeWidthTo(1f, step_duration, Easing.OutQuint);
diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs
index d0bd24496a..572ff0d1aa 100644
--- a/osu.Game/Overlays/Mods/ModButton.cs
+++ b/osu.Game/Overlays/Mods/ModButton.cs
@@ -91,7 +91,7 @@ namespace osu.Game.Overlays.Mods
backgroundIcon.Mod = newSelection;
- using (BeginDelayedSequence(mod_switch_duration, true))
+ using (BeginDelayedSequence(mod_switch_duration))
{
foregroundIcon
.RotateTo(-rotate_angle * direction)
diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs
index e4aab978fc..6c47b92d29 100644
--- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs
+++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs
@@ -128,7 +128,7 @@ namespace osu.Game.Overlays.Mods
RowDimensions = new[]
{
new Dimension(GridSizeMode.Absolute, 90),
- new Dimension(GridSizeMode.Distributed),
+ new Dimension(),
new Dimension(GridSizeMode.AutoSize),
},
Content = new[]
diff --git a/osu.Game/Overlays/News/NewsHeader.cs b/osu.Game/Overlays/News/NewsHeader.cs
index 94bfd62c32..56c54425bd 100644
--- a/osu.Game/Overlays/News/NewsHeader.cs
+++ b/osu.Game/Overlays/News/NewsHeader.cs
@@ -13,7 +13,7 @@ namespace osu.Game.Overlays.News
public Action ShowFrontPage;
- private readonly Bindable article = new Bindable(null);
+ private readonly Bindable article = new Bindable();
public NewsHeader()
{
diff --git a/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs b/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs
index e4c0fe3a5a..3cdf110090 100644
--- a/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs
+++ b/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs
@@ -57,7 +57,7 @@ namespace osu.Game.Overlays.Profile.Header.Components
ColumnDimensions = new[]
{
new Dimension(GridSizeMode.AutoSize),
- new Dimension(GridSizeMode.Distributed)
+ new Dimension()
},
Content = new[]
{
diff --git a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs
index 943897581e..e8a76c64ec 100644
--- a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs
+++ b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs
@@ -61,7 +61,7 @@ namespace osu.Game.Overlays.Rankings.Tables
private static TableColumn[] mainHeaders => new[]
{
new TableColumn(string.Empty, Anchor.Centre, new Dimension(GridSizeMode.Absolute, 40)), // place
- new TableColumn(string.Empty, Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed)), // flag and username (country name)
+ new TableColumn(string.Empty, Anchor.CentreLeft, new Dimension()), // flag and username (country name)
};
protected abstract TableColumn[] CreateAdditionalHeaders();
diff --git a/osu.Game/Overlays/Volume/VolumeMeter.cs b/osu.Game/Overlays/Volume/VolumeMeter.cs
index a15076581e..1832841476 100644
--- a/osu.Game/Overlays/Volume/VolumeMeter.cs
+++ b/osu.Game/Overlays/Volume/VolumeMeter.cs
@@ -4,6 +4,8 @@
using System;
using System.Globalization;
using osu.Framework.Allocation;
+using osu.Framework.Audio;
+using osu.Framework.Audio.Sample;
using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
@@ -36,6 +38,9 @@ namespace osu.Game.Overlays.Volume
private OsuSpriteText text;
private BufferedContainer maxGlow;
+ private Sample sample;
+ private double sampleLastPlaybackTime;
+
public VolumeMeter(string name, float circleSize, Color4 meterColour)
{
this.circleSize = circleSize;
@@ -46,8 +51,11 @@ namespace osu.Game.Overlays.Volume
}
[BackgroundDependencyLoader]
- private void load(OsuColour colours)
+ private void load(OsuColour colours, AudioManager audio)
{
+ sample = audio.Samples.Get(@"UI/notch-tick");
+ sampleLastPlaybackTime = Time.Current;
+
Color4 backgroundColour = colours.Gray1;
CircularProgress bgProgress;
@@ -178,22 +186,12 @@ namespace osu.Game.Overlays.Volume
}
};
- Bindable.ValueChanged += volume =>
- {
- this.TransformTo("DisplayVolume",
- volume.NewValue,
- 400,
- Easing.OutQuint);
- };
+ Bindable.BindValueChanged(volume => { this.TransformTo(nameof(DisplayVolume), volume.NewValue, 400, Easing.OutQuint); }, true);
bgProgress.Current.Value = 0.75f;
}
- protected override void LoadComplete()
- {
- base.LoadComplete();
- Bindable.TriggerChange();
- }
+ private int? displayVolumeInt;
private double displayVolume;
@@ -204,6 +202,11 @@ namespace osu.Game.Overlays.Volume
{
displayVolume = value;
+ int intValue = (int)Math.Round(displayVolume * 100);
+ bool intVolumeChanged = intValue != displayVolumeInt;
+
+ displayVolumeInt = intValue;
+
if (displayVolume >= 0.995f)
{
text.Text = "MAX";
@@ -212,14 +215,36 @@ namespace osu.Game.Overlays.Volume
else
{
maxGlow.EffectColour = Color4.Transparent;
- text.Text = Math.Round(displayVolume * 100).ToString(CultureInfo.CurrentCulture);
+ text.Text = intValue.ToString(CultureInfo.CurrentCulture);
}
volumeCircle.Current.Value = displayVolume * 0.75f;
volumeCircleGlow.Current.Value = displayVolume * 0.75f;
+
+ if (intVolumeChanged && IsLoaded)
+ Scheduler.AddOnce(playTickSound);
}
}
+ private void playTickSound()
+ {
+ const int tick_debounce_time = 30;
+
+ if (Time.Current - sampleLastPlaybackTime <= tick_debounce_time)
+ return;
+
+ var channel = sample.GetChannel();
+
+ channel.Frequency.Value = 0.99f + RNG.NextDouble(0.02f) + displayVolume * 0.1f;
+
+ // intentionally pitched down, even when hitting max.
+ if (displayVolumeInt == 0 || displayVolumeInt == 100)
+ channel.Frequency.Value -= 0.5f;
+
+ channel.Play();
+ sampleLastPlaybackTime = Time.Current;
+ }
+
public double Volume
{
get => Bindable.Value;
diff --git a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs
index 8a57b4af91..0f22d35bb5 100644
--- a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs
+++ b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs
@@ -117,7 +117,7 @@ namespace osu.Game.Rulesets.Judgements
LifetimeStart = Result.TimeAbsolute;
- using (BeginAbsoluteSequence(Result.TimeAbsolute, true))
+ using (BeginAbsoluteSequence(Result.TimeAbsolute))
{
// not sure if this should remain going forward.
JudgementBody.ResetAnimation();
diff --git a/osu.Game/Rulesets/Mods/ModBarrelRoll.cs b/osu.Game/Rulesets/Mods/ModBarrelRoll.cs
index 0d344b5269..872daadd46 100644
--- a/osu.Game/Rulesets/Mods/ModBarrelRoll.cs
+++ b/osu.Game/Rulesets/Mods/ModBarrelRoll.cs
@@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Mods
};
[SettingSource("Direction", "The direction of rotation")]
- public Bindable Direction { get; } = new Bindable(RotationDirection.Clockwise);
+ public Bindable Direction { get; } = new Bindable();
public override string Name => "Barrel Roll";
public override string Acronym => "BR";
diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
index a0717ec38e..c5db806918 100644
--- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
+++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
@@ -404,13 +404,13 @@ namespace osu.Game.Rulesets.Objects.Drawables
clearExistingStateTransforms();
- using (BeginAbsoluteSequence(transformTime, true))
+ using (BeginAbsoluteSequence(transformTime))
UpdateInitialTransforms();
- using (BeginAbsoluteSequence(StateUpdateTime, true))
+ using (BeginAbsoluteSequence(StateUpdateTime))
UpdateStartTimeStateTransforms();
- using (BeginAbsoluteSequence(HitStateUpdateTime, true))
+ using (BeginAbsoluteSequence(HitStateUpdateTime))
UpdateHitStateTransforms(newState);
state.Value = newState;
diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs
index 8dcc1ca164..daf46dcdcc 100644
--- a/osu.Game/Rulesets/UI/DrawableRuleset.cs
+++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs
@@ -489,15 +489,15 @@ namespace osu.Game.Rulesets.UI
{
get
{
- foreach (var h in Objects)
+ foreach (var hitObject in Objects)
{
- if (h.HitWindows.WindowFor(HitResult.Miss) > 0)
- return h.HitWindows;
+ if (hitObject.HitWindows.WindowFor(HitResult.Miss) > 0)
+ return hitObject.HitWindows;
- foreach (var n in h.NestedHitObjects)
+ foreach (var nested in hitObject.NestedHitObjects)
{
- if (h.HitWindows.WindowFor(HitResult.Miss) > 0)
- return n.HitWindows;
+ if (nested.HitWindows.WindowFor(HitResult.Miss) > 0)
+ return nested.HitWindows;
}
}
diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs
index d5bea0affc..ebbdc8a109 100644
--- a/osu.Game/Scoring/ScoreManager.cs
+++ b/osu.Game/Scoring/ScoreManager.cs
@@ -208,7 +208,7 @@ namespace osu.Game.Scoring
}
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.
beatmapMaxCombo = Enum.GetValues(typeof(HitResult)).OfType().Where(r => r.AffectsCombo()).Select(r => score.Statistics.GetOrDefault(r)).Sum();
}
diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs
index 38290a6530..bdb0157746 100644
--- a/osu.Game/Screens/Menu/ButtonSystem.cs
+++ b/osu.Game/Screens/Menu/ButtonSystem.cs
@@ -297,7 +297,7 @@ namespace osu.Game.Screens.Menu
Logger.Log($"{nameof(ButtonSystem)}'s state changed from {lastState} to {state}");
- using (buttonArea.BeginDelayedSequence(lastState == ButtonSystemState.Initial ? 150 : 0, true))
+ using (buttonArea.BeginDelayedSequence(lastState == ButtonSystemState.Initial ? 150 : 0))
{
buttonArea.ButtonSystemState = state;
diff --git a/osu.Game/Screens/Menu/Disclaimer.cs b/osu.Game/Screens/Menu/Disclaimer.cs
index 72eb9c7c0c..7f34e1e395 100644
--- a/osu.Game/Screens/Menu/Disclaimer.cs
+++ b/osu.Game/Screens/Menu/Disclaimer.cs
@@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@@ -36,6 +37,8 @@ namespace osu.Game.Screens.Menu
private readonly Bindable currentUser = new Bindable();
private FillFlowContainer fill;
+ private readonly List expendableText = new List();
+
public Disclaimer(OsuScreen nextScreen = null)
{
this.nextScreen = nextScreen;
@@ -54,7 +57,7 @@ namespace osu.Game.Screens.Menu
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
- Icon = FontAwesome.Solid.Flask,
+ Icon = OsuIcon.Logo,
Size = new Vector2(icon_size),
Y = icon_y,
},
@@ -70,37 +73,55 @@ namespace osu.Game.Screens.Menu
{
textFlow = new LinkFlowContainer
{
- RelativeSizeAxes = Axes.X,
+ Width = 680,
AutoSizeAxes = Axes.Y,
TextAnchor = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Spacing = new Vector2(0, 2),
- LayoutDuration = 2000,
- LayoutEasing = Easing.OutQuint
- },
- supportFlow = new LinkFlowContainer
- {
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- TextAnchor = Anchor.TopCentre,
- Anchor = Anchor.TopCentre,
- Origin = Anchor.TopCentre,
- Alpha = 0,
- Spacing = new Vector2(0, 2),
},
}
- }
+ },
+ supportFlow = new LinkFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ TextAnchor = Anchor.BottomCentre,
+ Anchor = Anchor.BottomCentre,
+ Origin = Anchor.BottomCentre,
+ Padding = new MarginPadding(20),
+ Alpha = 0,
+ Spacing = new Vector2(0, 2),
+ },
};
- textFlow.AddText("This project is an ongoing ", t => t.Font = t.Font.With(Typeface.Torus, 30, FontWeight.Light));
- textFlow.AddText("work in progress", t => t.Font = t.Font.With(Typeface.Torus, 30, FontWeight.SemiBold));
+ textFlow.AddText("this is osu!", t => t.Font = t.Font.With(Typeface.Torus, 30, FontWeight.Regular));
+
+ expendableText.AddRange(textFlow.AddText("lazer", t =>
+ {
+ t.Font = t.Font.With(Typeface.Torus, 30, FontWeight.Regular);
+ t.Colour = colours.PinkLight;
+ }));
+
+ static void formatRegular(SpriteText t) => t.Font = OsuFont.GetFont(size: 20, weight: FontWeight.Regular);
+ static void formatSemiBold(SpriteText t) => t.Font = OsuFont.GetFont(size: 20, weight: FontWeight.SemiBold);
textFlow.NewParagraph();
- static void format(SpriteText t) => t.Font = OsuFont.GetFont(size: 15, weight: FontWeight.SemiBold);
+ textFlow.AddText("the next ", formatRegular);
+ textFlow.AddText("major update", t =>
+ {
+ t.Font = t.Font.With(Typeface.Torus, 20, FontWeight.SemiBold);
+ t.Colour = colours.Pink;
+ });
+ expendableText.AddRange(textFlow.AddText(" coming to osu!", formatRegular));
+ textFlow.AddText(".", formatRegular);
- textFlow.AddParagraph(getRandomTip(), t => t.Font = t.Font.With(Typeface.Torus, 20, FontWeight.SemiBold));
+ textFlow.NewParagraph();
+ textFlow.NewParagraph();
+
+ textFlow.AddParagraph("today's tip:", formatSemiBold);
+ textFlow.AddParagraph(getRandomTip(), formatRegular);
textFlow.NewParagraph();
textFlow.NewParagraph();
@@ -116,19 +137,19 @@ namespace osu.Game.Screens.Menu
if (e.NewValue.IsSupporter)
{
- supportFlow.AddText("Eternal thanks to you for supporting osu!", format);
+ supportFlow.AddText("Eternal thanks to you for supporting osu!", formatSemiBold);
}
else
{
- supportFlow.AddText("Consider becoming an ", format);
- supportFlow.AddLink("osu!supporter", "https://osu.ppy.sh/home/support", creationParameters: format);
- supportFlow.AddText(" to help support the game", format);
+ supportFlow.AddText("Consider becoming an ", formatSemiBold);
+ supportFlow.AddLink("osu!supporter", "https://osu.ppy.sh/home/support", formatSemiBold);
+ supportFlow.AddText(" to help support osu!'s development", formatSemiBold);
}
heart = supportFlow.AddIcon(FontAwesome.Solid.Heart, t =>
{
t.Padding = new MarginPadding { Left = 5, Top = 3 };
- t.Font = t.Font.With(size: 12);
+ t.Font = t.Font.With(size: 20);
t.Origin = Anchor.Centre;
t.Colour = colours.Pink;
}).First();
@@ -160,7 +181,7 @@ namespace osu.Game.Screens.Menu
icon.Delay(500).FadeIn(500).ScaleTo(1, 500, Easing.OutQuint);
- using (BeginDelayedSequence(3000, true))
+ using (BeginDelayedSequence(3000))
{
icon.FadeColour(iconColour, 200, Easing.OutQuint);
icon.MoveToY(icon_y * 1.3f, 500, Easing.OutCirc)
@@ -169,7 +190,15 @@ namespace osu.Game.Screens.Menu
.MoveToY(icon_y, 160, Easing.InQuart)
.FadeColour(Color4.White, 160);
- fill.Delay(520 + 160).MoveToOffset(new Vector2(0, 15), 160, Easing.OutQuart);
+ using (BeginDelayedSequence(520 + 160))
+ {
+ fill.MoveToOffset(new Vector2(0, 15), 160, Easing.OutQuart);
+ Schedule(() => expendableText.ForEach(t =>
+ {
+ t.FadeOut(100);
+ t.ScaleTo(new Vector2(0, 1), 100, Easing.OutQuart);
+ }));
+ }
}
supportFlow.FadeOut().Delay(2000).FadeIn(500);
@@ -201,7 +230,7 @@ namespace osu.Game.Screens.Menu
"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!",
"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!",
"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!",
diff --git a/osu.Game/Screens/Menu/IntroSequence.cs b/osu.Game/Screens/Menu/IntroSequence.cs
index d92d38da45..3a5cd6857a 100644
--- a/osu.Game/Screens/Menu/IntroSequence.cs
+++ b/osu.Game/Screens/Menu/IntroSequence.cs
@@ -189,7 +189,7 @@ namespace osu.Game.Screens.Menu
double remainingTime() => length - TransformDelay;
- using (BeginDelayedSequence(250, true))
+ using (BeginDelayedSequence(250))
{
welcomeText.FadeIn(700);
welcomeText.TransformSpacingTo(new Vector2(20, 0), remainingTime(), Easing.Out);
@@ -212,17 +212,17 @@ namespace osu.Game.Screens.Menu
lineBottomLeft.MoveTo(new Vector2(-line_end_offset, line_end_offset), line_duration, Easing.OutQuint);
lineBottomRight.MoveTo(new Vector2(line_end_offset, line_end_offset), line_duration, Easing.OutQuint);
- using (BeginDelayedSequence(length * 0.56, true))
+ using (BeginDelayedSequence(length * 0.56))
{
bigRing.ResizeTo(logo_size, 500, Easing.InOutQuint);
bigRing.Foreground.Delay(250).ResizeTo(1, 850, Easing.OutQuint);
- using (BeginDelayedSequence(250, true))
+ using (BeginDelayedSequence(250))
{
backgroundFill.ResizeHeightTo(1, remainingTime(), Easing.InOutQuart);
backgroundFill.RotateTo(-90, remainingTime(), Easing.InOutQuart);
- using (BeginDelayedSequence(50, true))
+ using (BeginDelayedSequence(50))
{
foregroundFill.ResizeWidthTo(1, remainingTime(), Easing.InOutQuart);
foregroundFill.RotateTo(-90, remainingTime(), Easing.InOutQuart);
@@ -239,19 +239,19 @@ namespace osu.Game.Screens.Menu
purpleCircle.Delay(rotation_delay).RotateTo(-180, remainingTime() - rotation_delay, Easing.InOutQuart);
purpleCircle.ResizeTo(circle_size, remainingTime(), Easing.InOutQuart);
- using (BeginDelayedSequence(appear_delay, true))
+ using (BeginDelayedSequence(appear_delay))
{
yellowCircle.MoveToY(-circle_size / 2, remainingTime(), Easing.InOutQuart);
yellowCircle.Delay(rotation_delay).RotateTo(-180, remainingTime() - rotation_delay, Easing.InOutQuart);
yellowCircle.ResizeTo(circle_size, remainingTime(), Easing.InOutQuart);
- using (BeginDelayedSequence(appear_delay, true))
+ using (BeginDelayedSequence(appear_delay))
{
blueCircle.MoveToX(-circle_size / 2, remainingTime(), Easing.InOutQuart);
blueCircle.Delay(rotation_delay).RotateTo(-180, remainingTime() - rotation_delay, Easing.InOutQuart);
blueCircle.ResizeTo(circle_size, remainingTime(), Easing.InOutQuart);
- using (BeginDelayedSequence(appear_delay, true))
+ using (BeginDelayedSequence(appear_delay))
{
pinkCircle.MoveToX(circle_size / 2, remainingTime(), Easing.InOutQuart);
pinkCircle.Delay(rotation_delay).RotateTo(-180, remainingTime() - rotation_delay, Easing.InOutQuart);
diff --git a/osu.Game/Screens/Menu/IntroTriangles.cs b/osu.Game/Screens/Menu/IntroTriangles.cs
index abe6c62461..0ea83fe5e7 100644
--- a/osu.Game/Screens/Menu/IntroTriangles.cs
+++ b/osu.Game/Screens/Menu/IntroTriangles.cs
@@ -172,27 +172,27 @@ namespace osu.Game.Screens.Menu
lazerLogo.Hide();
background.ApplyToBackground(b => b.Hide());
- using (BeginAbsoluteSequence(0, true))
+ using (BeginAbsoluteSequence(0))
{
- using (BeginDelayedSequence(text_1, true))
+ using (BeginDelayedSequence(text_1))
welcomeText.FadeIn().OnComplete(t => t.Text = "wel");
- using (BeginDelayedSequence(text_2, true))
+ using (BeginDelayedSequence(text_2))
welcomeText.FadeIn().OnComplete(t => t.Text = "welcome");
- using (BeginDelayedSequence(text_3, true))
+ using (BeginDelayedSequence(text_3))
welcomeText.FadeIn().OnComplete(t => t.Text = "welcome to");
- using (BeginDelayedSequence(text_4, true))
+ using (BeginDelayedSequence(text_4))
{
welcomeText.FadeIn().OnComplete(t => t.Text = "welcome to osu!");
welcomeText.TransformTo(nameof(welcomeText.Spacing), new Vector2(50, 0), 5000);
}
- using (BeginDelayedSequence(text_glitch, true))
+ using (BeginDelayedSequence(text_glitch))
triangles.FadeIn();
- using (BeginDelayedSequence(rulesets_1, true))
+ using (BeginDelayedSequence(rulesets_1))
{
rulesetsScale.ScaleTo(0.8f, 1000);
rulesets.FadeIn().ScaleTo(1).TransformSpacingTo(new Vector2(200, 0));
@@ -200,18 +200,18 @@ namespace osu.Game.Screens.Menu
triangles.FadeOut();
}
- using (BeginDelayedSequence(rulesets_2, true))
+ using (BeginDelayedSequence(rulesets_2))
{
rulesets.ScaleTo(2).TransformSpacingTo(new Vector2(30, 0));
}
- using (BeginDelayedSequence(rulesets_3, true))
+ using (BeginDelayedSequence(rulesets_3))
{
rulesets.ScaleTo(4).TransformSpacingTo(new Vector2(10, 0));
rulesetsScale.ScaleTo(1.3f, 1000);
}
- using (BeginDelayedSequence(logo_1, true))
+ using (BeginDelayedSequence(logo_1))
{
rulesets.FadeOut();
@@ -223,7 +223,7 @@ namespace osu.Game.Screens.Menu
logoContainerSecondary.ScaleTo(scale_start).Then().ScaleTo(scale_start - scale_adjust * 0.25f, logo_scale_duration, Easing.InQuad);
}
- using (BeginDelayedSequence(logo_2, true))
+ using (BeginDelayedSequence(logo_2))
{
lazerLogo.FadeOut().OnComplete(_ =>
{
diff --git a/osu.Game/Screens/Menu/IntroWelcome.cs b/osu.Game/Screens/Menu/IntroWelcome.cs
index 521e863683..f74043b045 100644
--- a/osu.Game/Screens/Menu/IntroWelcome.cs
+++ b/osu.Game/Screens/Menu/IntroWelcome.cs
@@ -143,7 +143,7 @@ namespace osu.Game.Screens.Menu
{
base.LoadComplete();
- using (BeginDelayedSequence(0, true))
+ using (BeginDelayedSequence(0))
{
scaleContainer.ScaleTo(0.9f).ScaleTo(1, delay_step_two).OnComplete(_ => Expire());
scaleContainer.FadeInFromZero(1800);
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs
index fe9979b161..2e180f31fd 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs
@@ -93,7 +93,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
RelativeSizeAxes = Axes.Both,
RowDimensions = new[]
{
- new Dimension(GridSizeMode.Distributed),
+ new Dimension(),
new Dimension(GridSizeMode.AutoSize),
},
Content = new[]
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs
index 1bbe49a705..043cce4630 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs
@@ -125,9 +125,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
{
const float padding = 44; // enough margin to avoid the hit error display.
- leaderboard.Position = new Vector2(
- padding,
- padding + HUDOverlay.TopScoringElementsHeight);
+ leaderboard.Position = new Vector2(padding, padding + HUDOverlay.TopScoringElementsHeight);
}
private void onMatchStarted() => Scheduler.Add(() =>
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/CatchUpSyncManager.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/CatchUpSyncManager.cs
index 94278a47b6..cf0dfbb585 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/CatchUpSyncManager.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/CatchUpSyncManager.cs
@@ -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.
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();
continue;
}
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorLeaderboard.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorLeaderboard.cs
index ab3ead68b5..55c4270c70 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorLeaderboard.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorLeaderboard.cs
@@ -19,7 +19,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
public void AddClock(int userId, IClock clock)
{
if (!UserScores.TryGetValue(userId, out var data))
- return;
+ throw new ArgumentException(@"Provided user is not tracked by this leaderboard", nameof(userId));
((SpectatingTrackedUserData)data).Clock = clock;
}
@@ -27,7 +27,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
public void RemoveClock(int userId)
{
if (!UserScores.TryGetValue(userId, out var data))
- return;
+ throw new ArgumentException(@"Provided user is not tracked by this leaderboard", nameof(userId));
((SpectatingTrackedUserData)data).Clock = null;
}
diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs
index 5062a296a8..5eb2b545cb 100644
--- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs
+++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs
@@ -75,7 +75,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
RelativeSizeAxes = Axes.Both,
RowDimensions = new[]
{
- new Dimension(GridSizeMode.Distributed),
+ new Dimension(),
new Dimension(GridSizeMode.AutoSize),
},
Content = new[]
diff --git a/osu.Game/Screens/Play/BreakOverlay.cs b/osu.Game/Screens/Play/BreakOverlay.cs
index 36f825b8f6..1665ee83ae 100644
--- a/osu.Game/Screens/Play/BreakOverlay.cs
+++ b/osu.Game/Screens/Play/BreakOverlay.cs
@@ -126,7 +126,7 @@ namespace osu.Game.Screens.Play
if (!b.HasEffect)
continue;
- using (BeginAbsoluteSequence(b.StartTime, true))
+ using (BeginAbsoluteSequence(b.StartTime))
{
fadeContainer.FadeIn(BREAK_FADE_DURATION);
breakArrows.Show(BREAK_FADE_DURATION);
@@ -143,7 +143,7 @@ namespace osu.Game.Screens.Play
remainingTimeCounter.CountTo(b.Duration).CountTo(0, b.Duration);
- using (BeginDelayedSequence(b.Duration - BREAK_FADE_DURATION, true))
+ using (BeginDelayedSequence(b.Duration - BREAK_FADE_DURATION))
{
fadeContainer.FadeOut(BREAK_FADE_DURATION);
breakArrows.Hide(BREAK_FADE_DURATION);
diff --git a/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs b/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs
index acff949353..5c5b66d496 100644
--- a/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs
+++ b/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs
@@ -23,8 +23,6 @@ namespace osu.Game.Screens.Play.HUD
private const double pop_out_duration = 150;
- private const Easing pop_out_easing = Easing.None;
-
private const double fade_out_duration = 100;
///
@@ -170,9 +168,9 @@ namespace osu.Game.Screens.Play.HUD
popOutCount.FadeTo(0.75f);
popOutCount.MoveTo(Vector2.Zero);
- popOutCount.ScaleTo(1, pop_out_duration, pop_out_easing);
- popOutCount.FadeOut(pop_out_duration, pop_out_easing);
- popOutCount.MoveTo(displayedCountSpriteText.Position, pop_out_duration, pop_out_easing);
+ popOutCount.ScaleTo(1, pop_out_duration);
+ popOutCount.FadeOut(pop_out_duration);
+ popOutCount.MoveTo(displayedCountSpriteText.Position, pop_out_duration);
}
private void transformNoPopOut(int newValue)
@@ -186,7 +184,7 @@ namespace osu.Game.Screens.Play.HUD
{
((IHasText)displayedCountSpriteText).Text = formatCount(newValue);
displayedCountSpriteText.ScaleTo(1.1f);
- displayedCountSpriteText.ScaleTo(1, pop_out_duration, pop_out_easing);
+ displayedCountSpriteText.ScaleTo(1, pop_out_duration);
}
private void scheduledPopOutSmall(uint id)
@@ -261,7 +259,7 @@ namespace osu.Game.Screens.Play.HUD
}
private void transformRoll(int currentValue, int newValue) =>
- this.TransformTo(nameof(DisplayedCount), newValue, getProportionalDuration(currentValue, newValue), Easing.None);
+ this.TransformTo(nameof(DisplayedCount), newValue, getProportionalDuration(currentValue, newValue));
private string formatCount(int count) => $@"{count}x";
diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs
index c3bfe19b29..a10c16fcd5 100644
--- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs
+++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs
@@ -55,20 +55,27 @@ namespace osu.Game.Screens.Play.HUD
foreach (var userId in playingUsers)
{
- // probably won't be required in the final implementation.
- var resolvedUser = userLookupCache.GetUserAsync(userId).Result;
-
var trackedUser = CreateUserData(userId, scoreProcessor);
trackedUser.ScoringMode.BindTo(scoringMode);
-
- var leaderboardScore = AddPlayer(resolvedUser, resolvedUser?.Id == api.LocalUser.Value.Id);
- leaderboardScore.Accuracy.BindTo(trackedUser.Accuracy);
- leaderboardScore.TotalScore.BindTo(trackedUser.Score);
- leaderboardScore.Combo.BindTo(trackedUser.CurrentCombo);
- leaderboardScore.HasQuit.BindTo(trackedUser.UserQuit);
-
UserScores[userId] = trackedUser;
}
+
+ userLookupCache.GetUsersAsync(playingUsers.ToArray()).ContinueWith(users => Schedule(() =>
+ {
+ foreach (var user in users.Result)
+ {
+ if (user == null)
+ continue;
+
+ var trackedUser = UserScores[user.Id];
+
+ var leaderboardScore = AddPlayer(user, user.Id == api.LocalUser.Value.Id);
+ leaderboardScore.Accuracy.BindTo(trackedUser.Accuracy);
+ leaderboardScore.TotalScore.BindTo(trackedUser.Score);
+ leaderboardScore.Combo.BindTo(trackedUser.CurrentCombo);
+ leaderboardScore.HasQuit.BindTo(trackedUser.UserQuit);
+ }
+ }));
}
protected override void LoadComplete()
@@ -84,6 +91,8 @@ namespace osu.Game.Screens.Play.HUD
usersChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, new[] { userId }));
}
+ // bind here is to support players leaving the match.
+ // new players are not supported.
playingUsers.BindTo(multiplayerClient.CurrentMatchPlayingUserIds);
playingUsers.BindCollectionChanged(usersChanged);
diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs
index 7c5a06707d..76e9f28dae 100644
--- a/osu.Game/Screens/Play/SubmittingPlayer.cs
+++ b/osu.Game/Screens/Play/SubmittingPlayer.cs
@@ -10,6 +10,7 @@ using osu.Framework.Logging;
using osu.Framework.Screens;
using osu.Game.Online.API;
using osu.Game.Online.Rooms;
+using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
namespace osu.Game.Screens.Play
@@ -144,6 +145,10 @@ namespace osu.Game.Screens.Play
if (scoreSubmissionSource != null)
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();
var request = CreateSubmissionRequest(score, token.Value);
diff --git a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs
index 0a10eee644..4d3f7a4184 100644
--- a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs
+++ b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs
@@ -256,7 +256,7 @@ namespace osu.Game.Screens.Ranking.Expanded
// Score counter value setting must be scheduled so it isn't transferred instantaneously
ScheduleAfterChildren(() =>
{
- using (BeginDelayedSequence(AccuracyCircle.ACCURACY_TRANSFORM_DELAY, true))
+ using (BeginDelayedSequence(AccuracyCircle.ACCURACY_TRANSFORM_DELAY))
{
scoreCounter.FadeIn();
scoreCounter.Current = scoreManager.GetBindableTotalScore(score);
@@ -265,7 +265,7 @@ namespace osu.Game.Screens.Ranking.Expanded
foreach (var stat in statisticDisplays)
{
- using (BeginDelayedSequence(delay, true))
+ using (BeginDelayedSequence(delay))
stat.Appear();
delay += 200;
diff --git a/osu.Game/Screens/Ranking/Expanded/Statistics/ComboStatistic.cs b/osu.Game/Screens/Ranking/Expanded/Statistics/ComboStatistic.cs
index e13138c5a0..b92c244174 100644
--- a/osu.Game/Screens/Ranking/Expanded/Statistics/ComboStatistic.cs
+++ b/osu.Game/Screens/Ranking/Expanded/Statistics/ComboStatistic.cs
@@ -38,7 +38,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Statistics
if (isPerfect)
{
- using (BeginDelayedSequence(AccuracyCircle.ACCURACY_TRANSFORM_DURATION / 2, true))
+ using (BeginDelayedSequence(AccuracyCircle.ACCURACY_TRANSFORM_DURATION / 2))
perfectText.FadeIn(50);
}
}
diff --git a/osu.Game/Screens/Ranking/ScorePanel.cs b/osu.Game/Screens/Ranking/ScorePanel.cs
index f66a998db6..6ddecf8297 100644
--- a/osu.Game/Screens/Ranking/ScorePanel.cs
+++ b/osu.Game/Screens/Ranking/ScorePanel.cs
@@ -234,7 +234,7 @@ namespace osu.Game.Screens.Ranking
bool topLayerExpanded = topLayerContainer.Y < 0;
// If the top layer was already expanded, then we don't need to wait for the resize and can instead transform immediately. This looks better when changing the panel state.
- using (BeginDelayedSequence(topLayerExpanded ? 0 : RESIZE_DURATION + TOP_LAYER_EXPAND_DELAY, true))
+ using (BeginDelayedSequence(topLayerExpanded ? 0 : RESIZE_DURATION + TOP_LAYER_EXPAND_DELAY))
{
topLayerContainer.FadeIn();
diff --git a/osu.Game/Screens/ScreenWhiteBox.cs b/osu.Game/Screens/ScreenWhiteBox.cs
index cf0c183766..8b38b67f5c 100644
--- a/osu.Game/Screens/ScreenWhiteBox.cs
+++ b/osu.Game/Screens/ScreenWhiteBox.cs
@@ -191,7 +191,7 @@ namespace osu.Game.Screens
boxContainer.ScaleTo(0.2f);
boxContainer.RotateTo(-20);
- using (BeginDelayedSequence(300, true))
+ using (BeginDelayedSequence(300))
{
boxContainer.ScaleTo(1, transition_time, Easing.OutElastic);
boxContainer.RotateTo(0, transition_time / 2, Easing.OutQuint);
diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs
index 26da4279f0..973f54c038 100644
--- a/osu.Game/Screens/Select/BeatmapDetails.cs
+++ b/osu.Game/Screens/Select/BeatmapDetails.cs
@@ -1,24 +1,25 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using osuTK;
-using osuTK.Graphics;
+using System.Linq;
using osu.Framework.Allocation;
+using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
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.Extensions.Color4Extensions;
-using osu.Game.Screens.Select.Details;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
+using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
-using osu.Game.Online.API.Requests;
-using osu.Game.Rulesets;
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
{
@@ -128,13 +129,11 @@ namespace osu.Game.Screens.Select
AutoSizeAxes = Axes.Y,
LayoutDuration = transition_duration,
LayoutEasing = Easing.OutQuad,
- Spacing = new Vector2(spacing * 2),
- Margin = new MarginPadding { Top = spacing * 2 },
Children = new[]
{
- description = new MetadataSection("Description"),
- source = new MetadataSection("Source"),
- tags = new MetadataSection("Tags"),
+ description = new MetadataSection(MetadataType.Description),
+ source = new MetadataSection(MetadataType.Source),
+ 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);
- });
- }
- }
}
}
diff --git a/osu.Game/Screens/Spectate/SpectatorScreen.cs b/osu.Game/Screens/Spectate/SpectatorScreen.cs
index 8fc9222f59..b6eafe496f 100644
--- a/osu.Game/Screens/Spectate/SpectatorScreen.cs
+++ b/osu.Game/Screens/Spectate/SpectatorScreen.cs
@@ -5,7 +5,6 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
-using System.Threading.Tasks;
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@@ -61,10 +60,15 @@ namespace osu.Game.Screens.Spectate
{
base.LoadComplete();
- getAllUsers().ContinueWith(users => Schedule(() =>
+ userLookupCache.GetUsersAsync(userIds.ToArray()).ContinueWith(users => Schedule(() =>
{
foreach (var u in users.Result)
+ {
+ if (u == null)
+ continue;
+
userMap[u.Id] = u;
+ }
playingUserStates.BindTo(spectatorClient.PlayingUserStates);
playingUserStates.BindCollectionChanged(onPlayingUserStatesChanged, true);
@@ -77,24 +81,6 @@ namespace osu.Game.Screens.Spectate
}));
}
- private Task getAllUsers()
- {
- var userLookupTasks = new List>();
-
- foreach (var u in userIds)
- {
- userLookupTasks.Add(userLookupCache.GetUserAsync(u).ContinueWith(task =>
- {
- if (!task.IsCompletedSuccessfully)
- return null;
-
- return task.Result;
- }));
- }
-
- return Task.WhenAll(userLookupTasks);
- }
-
private void beatmapUpdated(ValueChangedEvent> e)
{
if (!e.NewValue.TryGetTarget(out var beatmapSet))
diff --git a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs
index 7a35c8600d..17eb88226d 100644
--- a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs
+++ b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs
@@ -95,8 +95,8 @@ namespace osu.Game.Skinning.Editor
// scale adjust applied to each individual item should match that of the quad itself.
var scaledDelta = new Vector2(
- adjustedRect.Width / selectionRect.Width,
- adjustedRect.Height / selectionRect.Height
+ MathF.Max(adjustedRect.Width / selectionRect.Width, 0),
+ MathF.Max(adjustedRect.Height / selectionRect.Height, 0)
);
foreach (var b in SelectedBlueprints)
diff --git a/osu.Game/Skinning/LegacyColourCompatibility.cs b/osu.Game/Skinning/LegacyColourCompatibility.cs
index b842b50426..38e43432ce 100644
--- a/osu.Game/Skinning/LegacyColourCompatibility.cs
+++ b/osu.Game/Skinning/LegacyColourCompatibility.cs
@@ -7,7 +7,7 @@ using osuTK.Graphics;
namespace osu.Game.Skinning
{
///
- /// 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.
///
public static class LegacyColourCompatibility
{
diff --git a/osu.Game/Skinning/LegacyJudgementPieceNew.cs b/osu.Game/Skinning/LegacyJudgementPieceNew.cs
index ca25efaa01..e76f251ce5 100644
--- a/osu.Game/Skinning/LegacyJudgementPieceNew.cs
+++ b/osu.Game/Skinning/LegacyJudgementPieceNew.cs
@@ -72,7 +72,7 @@ namespace osu.Game.Skinning
if (particles != null)
{
// start the particles already some way into their animation to break cluster away from centre.
- using (particles.BeginDelayedSequence(-100, true))
+ using (particles.BeginDelayedSequence(-100))
particles.Restart();
}
diff --git a/osu.Game/Skinning/SkinInfo.cs b/osu.Game/Skinning/SkinInfo.cs
index e30bc16d8b..851d71f914 100644
--- a/osu.Game/Skinning/SkinInfo.cs
+++ b/osu.Game/Skinning/SkinInfo.cs
@@ -46,7 +46,7 @@ namespace osu.Game.Skinning
public static SkinInfo Default { get; } = new SkinInfo
{
ID = DEFAULT_SKIN,
- Name = "osu!lazer",
+ Name = "osu! (triangles)",
Creator = "team osu!",
InstantiationInfo = typeof(DefaultSkin).GetInvariantInstantiationInfo()
};
diff --git a/osu.Game/Tests/Visual/EditorTestScene.cs b/osu.Game/Tests/Visual/EditorTestScene.cs
index d7b02ef797..a393802309 100644
--- a/osu.Game/Tests/Visual/EditorTestScene.cs
+++ b/osu.Game/Tests/Visual/EditorTestScene.cs
@@ -94,7 +94,7 @@ namespace osu.Game.Tests.Visual
private readonly WorkingBeatmap testBeatmap;
public TestBeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, IResourceStore resources, GameHost host, WorkingBeatmap defaultBeatmap, WorkingBeatmap testBeatmap)
- : base(storage, contextFactory, rulesets, api, audioManager, resources, host, defaultBeatmap, false)
+ : base(storage, contextFactory, rulesets, api, audioManager, resources, host, defaultBeatmap)
{
this.testBeatmap = testBeatmap;
}
diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs
index 98aad821ce..57e400a77e 100644
--- a/osu.Game/Tests/Visual/OsuTestScene.cs
+++ b/osu.Game/Tests/Visual/OsuTestScene.cs
@@ -176,7 +176,7 @@ namespace osu.Game.Tests.Visual
protected virtual IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset);
protected WorkingBeatmap CreateWorkingBeatmap(RulesetInfo ruleset) =>
- CreateWorkingBeatmap(CreateBeatmap(ruleset), null);
+ CreateWorkingBeatmap(CreateBeatmap(ruleset));
protected virtual WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null) =>
new ClockBackedTestWorkingBeatmap(beatmap, storyboard, Clock, Audio);
diff --git a/osu.Game/Updater/UpdateManager.cs b/osu.Game/Updater/UpdateManager.cs
index 1c72f3ebe2..98ce2cb46c 100644
--- a/osu.Game/Updater/UpdateManager.cs
+++ b/osu.Game/Updater/UpdateManager.cs
@@ -90,7 +90,7 @@ namespace osu.Game.Updater
public UpdateCompleteNotification(string 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]
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 14efcd516e..8a3c69e40c 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -31,13 +31,13 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
+
+
diff --git a/osu.iOS.props b/osu.iOS.props
index 0ab0b26430..2eea646c61 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -70,8 +70,8 @@
-
-
+
+
@@ -93,7 +93,7 @@
-
+
diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings
index d2c5b1223c..7284ca1a9a 100644
--- a/osu.sln.DotSettings
+++ b/osu.sln.DotSettings
@@ -130,7 +130,7 @@
HINT
WARNING
WARNING
- HINT
+ WARNING
WARNING
WARNING
WARNING