mirror of
https://github.com/ppy/osu.git
synced 2024-11-11 20:07:29 +08:00
Merge branch 'master' into consume-bindable-current-factory
This commit is contained in:
commit
bb58a9412b
@ -27,7 +27,7 @@
|
||||
]
|
||||
},
|
||||
"ppy.localisationanalyser.tools": {
|
||||
"version": "2021.608.0",
|
||||
"version": "2021.705.0",
|
||||
"commands": [
|
||||
"localisation"
|
||||
]
|
||||
|
@ -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
|
||||
|
@ -51,8 +51,8 @@
|
||||
<Reference Include="Java.Interop" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.618.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.702.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.706.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.707.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Transitive Dependencies">
|
||||
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
|
||||
|
@ -5,8 +5,8 @@
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Description>A free-to-win rhythm game. Rhythm is just a *click* away!</Description>
|
||||
<AssemblyName>osu!</AssemblyName>
|
||||
<Title>osu!lazer</Title>
|
||||
<Product>osu!lazer</Product>
|
||||
<Title>osu!</Title>
|
||||
<Product>osu!</Product>
|
||||
<ApplicationIcon>lazer.ico</ApplicationIcon>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<Version>0.0.0</Version>
|
||||
|
@ -3,7 +3,7 @@
|
||||
<metadata>
|
||||
<id>osulazer</id>
|
||||
<version>0.0.0</version>
|
||||
<title>osu!lazer</title>
|
||||
<title>osu!</title>
|
||||
<authors>ppy Pty Ltd</authors>
|
||||
<owners>Dean Herbert</owners>
|
||||
<projectUrl>https://osu.ppy.sh/</projectUrl>
|
||||
@ -20,4 +20,3 @@
|
||||
<file src="**.config" target="lib\net45\"/>
|
||||
</files>
|
||||
</package>
|
||||
|
||||
|
114
osu.Game.Rulesets.Catch.Tests/TestSceneCatchSkinConfiguration.cs
Normal file
114
osu.Game.Rulesets.Catch.Tests/TestSceneCatchSkinConfiguration.cs
Normal file
@ -0,0 +1,114 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Catch.Judgements;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Catch.Skinning;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Tests.Visual;
|
||||
using Direction = osu.Game.Rulesets.Catch.UI.Direction;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
public class TestSceneCatchSkinConfiguration : OsuTestScene
|
||||
{
|
||||
[Cached]
|
||||
private readonly DroppedObjectContainer droppedObjectContainer;
|
||||
|
||||
private Catcher catcher;
|
||||
|
||||
private readonly Container container;
|
||||
|
||||
public TestSceneCatchSkinConfiguration()
|
||||
{
|
||||
Add(droppedObjectContainer = new DroppedObjectContainer());
|
||||
Add(container = new Container { RelativeSizeAxes = Axes.Both });
|
||||
}
|
||||
|
||||
[TestCase(false)]
|
||||
[TestCase(true)]
|
||||
public void TestCatcherPlateFlipping(bool flip)
|
||||
{
|
||||
AddStep("setup catcher", () =>
|
||||
{
|
||||
var skin = new TestSkin { FlipCatcherPlate = flip };
|
||||
container.Child = new SkinProvidingContainer(skin)
|
||||
{
|
||||
Child = catcher = new Catcher(new Container())
|
||||
{
|
||||
Anchor = Anchor.Centre
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
Fruit fruit = new Fruit();
|
||||
|
||||
AddStep("catch fruit", () => catchFruit(fruit, 20));
|
||||
|
||||
float position = 0;
|
||||
|
||||
AddStep("record fruit position", () => position = getCaughtObjectPosition(fruit));
|
||||
|
||||
AddStep("face left", () => catcher.VisualDirection = Direction.Left);
|
||||
|
||||
if (flip)
|
||||
AddAssert("fruit position changed", () => !Precision.AlmostEquals(getCaughtObjectPosition(fruit), position));
|
||||
else
|
||||
AddAssert("fruit position unchanged", () => Precision.AlmostEquals(getCaughtObjectPosition(fruit), position));
|
||||
|
||||
AddStep("face right", () => catcher.VisualDirection = Direction.Right);
|
||||
|
||||
AddAssert("fruit position restored", () => Precision.AlmostEquals(getCaughtObjectPosition(fruit), position));
|
||||
}
|
||||
|
||||
private float getCaughtObjectPosition(Fruit fruit)
|
||||
{
|
||||
var caughtObject = catcher.ChildrenOfType<CaughtObject>().Single(c => c.HitObject == fruit);
|
||||
return caughtObject.Parent.ToSpaceOfOtherDrawable(caughtObject.Position, catcher).X;
|
||||
}
|
||||
|
||||
private void catchFruit(Fruit fruit, float x)
|
||||
{
|
||||
fruit.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||
var drawableFruit = new DrawableFruit(fruit) { X = x };
|
||||
var judgement = fruit.CreateJudgement();
|
||||
catcher.OnNewResult(drawableFruit, new CatchJudgementResult(fruit, judgement)
|
||||
{
|
||||
Type = judgement.MaxResult
|
||||
});
|
||||
}
|
||||
|
||||
private class TestSkin : DefaultSkin
|
||||
{
|
||||
public bool FlipCatcherPlate { get; set; }
|
||||
|
||||
public TestSkin()
|
||||
: base(null)
|
||||
{
|
||||
}
|
||||
|
||||
public override IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
|
||||
{
|
||||
if (lookup is CatchSkinConfiguration config)
|
||||
{
|
||||
if (config == CatchSkinConfiguration.FlipCatcherPlate)
|
||||
return SkinUtils.As<TValue>(new Bindable<bool>(FlipCatcherPlate));
|
||||
}
|
||||
|
||||
return base.GetConfig<TLookup, TValue>(lookup);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -194,9 +194,9 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
AddStep("catch more fruits", () => attemptCatch(() => new Fruit(), 9));
|
||||
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]
|
||||
|
@ -138,7 +138,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
|
||||
AddStep("finish hyper-dashing", () =>
|
||||
{
|
||||
catcherArea.MovableCatcher.SetHyperDashState(1);
|
||||
catcherArea.MovableCatcher.SetHyperDashState();
|
||||
catcherArea.MovableCatcher.FinishTransforms();
|
||||
});
|
||||
|
||||
|
@ -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<CatchHitObject>())
|
||||
nested.OriginalXBindable.Value += deltaX;
|
||||
nested.OriginalX += deltaX;
|
||||
});
|
||||
|
||||
return true;
|
||||
|
@ -1,6 +1,7 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using 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
|
||||
/// <summary>
|
||||
/// The horizontal position of the hit object between 0 and <see cref="CatchPlayfield.WIDTH"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Only setter is exposed.
|
||||
/// Use <see cref="OriginalX"/> or <see cref="EffectiveX"/> to get the horizontal position.
|
||||
/// </remarks>
|
||||
[JsonIgnore]
|
||||
public float X
|
||||
{
|
||||
set => OriginalXBindable.Value = value;
|
||||
@ -34,6 +40,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
/// </summary>
|
||||
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 <see cref="X"/> value specified in the beatmap, not affected by the beatmap processing.
|
||||
/// Use <see cref="EffectiveX"/> for a gameplay.
|
||||
/// </remarks>
|
||||
public float OriginalX => OriginalXBindable.Value;
|
||||
public float OriginalX
|
||||
{
|
||||
get => OriginalXBindable.Value;
|
||||
set => OriginalXBindable.Value = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The effective horizontal position of the hit object between 0 and <see cref="CatchPlayfield.WIDTH"/>.
|
||||
@ -53,9 +64,9 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
/// This value is the original <see cref="X"/> value plus the offset applied by the beatmap processing.
|
||||
/// Use <see cref="OriginalX"/> if a value not affected by the offset is desired.
|
||||
/// </remarks>
|
||||
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<int> IndexInBeatmapBindable = new Bindable<int>();
|
||||
|
||||
|
@ -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; }
|
||||
|
||||
/// <summary>
|
||||
@ -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;
|
||||
|
@ -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
|
||||
/// <summary>
|
||||
/// The target fruit if we are to initiate a hyperdash.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public CatchHitObject HyperDashTarget
|
||||
{
|
||||
get => hyperDashTarget;
|
||||
|
13
osu.Game.Rulesets.Catch/Skinning/CatchSkinConfiguration.cs
Normal file
13
osu.Game.Rulesets.Catch/Skinning/CatchSkinConfiguration.cs
Normal file
@ -0,0 +1,13 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Skinning
|
||||
{
|
||||
public enum CatchSkinConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether the contents of the catcher plate should be visually flipped when the catcher direction is changed.
|
||||
/// </summary>
|
||||
FlipCatcherPlate
|
||||
}
|
||||
}
|
@ -103,6 +103,19 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
||||
|
||||
result.Value = LegacyColourCompatibility.DisallowZeroAlpha(result.Value);
|
||||
return (IBindable<TValue>)result;
|
||||
|
||||
case CatchSkinConfiguration config:
|
||||
switch (config)
|
||||
{
|
||||
case CatchSkinConfiguration.FlipCatcherPlate:
|
||||
// Don't flip catcher plate contents if the catcher is provided by this legacy skin.
|
||||
if (GetDrawableComponent(new CatchSkinComponent(CatchSkinComponents.Catcher)) != null)
|
||||
return (IBindable<TValue>)new Bindable<bool>();
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return base.GetConfig<TLookup, TValue>(lookup);
|
||||
|
@ -56,11 +56,6 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
/// </summary>
|
||||
public double Speed => (Dashing ? 1 : 0.5) * BASE_SPEED * hyperDashModifier;
|
||||
|
||||
/// <summary>
|
||||
/// The amount by which caught fruit should be offset from the plate surface to make them look visually "caught".
|
||||
/// </summary>
|
||||
public const float CAUGHT_FRUIT_VERTICAL_OFFSET = -5;
|
||||
|
||||
/// <summary>
|
||||
/// The amount by which caught fruit should be scaled down to fit on the plate.
|
||||
/// </summary>
|
||||
@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -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);
|
||||
}
|
||||
/// <summary>
|
||||
/// The currently facing direction.
|
||||
/// </summary>
|
||||
public Direction VisualDirection { get; set; } = Direction.Right;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the contents of the catcher plate should be visually flipped when the catcher direction is changed.
|
||||
/// </summary>
|
||||
private bool flipCatcherPlate;
|
||||
|
||||
/// <summary>
|
||||
/// Width of the area that can be used to attempt catches during gameplay.
|
||||
/// </summary>
|
||||
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, bool>(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);
|
||||
|
@ -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);
|
||||
|
@ -121,7 +121,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
case OsuSkinConfiguration osuLookup:
|
||||
if (osuLookup == OsuSkinConfiguration.CursorCentre)
|
||||
return SkinUtils.As<TValue>(new BindableBool(false));
|
||||
return SkinUtils.As<TValue>(new BindableBool());
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
double timeDifference = ApplyModsToTimeDelta(lastFrame.Time, h.StartTime);
|
||||
OsuReplayFrame lastLastFrame = Frames.Count >= 2 ? (OsuReplayFrame)Frames[^2] : null;
|
||||
|
||||
if (timeDifference > 0)
|
||||
{
|
||||
// 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;
|
||||
|
||||
double timeDifference = ApplyModsToTimeDelta(lastFrame.Time, h.StartTime);
|
||||
|
||||
// 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.
|
||||
{
|
||||
// Perform eased movement
|
||||
// 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
|
||||
/// <remarks>
|
||||
/// 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)
|
||||
/// </remarks>
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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(() =>
|
||||
{
|
||||
|
@ -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<RealmKeyBinding> query() => realmContextFactory.Context.All<RealmKeyBinding>();
|
||||
private int queryCount(GlobalAction? match = null)
|
||||
{
|
||||
using (var usage = realmContextFactory.GetForRead())
|
||||
{
|
||||
var results = usage.Realm.All<RealmKeyBinding>();
|
||||
if (match.HasValue)
|
||||
results = results.Where(k => k.ActionInt == (int)match.Value);
|
||||
return results.Count();
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUpdateViaQueriedReference()
|
||||
@ -59,7 +68,9 @@ namespace osu.Game.Tests.Database
|
||||
|
||||
keyBindingStore.Register(testContainer);
|
||||
|
||||
var backBinding = query().Single(k => k.ActionInt == (int)GlobalAction.Back);
|
||||
using (var primaryUsage = realmContextFactory.GetForRead())
|
||||
{
|
||||
var backBinding = primaryUsage.Realm.All<RealmKeyBinding>().Single(k => k.ActionInt == (int)GlobalAction.Back);
|
||||
|
||||
Assert.That(backBinding.KeyCombination.Keys, Is.EquivalentTo(new[] { InputKey.Escape }));
|
||||
|
||||
@ -76,9 +87,10 @@ namespace osu.Game.Tests.Database
|
||||
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);
|
||||
backBinding = primaryUsage.Realm.All<RealmKeyBinding>().Single(k => k.ActionInt == (int)GlobalAction.Back);
|
||||
Assert.That(backBinding.KeyCombination.Keys, Is.EquivalentTo(new[] { InputKey.BackSpace }));
|
||||
}
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
|
@ -0,0 +1,41 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Beatmaps;
|
||||
|
||||
namespace osu.Game.Tests.Localisation
|
||||
{
|
||||
[TestFixture]
|
||||
public class BeatmapMetadataRomanisationTest
|
||||
{
|
||||
[Test]
|
||||
public void TestRomanisation()
|
||||
{
|
||||
var metadata = new BeatmapMetadata
|
||||
{
|
||||
Artist = "Romanised Artist",
|
||||
ArtistUnicode = "Unicode Artist",
|
||||
Title = "Romanised title",
|
||||
TitleUnicode = "Unicode Title"
|
||||
};
|
||||
var romanisableString = metadata.ToRomanisableString();
|
||||
|
||||
Assert.AreEqual(metadata.ToString(), romanisableString.Romanised);
|
||||
Assert.AreEqual($"{metadata.ArtistUnicode} - {metadata.TitleUnicode}", romanisableString.Original);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRomanisationNoUnicode()
|
||||
{
|
||||
var metadata = new BeatmapMetadata
|
||||
{
|
||||
Artist = "Romanised Artist",
|
||||
Title = "Romanised title"
|
||||
};
|
||||
var romanisableString = metadata.ToRomanisableString();
|
||||
|
||||
Assert.AreEqual(romanisableString.Romanised, romanisableString.Original);
|
||||
}
|
||||
}
|
||||
}
|
123
osu.Game.Tests/NonVisual/FirstAvailableHitWindowsTest.cs
Normal file
123
osu.Game.Tests/NonVisual/FirstAvailableHitWindowsTest.cs
Normal file
@ -0,0 +1,123 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Scoring;
|
||||
|
||||
namespace osu.Game.Tests.NonVisual
|
||||
{
|
||||
public class FirstAvailableHitWindowsTest
|
||||
{
|
||||
private TestDrawableRuleset testDrawableRuleset;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
testDrawableRuleset = new TestDrawableRuleset();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestResultIfOnlyParentHitWindowIsEmpty()
|
||||
{
|
||||
var testObject = new TestHitObject(HitWindows.Empty);
|
||||
HitObject nested = new TestHitObject(new HitWindows());
|
||||
testObject.AddNested(nested);
|
||||
testDrawableRuleset.HitObjects = new List<HitObject> { testObject };
|
||||
|
||||
Assert.AreSame(testDrawableRuleset.FirstAvailableHitWindows, nested.HitWindows);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestResultIfParentHitWindowsIsNotEmpty()
|
||||
{
|
||||
var testObject = new TestHitObject(new HitWindows());
|
||||
HitObject nested = new TestHitObject(new HitWindows());
|
||||
testObject.AddNested(nested);
|
||||
testDrawableRuleset.HitObjects = new List<HitObject> { testObject };
|
||||
|
||||
Assert.AreSame(testDrawableRuleset.FirstAvailableHitWindows, testObject.HitWindows);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestResultIfParentAndChildHitWindowsAreEmpty()
|
||||
{
|
||||
var firstObject = new TestHitObject(HitWindows.Empty);
|
||||
HitObject nested = new TestHitObject(HitWindows.Empty);
|
||||
firstObject.AddNested(nested);
|
||||
|
||||
var secondObject = new TestHitObject(new HitWindows());
|
||||
testDrawableRuleset.HitObjects = new List<HitObject> { firstObject, secondObject };
|
||||
|
||||
Assert.AreSame(testDrawableRuleset.FirstAvailableHitWindows, secondObject.HitWindows);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestResultIfAllHitWindowsAreEmpty()
|
||||
{
|
||||
var firstObject = new TestHitObject(HitWindows.Empty);
|
||||
HitObject nested = new TestHitObject(HitWindows.Empty);
|
||||
firstObject.AddNested(nested);
|
||||
|
||||
testDrawableRuleset.HitObjects = new List<HitObject> { firstObject };
|
||||
|
||||
Assert.IsNull(testDrawableRuleset.FirstAvailableHitWindows);
|
||||
}
|
||||
|
||||
[SuppressMessage("ReSharper", "UnassignedGetOnlyAutoProperty")]
|
||||
private class TestDrawableRuleset : DrawableRuleset
|
||||
{
|
||||
public List<HitObject> HitObjects;
|
||||
public override IEnumerable<HitObject> Objects => HitObjects;
|
||||
|
||||
public override event Action<JudgementResult> NewResult;
|
||||
public override event Action<JudgementResult> RevertResult;
|
||||
|
||||
public override Playfield Playfield { get; }
|
||||
public override Container Overlays { get; }
|
||||
public override Container FrameStableComponents { get; }
|
||||
public override IFrameStableClock FrameStableClock { get; }
|
||||
internal override bool FrameStablePlayback { get; set; }
|
||||
public override IReadOnlyList<Mod> Mods { get; }
|
||||
|
||||
public override double GameplayStartTime { get; }
|
||||
public override GameplayCursorContainer Cursor { get; }
|
||||
|
||||
public TestDrawableRuleset()
|
||||
: base(new OsuRuleset())
|
||||
{
|
||||
// won't compile without this.
|
||||
NewResult?.Invoke(null);
|
||||
RevertResult?.Invoke(null);
|
||||
}
|
||||
|
||||
public override void SetReplayScore(Score replayScore) => throw new NotImplementedException();
|
||||
|
||||
public override void SetRecordTarget(Score score) => throw new NotImplementedException();
|
||||
|
||||
public override void RequestResume(Action continueResume) => throw new NotImplementedException();
|
||||
|
||||
public override void CancelResume() => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public class TestHitObject : HitObject
|
||||
{
|
||||
public TestHitObject(HitWindows hitWindows)
|
||||
{
|
||||
HitWindows = hitWindows;
|
||||
HitWindows.SetDifficulty(0.5f);
|
||||
}
|
||||
|
||||
public new void AddNested(HitObject nested) => base.AddNested(nested);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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", () =>
|
||||
|
@ -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)
|
||||
}
|
||||
},
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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()
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used for the sole purpose of adding <see cref="TestMultiplayerClient"/> as a resolvable dependency.
|
||||
/// </summary>
|
||||
|
@ -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<OsuButton>().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<OsuButton>().Single().Enabled.Value);
|
||||
AddUntilStep("ready button enabled", () => button.ChildrenOfType<OsuButton>().Single().Enabled.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<OsuButton>().Single().Enabled.Value == shouldBeEnabled);
|
||||
=> AddUntilStep($"spectate button {(shouldBeEnabled ? "is" : "is not")} enabled", () => spectateButton.ChildrenOfType<OsuButton>().Single().Enabled.Value == shouldBeEnabled);
|
||||
|
||||
private void assertReadyButtonEnablement(bool shouldBeEnabled)
|
||||
=> AddAssert($"ready button {(shouldBeEnabled ? "is" : "is not")} enabled", () => readyButton.ChildrenOfType<OsuButton>().Single().Enabled.Value == shouldBeEnabled);
|
||||
=> AddUntilStep($"ready button {(shouldBeEnabled ? "is" : "is not")} enabled", () => readyButton.ChildrenOfType<OsuButton>().Single().Enabled.Value == shouldBeEnabled);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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<MultiMod>().FirstOrDefault(m => m.Mods.Any(a => a is OsuModDoubleTime));
|
||||
|
||||
var targetMod = conversionMods.FirstOrDefault(m => m is OsuModTarget);
|
||||
|
||||
var easy = easierMods.FirstOrDefault(m => m is OsuModEasy);
|
||||
var 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<ModButton>().Count(b => b.Selected) == 2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUnimplementedModIsUnselectable()
|
||||
{
|
||||
var testRuleset = new TestUnimplementedModOsuRuleset();
|
||||
changeTestRuleset(testRuleset.RulesetInfo);
|
||||
|
||||
var conversionMods = testRuleset.GetModsFor(ModType.Conversion);
|
||||
|
||||
var unimplementedMod = conversionMods.FirstOrDefault(m => m is TestUnimplementedMod);
|
||||
|
||||
testUnimplementedMod(unimplementedMod);
|
||||
}
|
||||
|
||||
private void testSingleMod(Mod mod)
|
||||
{
|
||||
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<Mod> GetModsFor(ModType type)
|
||||
{
|
||||
if (type == ModType.Conversion) return base.GetModsFor(type).Concat(new[] { new TestUnimplementedMod() });
|
||||
|
||||
return base.GetModsFor(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<CounterPill>().First().Alpha == 1);
|
||||
|
||||
|
@ -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
|
||||
{
|
||||
@ -138,9 +138,14 @@ namespace osu.Game.Tournament.Tests.NonVisual
|
||||
Assert.True(storage.Exists(flagFile));
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
host.Storage.Delete("tournament.ini");
|
||||
host.Storage.DeleteDirectory("tournaments");
|
||||
}
|
||||
catch { }
|
||||
|
||||
host.Exit();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
2
osu.Game/.editorconfig
Normal file
2
osu.Game/.editorconfig
Normal file
@ -0,0 +1,2 @@
|
||||
[*.cs]
|
||||
dotnet_diagnostic.OLOC001.prefix_namespace = osu.Game.Resources.Localisation
|
@ -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);
|
||||
|
@ -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]
|
||||
|
@ -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...");
|
||||
|
@ -9,6 +9,7 @@ namespace osu.Game.Database
|
||||
{
|
||||
/// <summary>
|
||||
/// The main realm context, bound to the update thread.
|
||||
/// If querying from a non-update thread is needed, use <see cref="GetForRead"/> or <see cref="GetForWrite"/> to receive a context instead.
|
||||
/// </summary>
|
||||
Realm Context { get; }
|
||||
|
||||
|
@ -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
|
||||
|
@ -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,11 +40,18 @@ namespace osu.Game.Database
|
||||
private static readonly GlobalStatistic<int> pending_writes = GlobalStatistics.Get<int>("Realm", "Pending writes");
|
||||
private static readonly GlobalStatistic<int> active_usages = GlobalStatistics.Get<int>("Realm", "Active usages");
|
||||
|
||||
private readonly object updateContextLock = new object();
|
||||
|
||||
private Realm context;
|
||||
|
||||
public Realm Context
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!ThreadSafety.IsUpdateThread)
|
||||
throw new InvalidOperationException($"Use {nameof(GetForRead)} or {nameof(GetForWrite)} when performing realm operations from a non-update thread");
|
||||
|
||||
lock (updateContextLock)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
@ -55,6 +64,7 @@ namespace osu.Game.Database
|
||||
return context;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public RealmContextFactory(Storage storage)
|
||||
{
|
||||
@ -107,9 +117,12 @@ namespace osu.Game.Database
|
||||
{
|
||||
base.Update();
|
||||
|
||||
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;
|
||||
Realm previousContext;
|
||||
|
||||
lock (updateContextLock)
|
||||
{
|
||||
previousContext = context;
|
||||
context = null;
|
||||
}
|
||||
|
||||
// wait for all threaded usages to finish
|
||||
while (active_usages.Value > 0)
|
||||
|
@ -27,6 +27,30 @@ namespace osu.Game.Database
|
||||
[ItemCanBeNull]
|
||||
public Task<User> GetUserAsync(int userId, CancellationToken token = default) => GetAsync(userId, token);
|
||||
|
||||
/// <summary>
|
||||
/// Perform an API lookup on the specified users, populating a <see cref="User"/> model.
|
||||
/// </summary>
|
||||
/// <param name="userIds">The users to lookup.</param>
|
||||
/// <param name="token">An optional cancellation token.</param>
|
||||
/// <returns>The populated users. May include null results for failed retrievals.</returns>
|
||||
public Task<User[]> GetUsersAsync(int[] userIds, CancellationToken token = default)
|
||||
{
|
||||
var userLookupTasks = new List<Task<User>>();
|
||||
|
||||
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<User> ComputeValueAsync(int lookup, CancellationToken token = default)
|
||||
=> await queryUser(lookup).ConfigureAwait(false);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -1,38 +0,0 @@
|
||||
<root>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>1.3</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="solo" xml:space="preserve">
|
||||
<value>ソロ</value>
|
||||
</data>
|
||||
<data name="playlists" xml:space="preserve">
|
||||
<value>プレイリスト</value>
|
||||
</data>
|
||||
<data name="play" xml:space="preserve">
|
||||
<value>遊ぶ</value>
|
||||
</data>
|
||||
<data name="multi" xml:space="preserve">
|
||||
<value>マルチ</value>
|
||||
</data>
|
||||
<data name="edit" xml:space="preserve">
|
||||
<value>エディット</value>
|
||||
</data>
|
||||
<data name="browse" xml:space="preserve">
|
||||
<value>ブラウズ</value>
|
||||
</data>
|
||||
<data name="exit" xml:space="preserve">
|
||||
<value>閉じる</value>
|
||||
</data>
|
||||
<data name="settings" xml:space="preserve">
|
||||
<value>設定</value>
|
||||
</data>
|
||||
</root>
|
@ -1,88 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="solo" xml:space="preserve">
|
||||
<value>solo</value>
|
||||
</data>
|
||||
<data name="multi" xml:space="preserve">
|
||||
<value>multi</value>
|
||||
</data>
|
||||
<data name="playlists" xml:space="preserve">
|
||||
<value>playlists</value>
|
||||
</data>
|
||||
<data name="play" xml:space="preserve">
|
||||
<value>play</value>
|
||||
</data>
|
||||
<data name="edit" xml:space="preserve">
|
||||
<value>edit</value>
|
||||
</data>
|
||||
<data name="browse" xml:space="preserve">
|
||||
<value>browse</value>
|
||||
</data>
|
||||
<data name="settings" xml:space="preserve">
|
||||
<value>settings</value>
|
||||
</data>
|
||||
<data name="back" xml:space="preserve">
|
||||
<value>back</value>
|
||||
</data>
|
||||
<data name="exit" xml:space="preserve">
|
||||
<value>exit</value>
|
||||
</data>
|
||||
</root>
|
@ -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";
|
||||
|
||||
/// <summary>
|
||||
/// "solo"
|
||||
|
@ -1,67 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="header_title" xml:space="preserve">
|
||||
<value>chat</value>
|
||||
</data>
|
||||
<data name="header_description" xml:space="preserve">
|
||||
<value>join the real-time discussion</value>
|
||||
</data>
|
||||
</root>
|
@ -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";
|
||||
|
||||
/// <summary>
|
||||
/// "chat"
|
||||
|
@ -1,64 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="cancel" xml:space="preserve">
|
||||
<value>Cancel</value>
|
||||
</data>
|
||||
</root>
|
@ -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";
|
||||
|
||||
/// <summary>
|
||||
/// "Cancel"
|
||||
|
@ -1,67 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="header_title" xml:space="preserve">
|
||||
<value>notifications</value>
|
||||
</data>
|
||||
<data name="header_description" xml:space="preserve">
|
||||
<value>waiting for 'ya</value>
|
||||
</data>
|
||||
</root>
|
@ -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";
|
||||
|
||||
/// <summary>
|
||||
/// "notifications"
|
||||
|
@ -1,67 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="header_title" xml:space="preserve">
|
||||
<value>now playing</value>
|
||||
</data>
|
||||
<data name="header_description" xml:space="preserve">
|
||||
<value>manage the currently playing track</value>
|
||||
</data>
|
||||
</root>
|
@ -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";
|
||||
|
||||
/// <summary>
|
||||
/// "now playing"
|
||||
|
@ -1,67 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="header_title" xml:space="preserve">
|
||||
<value>settings</value>
|
||||
</data>
|
||||
<data name="header_description" xml:space="preserve">
|
||||
<value>change the way osu! behaves</value>
|
||||
</data>
|
||||
</root>
|
@ -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";
|
||||
|
||||
/// <summary>
|
||||
/// "settings"
|
||||
|
@ -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;
|
||||
|
@ -320,6 +320,7 @@ namespace osu.Game.Online.Chat
|
||||
JoinMultiplayerMatch,
|
||||
Spectate,
|
||||
OpenUserProfile,
|
||||
SearchBeatmapSet,
|
||||
OpenWiki,
|
||||
Custom,
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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++)
|
||||
|
@ -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
|
||||
/// <param name="beatmapId">The beatmap to show.</param>
|
||||
public void ShowBeatmap(int beatmapId) => waitForReady(() => beatmapSetOverlay, _ => beatmapSetOverlay.FetchAndShowBeatmap(beatmapId));
|
||||
|
||||
/// <summary>
|
||||
/// Shows the beatmap listing overlay, with the given <paramref name="query"/> in the search box.
|
||||
/// </summary>
|
||||
/// <param name="query">The query to search for.</param>
|
||||
public void SearchBeatmapSet(string query) => waitForReady(() => beatmapListing, _ => beatmapListing.ShowWithSearch(query));
|
||||
|
||||
/// <summary>
|
||||
/// Show a wiki's page as an overlay
|
||||
/// </summary>
|
||||
|
@ -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]
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
115
osu.Game/Overlays/BeatmapSet/MetadataSection.cs
Normal file
115
osu.Game/Overlays/BeatmapSet/MetadataSection.cs
Normal file
@ -0,0 +1,115 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Online.Chat;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapSet
|
||||
{
|
||||
public class MetadataSection : Container
|
||||
{
|
||||
private readonly FillFlowContainer textContainer;
|
||||
private readonly MetadataType type;
|
||||
private TextFlowContainer textFlow;
|
||||
|
||||
private const float transition_duration = 250;
|
||||
|
||||
public MetadataSection(MetadataType type)
|
||||
{
|
||||
this.type = type;
|
||||
|
||||
Alpha = 0;
|
||||
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
|
||||
InternalChild = textContainer = new FillFlowContainer
|
||||
{
|
||||
Alpha = 0,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
|
||||
Margin = new MarginPadding { Top = 15 },
|
||||
Spacing = new Vector2(5),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Child = new OsuSpriteText
|
||||
{
|
||||
Text = this.type.ToString(),
|
||||
Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 14),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public string Text
|
||||
{
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
this.FadeOut(transition_duration);
|
||||
return;
|
||||
}
|
||||
|
||||
this.FadeIn(transition_duration);
|
||||
|
||||
setTextAsync(value);
|
||||
}
|
||||
}
|
||||
|
||||
private void setTextAsync(string text)
|
||||
{
|
||||
LoadComponentAsync(new LinkFlowContainer(s => s.Font = s.Font.With(size: 14))
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Colour = Color4.White.Opacity(0.75f),
|
||||
}, loaded =>
|
||||
{
|
||||
textFlow?.Expire();
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case MetadataType.Tags:
|
||||
string[] tags = text.Split(" ");
|
||||
|
||||
for (int i = 0; i <= tags.Length - 1; i++)
|
||||
{
|
||||
loaded.AddLink(tags[i], LinkAction.SearchBeatmapSet, tags[i]);
|
||||
|
||||
if (i != tags.Length - 1)
|
||||
loaded.AddText(" ");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case MetadataType.Source:
|
||||
loaded.AddLink(text, LinkAction.SearchBeatmapSet, text);
|
||||
break;
|
||||
|
||||
default:
|
||||
loaded.AddText(text);
|
||||
break;
|
||||
}
|
||||
|
||||
textContainer.Add(textFlow = loaded);
|
||||
|
||||
// fade in if we haven't yet.
|
||||
textContainer.FadeIn(transition_duration);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
14
osu.Game/Overlays/BeatmapSet/MetadataType.cs
Normal file
14
osu.Game/Overlays/BeatmapSet/MetadataType.cs
Normal file
@ -0,0 +1,14 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapSet
|
||||
{
|
||||
public enum MetadataType
|
||||
{
|
||||
Tags,
|
||||
Source,
|
||||
Description,
|
||||
Genre,
|
||||
Language
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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[]
|
||||
|
@ -13,7 +13,7 @@ namespace osu.Game.Overlays.News
|
||||
|
||||
public Action ShowFrontPage;
|
||||
|
||||
private readonly Bindable<string> article = new Bindable<string>(null);
|
||||
private readonly Bindable<string> article = new Bindable<string>();
|
||||
|
||||
public NewsHeader()
|
||||
{
|
||||
|
@ -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[]
|
||||
{
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
};
|
||||
|
||||
[SettingSource("Direction", "The direction of rotation")]
|
||||
public Bindable<RotationDirection> Direction { get; } = new Bindable<RotationDirection>(RotationDirection.Clockwise);
|
||||
public Bindable<RotationDirection> Direction { get; } = new Bindable<RotationDirection>();
|
||||
|
||||
public override string Name => "Barrel Roll";
|
||||
public override string Acronym => "BR";
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<HitResult>().Where(r => r.AffectsCombo()).Select(r => score.Statistics.GetOrDefault(r)).Sum();
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.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<User> currentUser = new Bindable<User>();
|
||||
private FillFlowContainer fill;
|
||||
|
||||
private readonly List<Drawable> expendableText = new List<Drawable>();
|
||||
|
||||
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,
|
||||
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!",
|
||||
|
@ -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);
|
||||
|
@ -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(_ =>
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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[]
|
||||
|
@ -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(() =>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user