mirror of
https://github.com/ppy/osu.git
synced 2024-11-11 10:07:52 +08:00
Merge branch 'master' into hp-drain-fix-breaks
This commit is contained in:
commit
d4519f74ab
@ -21,10 +21,10 @@
|
||||
]
|
||||
},
|
||||
"ppy.localisationanalyser.tools": {
|
||||
"version": "2023.712.0",
|
||||
"version": "2023.1117.0",
|
||||
"commands": [
|
||||
"localisation"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
20
.github/workflows/ci.yml
vendored
20
.github/workflows/ci.yml
vendored
@ -108,6 +108,12 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup JDK 11
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: microsoft
|
||||
java-version: 11
|
||||
|
||||
- name: Install .NET 6.0.x
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
@ -121,24 +127,14 @@ jobs:
|
||||
|
||||
build-only-ios:
|
||||
name: Build only (iOS)
|
||||
# `macos-13` is required, because Xcode 14.3 is required (see below).
|
||||
# TODO: can be changed to `macos-latest` once `macos-13` becomes latest (currently in beta)
|
||||
# `macos-13` is required, because the newest Microsoft.iOS.Sdk versions require Xcode 14.3.
|
||||
# TODO: can be changed to `macos-latest` once `macos-13` becomes latest (currently in beta: https://github.com/actions/runner-images/tree/main#available-images)
|
||||
runs-on: macos-13
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
# newest Microsoft.iOS.Sdk versions require Xcode 14.3.
|
||||
# 14.3 is currently not the default Xcode version (https://github.com/actions/runner-images/blob/main/images/macos/macos-13-Readme.md#xcode),
|
||||
# so set it manually.
|
||||
# TODO: remove when 14.3 becomes the default Xcode version.
|
||||
- name: Set Xcode version
|
||||
shell: bash
|
||||
run: |
|
||||
sudo xcode-select -s "/Applications/Xcode_14.3.app"
|
||||
echo "MD_APPLE_SDK_ROOT=/Applications/Xcode_14.3.app" >> $GITHUB_ENV
|
||||
|
||||
- name: Install .NET 6.0.x
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
|
4
.github/workflows/diffcalc.yml
vendored
4
.github/workflows/diffcalc.yml
vendored
@ -185,9 +185,11 @@ jobs:
|
||||
|
||||
- name: Add comment environment
|
||||
if: ${{ github.event_name == 'issue_comment' }}
|
||||
env:
|
||||
COMMENT_BODY: ${{ github.event.comment.body }}
|
||||
run: |
|
||||
# Add comment environment
|
||||
echo '${{ github.event.comment.body }}' | sed -r 's/\r$//' | grep -E '^\w+=' | while read -r line; do
|
||||
echo $COMMENT_BODY | sed -r 's/\r$//' | grep -E '^\w+=' | while read -r line; do
|
||||
opt=$(echo ${line} | cut -d '=' -f1)
|
||||
sed -i "s;^${opt}=.*$;${line};" "${{ needs.directory.outputs.GENERATOR_ENV }}"
|
||||
done
|
||||
|
@ -10,7 +10,7 @@
|
||||
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2023.1030.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2023.1121.1" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<!-- Fody does not handle Android build well, and warns when unchanged.
|
||||
|
@ -11,6 +11,7 @@ using osu.Framework.Input.Handlers;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Game;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Game.Overlays.Settings.Sections.Input;
|
||||
using osu.Game.Updater;
|
||||
using osu.Game.Utils;
|
||||
|
||||
@ -97,6 +98,9 @@ namespace osu.Android
|
||||
case AndroidJoystickHandler jh:
|
||||
return new AndroidJoystickSettings(jh);
|
||||
|
||||
case AndroidTouchHandler th:
|
||||
return new TouchSettings(th);
|
||||
|
||||
default:
|
||||
return base.CreateSettingsSubsectionFor(handler);
|
||||
}
|
||||
|
@ -28,9 +28,9 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
|
||||
private class TestLegacySkin : LegacySkin
|
||||
{
|
||||
public TestLegacySkin(SkinInfo skin, IResourceStore<byte[]> storage)
|
||||
public TestLegacySkin(SkinInfo skin, IResourceStore<byte[]> fallbackStore)
|
||||
// Bypass LegacySkinResourceStore to avoid returning null for retrieving files due to bad skin info (SkinInfo.Files = null).
|
||||
: base(skin, null, storage)
|
||||
: base(skin, null, fallbackStore)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -140,7 +140,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor
|
||||
|
||||
AddStep("update hit object path", () =>
|
||||
{
|
||||
hitObject.Path = new SliderPath(PathType.PerfectCurve, new[]
|
||||
hitObject.Path = new SliderPath(PathType.PERFECT_CURVE, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(100, 100),
|
||||
@ -190,16 +190,16 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor
|
||||
[Test]
|
||||
public void TestVertexResampling()
|
||||
{
|
||||
addBlueprintStep(100, 100, new SliderPath(PathType.PerfectCurve, new[]
|
||||
addBlueprintStep(100, 100, new SliderPath(PathType.PERFECT_CURVE, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(100, 100),
|
||||
new Vector2(50, 200),
|
||||
}), 0.5);
|
||||
AddAssert("1 vertex per 1 nested HO", () => getVertices().Count == hitObject.NestedHitObjects.Count);
|
||||
AddAssert("slider path not yet changed", () => hitObject.Path.ControlPoints[0].Type == PathType.PerfectCurve);
|
||||
AddAssert("slider path not yet changed", () => hitObject.Path.ControlPoints[0].Type == PathType.PERFECT_CURVE);
|
||||
addAddVertexSteps(150, 150);
|
||||
AddAssert("slider path change to linear", () => hitObject.Path.ControlPoints[0].Type == PathType.Linear);
|
||||
AddAssert("slider path change to linear", () => hitObject.Path.ControlPoints[0].Type == PathType.LINEAR);
|
||||
}
|
||||
|
||||
private void addBlueprintStep(double time, float x, SliderPath sliderPath, double velocity) => AddStep("add selection blueprint", () =>
|
||||
|
@ -154,7 +154,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
} while (rng.Next(2) != 0);
|
||||
|
||||
int length = sliderPath.ControlPoints.Count - start + 1;
|
||||
sliderPath.ControlPoints[start].Type = length <= 2 ? PathType.Linear : length == 3 ? PathType.PerfectCurve : PathType.Bezier;
|
||||
sliderPath.ControlPoints[start].Type = length <= 2 ? PathType.LINEAR : length == 3 ? PathType.PERFECT_CURVE : PathType.BEZIER;
|
||||
} while (rng.Next(3) != 0);
|
||||
|
||||
if (rng.Next(5) == 0)
|
||||
@ -215,7 +215,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
|
||||
foreach (var point in sliderPath.ControlPoints)
|
||||
{
|
||||
Assert.That(point.Type, Is.EqualTo(PathType.Linear).Or.Null);
|
||||
Assert.That(point.Type, Is.EqualTo(PathType.LINEAR).Or.Null);
|
||||
Assert.That(sliderStartY + point.Position.Y, Is.InRange(0, JuiceStreamPath.OSU_PLAYFIELD_HEIGHT));
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Mods
|
||||
var stream = new JuiceStream
|
||||
{
|
||||
StartTime = 1000,
|
||||
Path = new SliderPath(PathType.Linear, new[]
|
||||
Path = new SliderPath(PathType.LINEAR, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(100, 0),
|
||||
|
@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Mods
|
||||
{
|
||||
X = CatchPlayfield.CENTER_X,
|
||||
StartTime = 3000,
|
||||
Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, Vector2.UnitY * 200 })
|
||||
Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, Vector2.UnitY * 200 })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
beatmap.HitObjects.Add(new JuiceStream
|
||||
{
|
||||
X = CatchPlayfield.CENTER_X - width / 2,
|
||||
Path = new SliderPath(PathType.Linear, new[]
|
||||
Path = new SliderPath(PathType.LINEAR, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(width, 0)
|
||||
|
@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
new JuiceStream
|
||||
{
|
||||
StartTime = 1000,
|
||||
Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(0, -192) }),
|
||||
Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(0, -192) }),
|
||||
X = CatchPlayfield.WIDTH / 2
|
||||
}
|
||||
}
|
||||
|
@ -126,7 +126,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
X = xCoords,
|
||||
StartTime = playfieldTime + 1000,
|
||||
Path = new SliderPath(PathType.Linear, new[]
|
||||
Path = new SliderPath(PathType.LINEAR, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(0, 200)
|
||||
|
@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
new JuiceStream
|
||||
{
|
||||
X = CatchPlayfield.CENTER_X,
|
||||
Path = new SliderPath(PathType.Linear, new[]
|
||||
Path = new SliderPath(PathType.LINEAR, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(0, 100)
|
||||
|
@ -74,7 +74,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components
|
||||
path.ConvertFromSliderPath(sliderPath, hitObject.Velocity);
|
||||
|
||||
// If the original slider path has non-linear type segments, resample the vertices at nested hit object times to reduce the number of vertices.
|
||||
if (sliderPath.ControlPoints.Any(p => p.Type != null && p.Type != PathType.Linear))
|
||||
if (sliderPath.ControlPoints.Any(p => p.Type != null && p.Type != PathType.LINEAR))
|
||||
{
|
||||
path.ResampleVertices(hitObject.NestedHitObjects
|
||||
.Skip(1).TakeWhile(h => !(h is Fruit)) // Only droplets in the first span are used.
|
||||
|
@ -236,7 +236,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
|
||||
for (int i = 1; i < vertices.Count; i++)
|
||||
{
|
||||
sliderPath.ControlPoints[^1].Type = PathType.Linear;
|
||||
sliderPath.ControlPoints[^1].Type = PathType.LINEAR;
|
||||
|
||||
float deltaX = vertices[i].X - lastPosition.X;
|
||||
double length = (vertices[i].Time - currentTime) * velocity;
|
||||
|
@ -0,0 +1,94 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Tests.Editor
|
||||
{
|
||||
public partial class TestSceneOpenEditorTimestampInMania : EditorTestScene
|
||||
{
|
||||
protected override Ruleset CreateEditorRuleset() => new ManiaRuleset();
|
||||
|
||||
[Test]
|
||||
public void TestNormalSelection()
|
||||
{
|
||||
addStepClickLink("00:05:920 (5920|3,6623|3,6857|2,7326|1)");
|
||||
AddAssert("selected group", () => checkSnapAndSelectColumn(5_920, new List<(int, int)>
|
||||
{ (5_920, 3), (6_623, 3), (6_857, 2), (7_326, 1) }
|
||||
));
|
||||
|
||||
addReset();
|
||||
addStepClickLink("00:42:716 (42716|3,43420|2,44123|0,44357|1,45295|1)");
|
||||
AddAssert("selected ungrouped", () => checkSnapAndSelectColumn(42_716, new List<(int, int)>
|
||||
{ (42_716, 3), (43_420, 2), (44_123, 0), (44_357, 1), (45_295, 1) }
|
||||
));
|
||||
|
||||
addReset();
|
||||
AddStep("add notes to row", () =>
|
||||
{
|
||||
if (EditorBeatmap.HitObjects.Any(x => x is ManiaHitObject m && m.StartTime == 11_545 && m.Column is 1 or 2 or 3))
|
||||
return;
|
||||
|
||||
ManiaHitObject first = (ManiaHitObject)EditorBeatmap.HitObjects.First(x => x is ManiaHitObject m && m.StartTime == 11_545 && m.Column == 0);
|
||||
ManiaHitObject second = new Note { Column = 1, StartTime = first.StartTime };
|
||||
ManiaHitObject third = new Note { Column = 2, StartTime = first.StartTime };
|
||||
ManiaHitObject forth = new Note { Column = 3, StartTime = first.StartTime };
|
||||
EditorBeatmap.AddRange(new[] { second, third, forth });
|
||||
});
|
||||
addStepClickLink("00:11:545 (11545|0,11545|1,11545|2,11545|3)");
|
||||
AddAssert("selected in row", () => checkSnapAndSelectColumn(11_545, new List<(int, int)>
|
||||
{ (11_545, 0), (11_545, 1), (11_545, 2), (11_545, 3) }
|
||||
));
|
||||
|
||||
addReset();
|
||||
addStepClickLink("01:36:623 (96623|1,97560|1,97677|1,97795|1,98966|1)");
|
||||
AddAssert("selected in column", () => checkSnapAndSelectColumn(96_623, new List<(int, int)>
|
||||
{ (96_623, 1), (97_560, 1), (97_677, 1), (97_795, 1), (98_966, 1) }
|
||||
));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUnusualSelection()
|
||||
{
|
||||
addStepClickLink("00:00:000 (0|1)", "wrong offset");
|
||||
AddAssert("snap to 1, select none", () => checkSnapAndSelectColumn(2_170));
|
||||
|
||||
addReset();
|
||||
addStepClickLink("00:00:000 (2)", "std link");
|
||||
AddAssert("snap to 1, select none", () => checkSnapAndSelectColumn(2_170));
|
||||
|
||||
addReset();
|
||||
addStepClickLink("00:00:000 (1,2)", "std link");
|
||||
AddAssert("snap to 1, select none", () => checkSnapAndSelectColumn(2_170));
|
||||
}
|
||||
|
||||
private void addStepClickLink(string timestamp, string step = "", bool displayTimestamp = true)
|
||||
{
|
||||
AddStep(displayTimestamp ? $"{step} {timestamp}" : step, () => Editor.HandleTimestamp(timestamp));
|
||||
AddUntilStep("wait for seek", () => EditorClock.SeekingOrStopped.Value);
|
||||
}
|
||||
|
||||
private void addReset() => addStepClickLink("00:00:000", "reset", false);
|
||||
|
||||
private bool checkSnapAndSelectColumn(double startTime, IReadOnlyCollection<(int, int)>? columnPairs = null)
|
||||
{
|
||||
bool checkColumns = columnPairs != null
|
||||
? EditorBeatmap.SelectedHitObjects.All(x => columnPairs.Any(col => isNoteAt(x, col.Item1, col.Item2)))
|
||||
: !EditorBeatmap.SelectedHitObjects.Any();
|
||||
|
||||
return EditorClock.CurrentTime == startTime
|
||||
&& EditorBeatmap.SelectedHitObjects.Count == (columnPairs?.Count ?? 0)
|
||||
&& checkColumns;
|
||||
}
|
||||
|
||||
private bool isNoteAt(HitObject hitObject, double time, int column) =>
|
||||
hitObject is ManiaHitObject maniaHitObject
|
||||
&& maniaHitObject.StartTime == time
|
||||
&& maniaHitObject.Column == column;
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Edit.Tools;
|
||||
@ -50,5 +51,37 @@ namespace osu.Game.Rulesets.Mania.Edit
|
||||
|
||||
public override string ConvertSelectionToString()
|
||||
=> string.Join(',', EditorBeatmap.SelectedHitObjects.Cast<ManiaHitObject>().OrderBy(h => h.StartTime).Select(h => $"{h.StartTime}|{h.Column}"));
|
||||
|
||||
// 123|0,456|1,789|2 ...
|
||||
private static readonly Regex selection_regex = new Regex(@"^\d+\|\d+(,\d+\|\d+)*$", RegexOptions.Compiled);
|
||||
|
||||
public override void SelectFromTimestamp(double timestamp, string objectDescription)
|
||||
{
|
||||
if (!selection_regex.IsMatch(objectDescription))
|
||||
return;
|
||||
|
||||
List<ManiaHitObject> remainingHitObjects = EditorBeatmap.HitObjects.Cast<ManiaHitObject>().Where(h => h.StartTime >= timestamp).ToList();
|
||||
string[] objectDescriptions = objectDescription.Split(',').ToArray();
|
||||
|
||||
for (int i = 0; i < objectDescriptions.Length; i++)
|
||||
{
|
||||
string[] split = objectDescriptions[i].Split('|').ToArray();
|
||||
if (split.Length != 2)
|
||||
continue;
|
||||
|
||||
if (!double.TryParse(split[0], out double time) || !int.TryParse(split[1], out int column))
|
||||
continue;
|
||||
|
||||
ManiaHitObject current = remainingHitObjects.FirstOrDefault(h => h.StartTime == time && h.Column == column);
|
||||
|
||||
if (current == null)
|
||||
continue;
|
||||
|
||||
EditorBeatmap.SelectedHitObjects.Add(current);
|
||||
|
||||
if (i < objectDescriptions.Length - 1)
|
||||
remainingHitObjects = remainingHitObjects.Where(h => h != current && h.StartTime >= current.StartTime).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -107,7 +107,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks
|
||||
Position = new Vector2(420, 240),
|
||||
Path = new SliderPath(new[]
|
||||
{
|
||||
new PathControlPoint(new Vector2(0, 0), PathType.Linear),
|
||||
new PathControlPoint(new Vector2(0, 0), PathType.LINEAR),
|
||||
new PathControlPoint(new Vector2(-100, 0))
|
||||
}),
|
||||
}
|
||||
@ -128,7 +128,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks
|
||||
Position = playfield_centre,
|
||||
Path = new SliderPath(new[]
|
||||
{
|
||||
new PathControlPoint(new Vector2(0, 0), PathType.Linear),
|
||||
new PathControlPoint(new Vector2(0, 0), PathType.LINEAR),
|
||||
new PathControlPoint(new Vector2(0, -playfield_centre.Y + 5))
|
||||
}),
|
||||
}
|
||||
@ -149,7 +149,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks
|
||||
Position = playfield_centre,
|
||||
Path = new SliderPath(new[]
|
||||
{
|
||||
new PathControlPoint(new Vector2(0, 0), PathType.Linear),
|
||||
new PathControlPoint(new Vector2(0, 0), PathType.LINEAR),
|
||||
new PathControlPoint(new Vector2(0, -playfield_centre.Y + 5))
|
||||
}),
|
||||
StackHeight = 5
|
||||
@ -171,7 +171,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks
|
||||
Position = new Vector2(0, 0),
|
||||
Path = new SliderPath(new[]
|
||||
{
|
||||
new PathControlPoint(new Vector2(0, 0), PathType.Linear),
|
||||
new PathControlPoint(new Vector2(0, 0), PathType.LINEAR),
|
||||
new PathControlPoint(playfield_centre)
|
||||
}),
|
||||
}
|
||||
@ -192,7 +192,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks
|
||||
Position = playfield_centre,
|
||||
Path = new SliderPath(new[]
|
||||
{
|
||||
new PathControlPoint(new Vector2(0, 0), PathType.Linear),
|
||||
new PathControlPoint(new Vector2(0, 0), PathType.LINEAR),
|
||||
new PathControlPoint(-playfield_centre)
|
||||
}),
|
||||
}
|
||||
@ -214,7 +214,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks
|
||||
Path = new SliderPath(new[]
|
||||
{
|
||||
// Circular arc shoots over the top of the screen.
|
||||
new PathControlPoint(new Vector2(0, 0), PathType.PerfectCurve),
|
||||
new PathControlPoint(new Vector2(0, 0), PathType.PERFECT_CURVE),
|
||||
new PathControlPoint(new Vector2(-100, -200)),
|
||||
new PathControlPoint(new Vector2(100, -200))
|
||||
}),
|
||||
|
@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
mergeSelection();
|
||||
|
||||
AddAssert("slider created", () => circle1 is not null && circle2 is not null && sliderCreatedFor(
|
||||
(pos: circle1.Position, pathType: PathType.Linear),
|
||||
(pos: circle1.Position, pathType: PathType.LINEAR),
|
||||
(pos: circle2.Position, pathType: null)));
|
||||
|
||||
AddStep("undo", () => Editor.Undo());
|
||||
@ -73,11 +73,11 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
|
||||
var controlPoints = slider.Path.ControlPoints;
|
||||
(Vector2, PathType?)[] args = new (Vector2, PathType?)[controlPoints.Count + 2];
|
||||
args[0] = (circle1.Position, PathType.Linear);
|
||||
args[0] = (circle1.Position, PathType.LINEAR);
|
||||
|
||||
for (int i = 0; i < controlPoints.Count; i++)
|
||||
{
|
||||
args[i + 1] = (controlPoints[i].Position + slider.Position, i == controlPoints.Count - 1 ? PathType.Linear : controlPoints[i].Type);
|
||||
args[i + 1] = (controlPoints[i].Position + slider.Position, i == controlPoints.Count - 1 ? PathType.LINEAR : controlPoints[i].Type);
|
||||
}
|
||||
|
||||
args[^1] = (circle2.Position, null);
|
||||
@ -172,7 +172,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
mergeSelection();
|
||||
|
||||
AddAssert("slider created", () => circle1 is not null && circle2 is not null && sliderCreatedFor(
|
||||
(pos: circle1.Position, pathType: PathType.Linear),
|
||||
(pos: circle1.Position, pathType: PathType.LINEAR),
|
||||
(pos: circle2.Position, pathType: null)));
|
||||
|
||||
AddAssert("samples exist", sliderSampleExist);
|
||||
@ -227,7 +227,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
mergeSelection();
|
||||
|
||||
AddAssert("slider created", () => circle1 is not null && circle2 is not null && sliderCreatedFor(
|
||||
(pos: circle1.Position, pathType: PathType.Linear),
|
||||
(pos: circle1.Position, pathType: PathType.LINEAR),
|
||||
(pos: circle2.Position, pathType: null)));
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,91 @@
|
||||
// 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.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
{
|
||||
public partial class TestSceneOpenEditorTimestampInOsu : EditorTestScene
|
||||
{
|
||||
protected override Ruleset CreateEditorRuleset() => new OsuRuleset();
|
||||
|
||||
[Test]
|
||||
public void TestNormalSelection()
|
||||
{
|
||||
addStepClickLink("00:02:170 (1,2,3)");
|
||||
checkSelection(() => 2_170, 1, 2, 3);
|
||||
|
||||
addReset();
|
||||
addStepClickLink("00:04:748 (2,3,4,1,2)");
|
||||
checkSelection(() => 4_748, 2, 3, 4, 1, 2);
|
||||
|
||||
addReset();
|
||||
addStepClickLink("00:02:170 (1,1,1)");
|
||||
checkSelection(() => 2_170, 1, 1, 1);
|
||||
|
||||
addReset();
|
||||
addStepClickLink("00:02:873 (2,2,2,2)");
|
||||
checkSelection(() => 2_873, 2, 2, 2, 2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUnusualSelection()
|
||||
{
|
||||
HitObject firstObject = null!;
|
||||
|
||||
AddStep("retrieve first object", () => firstObject = EditorBeatmap.HitObjects.First());
|
||||
|
||||
addStepClickLink("00:00:000 (0)", "invalid combo");
|
||||
checkSelection(() => firstObject.StartTime);
|
||||
|
||||
addReset();
|
||||
addStepClickLink("00:00:000 (1)", "wrong offset");
|
||||
checkSelection(() => firstObject.StartTime, 1);
|
||||
|
||||
addReset();
|
||||
addStepClickLink("00:00:956 (2,3,4)", "wrong offset");
|
||||
checkSelection(() => firstObject.StartTime, 2, 3, 4);
|
||||
|
||||
addReset();
|
||||
addStepClickLink("00:00:956 (956|1,956|2)", "mania link");
|
||||
checkSelection(() => firstObject.StartTime);
|
||||
}
|
||||
|
||||
private void addReset() => addStepClickLink("00:00:000", "reset", false);
|
||||
|
||||
private void addStepClickLink(string timestamp, string step = "", bool displayTimestamp = true)
|
||||
{
|
||||
AddStep(displayTimestamp ? $"{step} {timestamp}" : step, () => Editor.HandleTimestamp(timestamp));
|
||||
AddUntilStep("wait for seek", () => EditorClock.SeekingOrStopped.Value);
|
||||
}
|
||||
|
||||
private void checkSelection(Func<double> startTime, params int[] comboNumbers)
|
||||
=> AddUntilStep($"seeked & selected {(comboNumbers.Any() ? string.Join(",", comboNumbers) : "nothing")}", () =>
|
||||
{
|
||||
bool checkCombos = comboNumbers.Any()
|
||||
? hasCombosInOrder(EditorBeatmap.SelectedHitObjects, comboNumbers)
|
||||
: !EditorBeatmap.SelectedHitObjects.Any();
|
||||
|
||||
return EditorClock.CurrentTime == startTime()
|
||||
&& EditorBeatmap.SelectedHitObjects.Count == comboNumbers.Length
|
||||
&& checkCombos;
|
||||
});
|
||||
|
||||
private bool hasCombosInOrder(IEnumerable<HitObject> selected, params int[] comboNumbers)
|
||||
{
|
||||
List<HitObject> hitObjects = selected.ToList();
|
||||
if (hitObjects.Count != comboNumbers.Length)
|
||||
return false;
|
||||
|
||||
return !hitObjects.Select(x => (OsuHitObject)x)
|
||||
.Where((x, i) => x.IndexInCurrentCombo + 1 != comboNumbers[i])
|
||||
.Any();
|
||||
}
|
||||
}
|
||||
}
|
@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
|
||||
PathControlPoint[] points =
|
||||
{
|
||||
new PathControlPoint(new Vector2(0), PathType.PerfectCurve),
|
||||
new PathControlPoint(new Vector2(0), PathType.PERFECT_CURVE),
|
||||
new PathControlPoint(new Vector2(-100, 0)),
|
||||
new PathControlPoint(new Vector2(100, 20))
|
||||
};
|
||||
|
@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
{
|
||||
createVisualiser(true);
|
||||
|
||||
addControlPointStep(new Vector2(200), PathType.Bezier);
|
||||
addControlPointStep(new Vector2(200), PathType.BEZIER);
|
||||
addControlPointStep(new Vector2(300));
|
||||
addControlPointStep(new Vector2(500, 300));
|
||||
addControlPointStep(new Vector2(700, 200));
|
||||
@ -63,9 +63,9 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
AddStep("select control point", () => visualiser.Pieces[1].IsSelected.Value = true);
|
||||
addContextMenuItemStep("Perfect curve");
|
||||
|
||||
assertControlPointPathType(0, PathType.Bezier);
|
||||
assertControlPointPathType(1, PathType.PerfectCurve);
|
||||
assertControlPointPathType(3, PathType.Bezier);
|
||||
assertControlPointPathType(0, PathType.BEZIER);
|
||||
assertControlPointPathType(1, PathType.PERFECT_CURVE);
|
||||
assertControlPointPathType(3, PathType.BEZIER);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
{
|
||||
createVisualiser(true);
|
||||
|
||||
addControlPointStep(new Vector2(200), PathType.Bezier);
|
||||
addControlPointStep(new Vector2(200), PathType.BEZIER);
|
||||
addControlPointStep(new Vector2(300));
|
||||
addControlPointStep(new Vector2(500, 300));
|
||||
addControlPointStep(new Vector2(700, 200));
|
||||
@ -83,8 +83,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
AddStep("select control point", () => visualiser.Pieces[2].IsSelected.Value = true);
|
||||
addContextMenuItemStep("Perfect curve");
|
||||
|
||||
assertControlPointPathType(0, PathType.Bezier);
|
||||
assertControlPointPathType(2, PathType.PerfectCurve);
|
||||
assertControlPointPathType(0, PathType.BEZIER);
|
||||
assertControlPointPathType(2, PathType.PERFECT_CURVE);
|
||||
assertControlPointPathType(4, null);
|
||||
}
|
||||
|
||||
@ -93,7 +93,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
{
|
||||
createVisualiser(true);
|
||||
|
||||
addControlPointStep(new Vector2(200), PathType.Bezier);
|
||||
addControlPointStep(new Vector2(200), PathType.BEZIER);
|
||||
addControlPointStep(new Vector2(300));
|
||||
addControlPointStep(new Vector2(500, 300));
|
||||
addControlPointStep(new Vector2(700, 200));
|
||||
@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
AddStep("select control point", () => visualiser.Pieces[3].IsSelected.Value = true);
|
||||
addContextMenuItemStep("Perfect curve");
|
||||
|
||||
assertControlPointPathType(0, PathType.Bezier);
|
||||
assertControlPointPathType(0, PathType.BEZIER);
|
||||
AddAssert("point 3 is not inherited", () => slider.Path.ControlPoints[3].Type != null);
|
||||
}
|
||||
|
||||
@ -112,7 +112,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
{
|
||||
createVisualiser(true);
|
||||
|
||||
addControlPointStep(new Vector2(200), PathType.Linear);
|
||||
addControlPointStep(new Vector2(200), PathType.LINEAR);
|
||||
addControlPointStep(new Vector2(300));
|
||||
addControlPointStep(new Vector2(500, 300));
|
||||
addControlPointStep(new Vector2(700, 200));
|
||||
@ -123,9 +123,9 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
AddStep("select control point", () => visualiser.Pieces[1].IsSelected.Value = true);
|
||||
addContextMenuItemStep("Perfect curve");
|
||||
|
||||
assertControlPointPathType(0, PathType.Linear);
|
||||
assertControlPointPathType(1, PathType.PerfectCurve);
|
||||
assertControlPointPathType(3, PathType.Linear);
|
||||
assertControlPointPathType(0, PathType.LINEAR);
|
||||
assertControlPointPathType(1, PathType.PERFECT_CURVE);
|
||||
assertControlPointPathType(3, PathType.LINEAR);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -133,21 +133,45 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
{
|
||||
createVisualiser(true);
|
||||
|
||||
addControlPointStep(new Vector2(200), PathType.Bezier);
|
||||
addControlPointStep(new Vector2(300), PathType.PerfectCurve);
|
||||
addControlPointStep(new Vector2(200), PathType.BEZIER);
|
||||
addControlPointStep(new Vector2(300), PathType.PERFECT_CURVE);
|
||||
addControlPointStep(new Vector2(500, 300));
|
||||
addControlPointStep(new Vector2(700, 200), PathType.Bezier);
|
||||
addControlPointStep(new Vector2(700, 200), PathType.BEZIER);
|
||||
addControlPointStep(new Vector2(500, 100));
|
||||
|
||||
moveMouseToControlPoint(3);
|
||||
AddStep("select control point", () => visualiser.Pieces[3].IsSelected.Value = true);
|
||||
addContextMenuItemStep("Inherit");
|
||||
|
||||
assertControlPointPathType(0, PathType.Bezier);
|
||||
assertControlPointPathType(1, PathType.Bezier);
|
||||
assertControlPointPathType(0, PathType.BEZIER);
|
||||
assertControlPointPathType(1, PathType.BEZIER);
|
||||
assertControlPointPathType(3, null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCatmullAvailableIffSelectionContainsCatmull()
|
||||
{
|
||||
createVisualiser(true);
|
||||
|
||||
addControlPointStep(new Vector2(200), PathType.CATMULL);
|
||||
addControlPointStep(new Vector2(300));
|
||||
addControlPointStep(new Vector2(500, 300));
|
||||
addControlPointStep(new Vector2(700, 200));
|
||||
addControlPointStep(new Vector2(500, 100));
|
||||
|
||||
moveMouseToControlPoint(2);
|
||||
AddStep("select first and third control point", () =>
|
||||
{
|
||||
visualiser.Pieces[0].IsSelected.Value = true;
|
||||
visualiser.Pieces[2].IsSelected.Value = true;
|
||||
});
|
||||
addContextMenuItemStep("Catmull");
|
||||
|
||||
assertControlPointPathType(0, PathType.CATMULL);
|
||||
assertControlPointPathType(2, PathType.CATMULL);
|
||||
assertControlPointPathType(4, null);
|
||||
}
|
||||
|
||||
private void createVisualiser(bool allowSelection) => AddStep("create visualiser", () => Child = visualiser = new PathControlPointVisualiser<Slider>(slider, allowSelection)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
@ -158,7 +182,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
|
||||
private void addControlPointStep(Vector2 position, PathType? type)
|
||||
{
|
||||
AddStep($"add {type} control point at {position}", () =>
|
||||
AddStep($"add {type?.Type} control point at {position}", () =>
|
||||
{
|
||||
slider.Path.ControlPoints.Add(new PathControlPoint(position, type));
|
||||
});
|
||||
|
@ -38,9 +38,9 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
Position = new Vector2(256, 192),
|
||||
Path = new SliderPath(new[]
|
||||
{
|
||||
new PathControlPoint(Vector2.Zero, PathType.PerfectCurve),
|
||||
new PathControlPoint(Vector2.Zero, PathType.PERFECT_CURVE),
|
||||
new PathControlPoint(new Vector2(150, 150)),
|
||||
new PathControlPoint(new Vector2(300, 0), PathType.PerfectCurve),
|
||||
new PathControlPoint(new Vector2(300, 0), PathType.PERFECT_CURVE),
|
||||
new PathControlPoint(new Vector2(400, 0)),
|
||||
new PathControlPoint(new Vector2(400, 150))
|
||||
})
|
||||
@ -182,7 +182,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
AddStep("release", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||
|
||||
assertControlPointPosition(1, new Vector2(150, 50));
|
||||
assertControlPointType(0, PathType.PerfectCurve);
|
||||
assertControlPointType(0, PathType.PERFECT_CURVE);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -210,7 +210,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
AddAssert("three control point pieces selected", () => this.ChildrenOfType<PathControlPointPiece<Slider>>().Count(piece => piece.IsSelected.Value) == 3);
|
||||
|
||||
assertControlPointPosition(2, new Vector2(450, 50));
|
||||
assertControlPointType(2, PathType.PerfectCurve);
|
||||
assertControlPointType(2, PathType.PERFECT_CURVE);
|
||||
|
||||
assertControlPointPosition(3, new Vector2(550, 50));
|
||||
|
||||
@ -249,7 +249,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
AddAssert("slider moved", () => Precision.AlmostEquals(slider.Position, new Vector2(256, 192) + new Vector2(150, 50)));
|
||||
|
||||
assertControlPointPosition(0, Vector2.Zero);
|
||||
assertControlPointType(0, PathType.PerfectCurve);
|
||||
assertControlPointType(0, PathType.PERFECT_CURVE);
|
||||
|
||||
assertControlPointPosition(1, new Vector2(0, 100));
|
||||
|
||||
@ -272,7 +272,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
AddStep("release", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||
|
||||
assertControlPointPosition(1, new Vector2(400, 0.01f));
|
||||
assertControlPointType(0, PathType.Bezier);
|
||||
assertControlPointType(0, PathType.BEZIER);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -282,13 +282,13 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
AddStep("hold", () => InputManager.PressButton(MouseButton.Left));
|
||||
|
||||
addMovementStep(new Vector2(400, 0.01f));
|
||||
assertControlPointType(0, PathType.Bezier);
|
||||
assertControlPointType(0, PathType.BEZIER);
|
||||
|
||||
addMovementStep(new Vector2(150, 50));
|
||||
AddStep("release", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||
|
||||
assertControlPointPosition(1, new Vector2(150, 50));
|
||||
assertControlPointType(0, PathType.PerfectCurve);
|
||||
assertControlPointType(0, PathType.PERFECT_CURVE);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -298,32 +298,32 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
AddStep("hold", () => InputManager.PressButton(MouseButton.Left));
|
||||
|
||||
addMovementStep(new Vector2(350, 0.01f));
|
||||
assertControlPointType(2, PathType.Bezier);
|
||||
assertControlPointType(2, PathType.BEZIER);
|
||||
|
||||
addMovementStep(new Vector2(150, 150));
|
||||
AddStep("release", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||
|
||||
assertControlPointPosition(4, new Vector2(150, 150));
|
||||
assertControlPointType(2, PathType.PerfectCurve);
|
||||
assertControlPointType(2, PathType.PERFECT_CURVE);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDragControlPointPathAfterChangingType()
|
||||
{
|
||||
AddStep("change type to bezier", () => slider.Path.ControlPoints[2].Type = PathType.Bezier);
|
||||
AddStep("change type to bezier", () => slider.Path.ControlPoints[2].Type = PathType.BEZIER);
|
||||
AddStep("add point", () => slider.Path.ControlPoints.Add(new PathControlPoint(new Vector2(500, 10))));
|
||||
AddStep("change type to perfect", () => slider.Path.ControlPoints[3].Type = PathType.PerfectCurve);
|
||||
AddStep("change type to perfect", () => slider.Path.ControlPoints[3].Type = PathType.PERFECT_CURVE);
|
||||
|
||||
moveMouseToControlPoint(4);
|
||||
AddStep("hold", () => InputManager.PressButton(MouseButton.Left));
|
||||
|
||||
assertControlPointType(3, PathType.PerfectCurve);
|
||||
assertControlPointType(3, PathType.PERFECT_CURVE);
|
||||
|
||||
addMovementStep(new Vector2(350, 0.01f));
|
||||
AddStep("release", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||
|
||||
assertControlPointPosition(4, new Vector2(350, 0.01f));
|
||||
assertControlPointType(3, PathType.Bezier);
|
||||
assertControlPointType(3, PathType.BEZIER);
|
||||
}
|
||||
|
||||
private void addMovementStep(Vector2 relativePosition)
|
||||
|
@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
|
||||
PathControlPoint[] points =
|
||||
{
|
||||
new PathControlPoint(new Vector2(0), PathType.Linear),
|
||||
new PathControlPoint(new Vector2(0), PathType.LINEAR),
|
||||
new PathControlPoint(new Vector2(100, 0)),
|
||||
};
|
||||
|
||||
@ -82,7 +82,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
|
||||
PathControlPoint[] points =
|
||||
{
|
||||
new PathControlPoint(new Vector2(0), PathType.Linear),
|
||||
new PathControlPoint(new Vector2(0), PathType.LINEAR),
|
||||
new PathControlPoint(new Vector2(100, 0)),
|
||||
};
|
||||
|
||||
@ -126,7 +126,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
|
||||
PathControlPoint[] points =
|
||||
{
|
||||
new PathControlPoint(new Vector2(0), PathType.PerfectCurve),
|
||||
new PathControlPoint(new Vector2(0), PathType.PERFECT_CURVE),
|
||||
new PathControlPoint(new Vector2(100, 0)),
|
||||
new PathControlPoint(new Vector2(0, 10))
|
||||
};
|
||||
@ -165,7 +165,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
|
||||
PathControlPoint[] points =
|
||||
{
|
||||
new PathControlPoint(new Vector2(0), PathType.Linear),
|
||||
new PathControlPoint(new Vector2(0), PathType.LINEAR),
|
||||
new PathControlPoint(new Vector2(0, 50)),
|
||||
new PathControlPoint(new Vector2(0, 100))
|
||||
};
|
||||
|
@ -1,8 +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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
@ -58,7 +57,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
assertPlaced(true);
|
||||
assertLength(200);
|
||||
assertControlPointCount(2);
|
||||
assertControlPointType(0, PathType.Linear);
|
||||
assertControlPointType(0, PathType.LINEAR);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -72,7 +71,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
|
||||
assertPlaced(true);
|
||||
assertControlPointCount(2);
|
||||
assertControlPointType(0, PathType.Linear);
|
||||
assertControlPointType(0, PathType.LINEAR);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -90,7 +89,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
assertPlaced(true);
|
||||
assertControlPointCount(3);
|
||||
assertControlPointPosition(1, new Vector2(100, 0));
|
||||
assertControlPointType(0, PathType.PerfectCurve);
|
||||
assertControlPointType(0, PathType.PERFECT_CURVE);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -112,7 +111,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
assertControlPointCount(4);
|
||||
assertControlPointPosition(1, new Vector2(100, 0));
|
||||
assertControlPointPosition(2, new Vector2(100, 100));
|
||||
assertControlPointType(0, PathType.Bezier);
|
||||
assertControlPointType(0, PathType.BEZIER);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -131,8 +130,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
assertPlaced(true);
|
||||
assertControlPointCount(3);
|
||||
assertControlPointPosition(1, new Vector2(100, 0));
|
||||
assertControlPointType(0, PathType.Linear);
|
||||
assertControlPointType(1, PathType.Linear);
|
||||
assertControlPointType(0, PathType.LINEAR);
|
||||
assertControlPointType(1, PathType.LINEAR);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -150,7 +149,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
|
||||
assertPlaced(true);
|
||||
assertControlPointCount(2);
|
||||
assertControlPointType(0, PathType.Linear);
|
||||
assertControlPointType(0, PathType.LINEAR);
|
||||
assertLength(100);
|
||||
}
|
||||
|
||||
@ -172,7 +171,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
|
||||
assertPlaced(true);
|
||||
assertControlPointCount(3);
|
||||
assertControlPointType(0, PathType.PerfectCurve);
|
||||
assertControlPointType(0, PathType.PERFECT_CURVE);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -196,7 +195,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
|
||||
assertPlaced(true);
|
||||
assertControlPointCount(4);
|
||||
assertControlPointType(0, PathType.Bezier);
|
||||
assertControlPointType(0, PathType.BEZIER);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -216,8 +215,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
assertControlPointCount(3);
|
||||
assertControlPointPosition(1, new Vector2(100, 0));
|
||||
assertControlPointPosition(2, new Vector2(100));
|
||||
assertControlPointType(0, PathType.Linear);
|
||||
assertControlPointType(1, PathType.Linear);
|
||||
assertControlPointType(0, PathType.LINEAR);
|
||||
assertControlPointType(1, PathType.LINEAR);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -240,8 +239,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
assertControlPointCount(4);
|
||||
assertControlPointPosition(1, new Vector2(100, 0));
|
||||
assertControlPointPosition(2, new Vector2(100));
|
||||
assertControlPointType(0, PathType.Linear);
|
||||
assertControlPointType(1, PathType.PerfectCurve);
|
||||
assertControlPointType(0, PathType.LINEAR);
|
||||
assertControlPointType(1, PathType.PERFECT_CURVE);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -269,25 +268,79 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
assertControlPointPosition(2, new Vector2(100));
|
||||
assertControlPointPosition(3, new Vector2(200, 100));
|
||||
assertControlPointPosition(4, new Vector2(200));
|
||||
assertControlPointType(0, PathType.PerfectCurve);
|
||||
assertControlPointType(2, PathType.PerfectCurve);
|
||||
assertControlPointType(0, PathType.PERFECT_CURVE);
|
||||
assertControlPointType(2, PathType.PERFECT_CURVE);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBeginPlacementWithoutReleasingMouse()
|
||||
public void TestSliderDrawingDoesntActivateAfterNormalPlacement()
|
||||
{
|
||||
Vector2 startPoint = new Vector2(200);
|
||||
|
||||
addMovementStep(startPoint);
|
||||
addClickStep(MouseButton.Left);
|
||||
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
if (i == 5)
|
||||
AddStep("press left button", () => InputManager.PressButton(MouseButton.Left));
|
||||
addMovementStep(startPoint + new Vector2(i * 40, MathF.Sin(i * MathF.PI / 5) * 50));
|
||||
}
|
||||
|
||||
AddStep("release left button", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||
assertPlaced(false);
|
||||
|
||||
addClickStep(MouseButton.Right);
|
||||
assertPlaced(true);
|
||||
|
||||
assertControlPointType(0, PathType.BEZIER);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSliderDrawingCurve()
|
||||
{
|
||||
Vector2 startPoint = new Vector2(200);
|
||||
|
||||
addMovementStep(startPoint);
|
||||
AddStep("press left button", () => InputManager.PressButton(MouseButton.Left));
|
||||
|
||||
for (int i = 0; i < 20; i++)
|
||||
addMovementStep(startPoint + new Vector2(i * 40, MathF.Sin(i * MathF.PI / 5) * 50));
|
||||
|
||||
AddStep("release left button", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||
|
||||
assertPlaced(true);
|
||||
assertLength(760, tolerance: 10);
|
||||
assertControlPointCount(5);
|
||||
assertControlPointType(0, PathType.BSpline(3));
|
||||
assertControlPointType(1, null);
|
||||
assertControlPointType(2, null);
|
||||
assertControlPointType(3, null);
|
||||
assertControlPointType(4, null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSliderDrawingLinear()
|
||||
{
|
||||
addMovementStep(new Vector2(200));
|
||||
AddStep("press left button", () => InputManager.PressButton(MouseButton.Left));
|
||||
|
||||
addMovementStep(new Vector2(300, 200));
|
||||
addMovementStep(new Vector2(400, 200));
|
||||
addMovementStep(new Vector2(400, 300));
|
||||
addMovementStep(new Vector2(400));
|
||||
addMovementStep(new Vector2(300, 400));
|
||||
addMovementStep(new Vector2(200, 400));
|
||||
|
||||
AddStep("release left button", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||
|
||||
addClickStep(MouseButton.Right);
|
||||
|
||||
assertPlaced(true);
|
||||
assertLength(200);
|
||||
assertControlPointCount(2);
|
||||
assertControlPointType(0, PathType.Linear);
|
||||
assertLength(600, tolerance: 10);
|
||||
assertControlPointCount(4);
|
||||
assertControlPointType(0, PathType.LINEAR);
|
||||
assertControlPointType(1, null);
|
||||
assertControlPointType(2, null);
|
||||
assertControlPointType(3, null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -306,7 +359,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
|
||||
assertPlaced(true);
|
||||
assertControlPointCount(3);
|
||||
assertControlPointType(0, PathType.Bezier);
|
||||
assertControlPointType(0, PathType.BEZIER);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -326,7 +379,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
|
||||
assertPlaced(true);
|
||||
assertControlPointCount(3);
|
||||
assertControlPointType(0, PathType.PerfectCurve);
|
||||
assertControlPointType(0, PathType.PERFECT_CURVE);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -347,7 +400,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
|
||||
assertPlaced(true);
|
||||
assertControlPointCount(3);
|
||||
assertControlPointType(0, PathType.PerfectCurve);
|
||||
assertControlPointType(0, PathType.PERFECT_CURVE);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -368,7 +421,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
|
||||
assertPlaced(true);
|
||||
assertControlPointCount(3);
|
||||
assertControlPointType(0, PathType.Bezier);
|
||||
assertControlPointType(0, PathType.BEZIER);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -385,7 +438,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
|
||||
assertPlaced(true);
|
||||
assertControlPointCount(3);
|
||||
assertControlPointType(0, PathType.PerfectCurve);
|
||||
assertControlPointType(0, PathType.PERFECT_CURVE);
|
||||
}
|
||||
|
||||
private void addMovementStep(Vector2 position) => AddStep($"move mouse to {position}", () => InputManager.MoveMouseTo(InputManager.ToScreenSpace(position)));
|
||||
@ -397,16 +450,16 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
|
||||
private void assertPlaced(bool expected) => AddAssert($"slider {(expected ? "placed" : "not placed")}", () => (getSlider() != null) == expected);
|
||||
|
||||
private void assertLength(double expected) => AddAssert($"slider length is {expected}", () => Precision.AlmostEquals(expected, getSlider().Distance, 1));
|
||||
private void assertLength(double expected, double tolerance = 1) => AddAssert($"slider length is {expected}±{tolerance}", () => getSlider()!.Distance, () => Is.EqualTo(expected).Within(tolerance));
|
||||
|
||||
private void assertControlPointCount(int expected) => AddAssert($"has {expected} control points", () => getSlider().Path.ControlPoints.Count == expected);
|
||||
private void assertControlPointCount(int expected) => AddAssert($"has {expected} control points", () => getSlider()!.Path.ControlPoints.Count, () => Is.EqualTo(expected));
|
||||
|
||||
private void assertControlPointType(int index, PathType type) => AddAssert($"control point {index} is {type}", () => getSlider().Path.ControlPoints[index].Type == type);
|
||||
private void assertControlPointType(int index, PathType? type) => AddAssert($"control point {index} is {type?.ToString() ?? "inherit"}", () => getSlider()!.Path.ControlPoints[index].Type, () => Is.EqualTo(type));
|
||||
|
||||
private void assertControlPointPosition(int index, Vector2 position) =>
|
||||
AddAssert($"control point {index} at {position}", () => Precision.AlmostEquals(position, getSlider().Path.ControlPoints[index].Position, 1));
|
||||
AddAssert($"control point {index} at {position}", () => Precision.AlmostEquals(position, getSlider()!.Path.ControlPoints[index].Position, 1));
|
||||
|
||||
private Slider getSlider() => HitObjectContainer.Count > 0 ? ((DrawableSlider)HitObjectContainer[0]).HitObject : null;
|
||||
private Slider? getSlider() => HitObjectContainer.Count > 0 ? ((DrawableSlider)HitObjectContainer[0]).HitObject : null;
|
||||
|
||||
protected override DrawableHitObject CreateHitObject(HitObject hitObject) => new DrawableSlider((Slider)hitObject);
|
||||
protected override PlacementBlueprint CreateBlueprint() => new SliderPlacementBlueprint();
|
||||
|
@ -22,12 +22,12 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
private readonly PathControlPoint[][] paths =
|
||||
{
|
||||
createPathSegment(
|
||||
PathType.PerfectCurve,
|
||||
PathType.PERFECT_CURVE,
|
||||
new Vector2(200, -50),
|
||||
new Vector2(250, 0)
|
||||
),
|
||||
createPathSegment(
|
||||
PathType.Linear,
|
||||
PathType.LINEAR,
|
||||
new Vector2(100, 0),
|
||||
new Vector2(100, 100)
|
||||
)
|
||||
|
@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
slider = new Slider
|
||||
{
|
||||
Position = new Vector2(256, 192),
|
||||
Path = new SliderPath(PathType.Bezier, new[]
|
||||
Path = new SliderPath(PathType.BEZIER, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(150, 150),
|
||||
|
@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
{
|
||||
ControlPoints =
|
||||
{
|
||||
new PathControlPoint(Vector2.Zero, PathType.PerfectCurve),
|
||||
new PathControlPoint(Vector2.Zero, PathType.PERFECT_CURVE),
|
||||
new PathControlPoint(new Vector2(136, 205)),
|
||||
new PathControlPoint(new Vector2(-4, 226))
|
||||
}
|
||||
@ -181,7 +181,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
{
|
||||
OsuSelectionHandler selectionHandler;
|
||||
|
||||
AddAssert("first control point perfect", () => slider.Path.ControlPoints[0].Type == PathType.PerfectCurve);
|
||||
AddAssert("first control point perfect", () => slider.Path.ControlPoints[0].Type == PathType.PERFECT_CURVE);
|
||||
|
||||
AddStep("select slider", () => EditorBeatmap.SelectedHitObjects.Add(slider));
|
||||
AddStep("rotate 90 degrees ccw", () =>
|
||||
@ -190,7 +190,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
selectionHandler.HandleRotation(-90);
|
||||
});
|
||||
|
||||
AddAssert("first control point still perfect", () => slider.Path.ControlPoints[0].Type == PathType.PerfectCurve);
|
||||
AddAssert("first control point still perfect", () => slider.Path.ControlPoints[0].Type == PathType.PERFECT_CURVE);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -223,7 +223,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
{
|
||||
OsuSelectionHandler selectionHandler;
|
||||
|
||||
AddAssert("first control point perfect", () => slider.Path.ControlPoints[0].Type == PathType.PerfectCurve);
|
||||
AddAssert("first control point perfect", () => slider.Path.ControlPoints[0].Type == PathType.PERFECT_CURVE);
|
||||
|
||||
AddStep("select slider", () => EditorBeatmap.SelectedHitObjects.Add(slider));
|
||||
AddStep("flip slider horizontally", () =>
|
||||
@ -232,7 +232,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
selectionHandler.OnPressed(new KeyBindingPressEvent<GlobalAction>(InputManager.CurrentState, GlobalAction.EditorFlipVertically));
|
||||
});
|
||||
|
||||
AddAssert("first control point still perfect", () => slider.Path.ControlPoints[0].Type == PathType.PerfectCurve);
|
||||
AddAssert("first control point still perfect", () => slider.Path.ControlPoints[0].Type == PathType.PERFECT_CURVE);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -45,9 +45,9 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
Position = new Vector2(0, 50),
|
||||
Path = new SliderPath(new[]
|
||||
{
|
||||
new PathControlPoint(Vector2.Zero, PathType.PerfectCurve),
|
||||
new PathControlPoint(Vector2.Zero, PathType.PERFECT_CURVE),
|
||||
new PathControlPoint(new Vector2(150, 150)),
|
||||
new PathControlPoint(new Vector2(300, 0), PathType.PerfectCurve),
|
||||
new PathControlPoint(new Vector2(300, 0), PathType.PERFECT_CURVE),
|
||||
new PathControlPoint(new Vector2(400, 0)),
|
||||
new PathControlPoint(new Vector2(400, 150))
|
||||
})
|
||||
@ -73,20 +73,20 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
|
||||
AddAssert("slider split", () => slider is not null && EditorBeatmap.HitObjects.Count == 2 &&
|
||||
sliderCreatedFor((Slider)EditorBeatmap.HitObjects[0], 0, EditorBeatmap.HitObjects[1].StartTime - split_gap,
|
||||
(new Vector2(0, 50), PathType.PerfectCurve),
|
||||
(new Vector2(0, 50), PathType.PERFECT_CURVE),
|
||||
(new Vector2(150, 200), null),
|
||||
(new Vector2(300, 50), null)
|
||||
) && sliderCreatedFor((Slider)EditorBeatmap.HitObjects[1], slider.StartTime, endTime + split_gap,
|
||||
(new Vector2(300, 50), PathType.PerfectCurve),
|
||||
(new Vector2(300, 50), PathType.PERFECT_CURVE),
|
||||
(new Vector2(400, 50), null),
|
||||
(new Vector2(400, 200), null)
|
||||
));
|
||||
|
||||
AddStep("undo", () => Editor.Undo());
|
||||
AddAssert("original slider restored", () => EditorBeatmap.HitObjects.Count == 1 && sliderCreatedFor((Slider)EditorBeatmap.HitObjects[0], 0, endTime,
|
||||
(new Vector2(0, 50), PathType.PerfectCurve),
|
||||
(new Vector2(0, 50), PathType.PERFECT_CURVE),
|
||||
(new Vector2(150, 200), null),
|
||||
(new Vector2(300, 50), PathType.PerfectCurve),
|
||||
(new Vector2(300, 50), PathType.PERFECT_CURVE),
|
||||
(new Vector2(400, 50), null),
|
||||
(new Vector2(400, 200), null)
|
||||
));
|
||||
@ -104,11 +104,11 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
Position = new Vector2(0, 50),
|
||||
Path = new SliderPath(new[]
|
||||
{
|
||||
new PathControlPoint(Vector2.Zero, PathType.PerfectCurve),
|
||||
new PathControlPoint(Vector2.Zero, PathType.PERFECT_CURVE),
|
||||
new PathControlPoint(new Vector2(150, 150)),
|
||||
new PathControlPoint(new Vector2(300, 0), PathType.Bezier),
|
||||
new PathControlPoint(new Vector2(300, 0), PathType.BEZIER),
|
||||
new PathControlPoint(new Vector2(400, 0)),
|
||||
new PathControlPoint(new Vector2(400, 150), PathType.Catmull),
|
||||
new PathControlPoint(new Vector2(400, 150), PathType.CATMULL),
|
||||
new PathControlPoint(new Vector2(300, 200)),
|
||||
new PathControlPoint(new Vector2(400, 250))
|
||||
})
|
||||
@ -139,15 +139,15 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
|
||||
AddAssert("slider split", () => slider is not null && EditorBeatmap.HitObjects.Count == 3 &&
|
||||
sliderCreatedFor((Slider)EditorBeatmap.HitObjects[0], 0, EditorBeatmap.HitObjects[1].StartTime - split_gap,
|
||||
(new Vector2(0, 50), PathType.PerfectCurve),
|
||||
(new Vector2(0, 50), PathType.PERFECT_CURVE),
|
||||
(new Vector2(150, 200), null),
|
||||
(new Vector2(300, 50), null)
|
||||
) && sliderCreatedFor((Slider)EditorBeatmap.HitObjects[1], EditorBeatmap.HitObjects[0].GetEndTime() + split_gap, slider.StartTime - split_gap,
|
||||
(new Vector2(300, 50), PathType.Bezier),
|
||||
(new Vector2(300, 50), PathType.BEZIER),
|
||||
(new Vector2(400, 50), null),
|
||||
(new Vector2(400, 200), null)
|
||||
) && sliderCreatedFor((Slider)EditorBeatmap.HitObjects[2], EditorBeatmap.HitObjects[1].GetEndTime() + split_gap, endTime + split_gap * 2,
|
||||
(new Vector2(400, 200), PathType.Catmull),
|
||||
(new Vector2(400, 200), PathType.CATMULL),
|
||||
(new Vector2(300, 250), null),
|
||||
(new Vector2(400, 300), null)
|
||||
));
|
||||
@ -165,9 +165,9 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
Position = new Vector2(0, 50),
|
||||
Path = new SliderPath(new[]
|
||||
{
|
||||
new PathControlPoint(Vector2.Zero, PathType.PerfectCurve),
|
||||
new PathControlPoint(Vector2.Zero, PathType.PERFECT_CURVE),
|
||||
new PathControlPoint(new Vector2(150, 150)),
|
||||
new PathControlPoint(new Vector2(300, 0), PathType.PerfectCurve),
|
||||
new PathControlPoint(new Vector2(300, 0), PathType.PERFECT_CURVE),
|
||||
new PathControlPoint(new Vector2(400, 0)),
|
||||
new PathControlPoint(new Vector2(400, 150))
|
||||
})
|
||||
|
@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
|
||||
PathControlPoint[] points =
|
||||
{
|
||||
new PathControlPoint(new Vector2(0), PathType.Linear),
|
||||
new PathControlPoint(new Vector2(0), PathType.LINEAR),
|
||||
new PathControlPoint(new Vector2(100, 0)),
|
||||
};
|
||||
|
||||
|
@ -81,12 +81,12 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
|
||||
new Slider
|
||||
{
|
||||
StartTime = 3200,
|
||||
Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(100, 0), })
|
||||
Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(100, 0), })
|
||||
},
|
||||
new Slider
|
||||
{
|
||||
StartTime = 5200,
|
||||
Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(100, 0), })
|
||||
Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(100, 0), })
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -105,12 +105,12 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
|
||||
new Slider
|
||||
{
|
||||
StartTime = 1000,
|
||||
Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(100, 0), })
|
||||
Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(100, 0), })
|
||||
},
|
||||
new Slider
|
||||
{
|
||||
StartTime = 4000,
|
||||
Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(100, 0), })
|
||||
Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(100, 0), })
|
||||
},
|
||||
}
|
||||
},
|
||||
@ -140,7 +140,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
|
||||
{
|
||||
StartTime = 3000,
|
||||
Position = new Vector2(156, 242),
|
||||
Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(200, 0), })
|
||||
Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(200, 0), })
|
||||
},
|
||||
new Spinner
|
||||
{
|
||||
|
@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
|
||||
var slider = new Slider
|
||||
{
|
||||
StartTime = 1000,
|
||||
Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(100, 0), })
|
||||
Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(100, 0), })
|
||||
};
|
||||
|
||||
CreateHitObjectTest(new HitObjectTestData(slider), shouldMiss);
|
||||
|
204
osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs
Normal file
204
osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs
Normal file
@ -0,0 +1,204 @@
|
||||
// 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.Input;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Timing;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Input;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Beatmaps;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Storyboards;
|
||||
using osu.Game.Tests.Visual;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests.Mods
|
||||
{
|
||||
public partial class TestSceneOsuModTouchDevice : RateAdjustedBeatmapTestScene
|
||||
{
|
||||
[Resolved]
|
||||
private SessionStatics statics { get; set; } = null!;
|
||||
|
||||
private ScoreAccessibleSoloPlayer currentPlayer = null!;
|
||||
private readonly ManualClock manualClock = new ManualClock { Rate = 0 };
|
||||
|
||||
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard? storyboard = null)
|
||||
=> new ClockBackedTestWorkingBeatmap(beatmap, storyboard, new FramedClock(manualClock), Audio);
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Add(new TouchInputInterceptor());
|
||||
}
|
||||
|
||||
public override void SetUpSteps()
|
||||
{
|
||||
AddStep("reset static", () => statics.SetValue(Static.TouchInputActive, false));
|
||||
base.SetUpSteps();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUserAlreadyHasTouchDeviceActive()
|
||||
{
|
||||
loadPlayer();
|
||||
// it is presumed that a previous screen (i.e. song select) will set this up
|
||||
AddStep("set up touchscreen user", () =>
|
||||
{
|
||||
currentPlayer.Score.ScoreInfo.Mods = currentPlayer.Score.ScoreInfo.Mods.Append(new OsuModTouchDevice()).ToArray();
|
||||
statics.SetValue(Static.TouchInputActive, true);
|
||||
});
|
||||
|
||||
AddStep("seek to 0", () => currentPlayer.GameplayClockContainer.Seek(0));
|
||||
AddUntilStep("wait until 0", () => currentPlayer.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(0));
|
||||
AddStep("touch circle", () =>
|
||||
{
|
||||
var touch = new Touch(TouchSource.Touch1, currentPlayer.DrawableRuleset.Playfield.ScreenSpaceDrawQuad.Centre);
|
||||
InputManager.BeginTouch(touch);
|
||||
InputManager.EndTouch(touch);
|
||||
});
|
||||
AddAssert("touch device mod activated", () => currentPlayer.Score.ScoreInfo.Mods, () => Has.One.InstanceOf<OsuModTouchDevice>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTouchDuringBreak()
|
||||
{
|
||||
loadPlayer();
|
||||
AddStep("seek to 2000", () => currentPlayer.GameplayClockContainer.Seek(2000));
|
||||
AddUntilStep("wait until 2000", () => currentPlayer.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(2000));
|
||||
AddUntilStep("wait until break entered", () => currentPlayer.IsBreakTime.Value);
|
||||
AddStep("touch playfield", () =>
|
||||
{
|
||||
var touch = new Touch(TouchSource.Touch1, currentPlayer.DrawableRuleset.Playfield.ScreenSpaceDrawQuad.Centre);
|
||||
InputManager.BeginTouch(touch);
|
||||
InputManager.EndTouch(touch);
|
||||
});
|
||||
AddAssert("touch device mod not activated", () => currentPlayer.Score.ScoreInfo.Mods, () => Has.None.InstanceOf<OsuModTouchDevice>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTouchMiss()
|
||||
{
|
||||
loadPlayer();
|
||||
// ensure mouse is active (and that it's not suppressed due to touches in previous tests)
|
||||
AddStep("click mouse", () => InputManager.Click(MouseButton.Left));
|
||||
|
||||
AddStep("seek to 200", () => currentPlayer.GameplayClockContainer.Seek(200));
|
||||
AddUntilStep("wait until 200", () => currentPlayer.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(200));
|
||||
AddStep("touch playfield", () =>
|
||||
{
|
||||
var touch = new Touch(TouchSource.Touch1, currentPlayer.DrawableRuleset.Playfield.ScreenSpaceDrawQuad.Centre);
|
||||
InputManager.BeginTouch(touch);
|
||||
InputManager.EndTouch(touch);
|
||||
});
|
||||
AddAssert("touch device mod activated", () => currentPlayer.Score.ScoreInfo.Mods, () => Has.One.InstanceOf<OsuModTouchDevice>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestIncompatibleModActive()
|
||||
{
|
||||
loadPlayer();
|
||||
// this is only a veneer of enabling autopilot as having it actually active from the start is annoying to make happen
|
||||
// given the tests' structure.
|
||||
AddStep("enable autopilot", () => currentPlayer.Score.ScoreInfo.Mods = new Mod[] { new OsuModAutopilot() });
|
||||
|
||||
AddStep("seek to 0", () => currentPlayer.GameplayClockContainer.Seek(0));
|
||||
AddUntilStep("wait until 0", () => currentPlayer.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(0));
|
||||
AddStep("touch playfield", () =>
|
||||
{
|
||||
var touch = new Touch(TouchSource.Touch1, currentPlayer.DrawableRuleset.Playfield.ScreenSpaceDrawQuad.Centre);
|
||||
InputManager.BeginTouch(touch);
|
||||
InputManager.EndTouch(touch);
|
||||
});
|
||||
AddAssert("touch device mod not activated", () => currentPlayer.Score.ScoreInfo.Mods, () => Has.None.InstanceOf<OsuModTouchDevice>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSecondObjectTouched()
|
||||
{
|
||||
loadPlayer();
|
||||
// ensure mouse is active (and that it's not suppressed due to touches in previous tests)
|
||||
AddStep("click mouse", () => InputManager.Click(MouseButton.Left));
|
||||
|
||||
AddStep("seek to 0", () => currentPlayer.GameplayClockContainer.Seek(0));
|
||||
AddUntilStep("wait until 0", () => currentPlayer.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(0));
|
||||
AddStep("click circle", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(currentPlayer.DrawableRuleset.Playfield.ScreenSpaceDrawQuad.Centre);
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
AddAssert("touch device mod not activated", () => currentPlayer.Score.ScoreInfo.Mods, () => Has.None.InstanceOf<OsuModTouchDevice>());
|
||||
|
||||
AddStep("seek to 5000", () => currentPlayer.GameplayClockContainer.Seek(5000));
|
||||
AddUntilStep("wait until 5000", () => currentPlayer.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(5000));
|
||||
AddStep("touch playfield", () =>
|
||||
{
|
||||
var touch = new Touch(TouchSource.Touch1, currentPlayer.DrawableRuleset.Playfield.ScreenSpaceDrawQuad.Centre);
|
||||
InputManager.BeginTouch(touch);
|
||||
InputManager.EndTouch(touch);
|
||||
});
|
||||
AddAssert("touch device mod activated", () => currentPlayer.Score.ScoreInfo.Mods, () => Has.One.InstanceOf<OsuModTouchDevice>());
|
||||
}
|
||||
|
||||
private void loadPlayer()
|
||||
{
|
||||
AddStep("load player", () =>
|
||||
{
|
||||
Beatmap.Value = CreateWorkingBeatmap(new OsuBeatmap
|
||||
{
|
||||
HitObjects =
|
||||
{
|
||||
new HitCircle
|
||||
{
|
||||
Position = OsuPlayfield.BASE_SIZE / 2,
|
||||
StartTime = 0,
|
||||
},
|
||||
new HitCircle
|
||||
{
|
||||
Position = OsuPlayfield.BASE_SIZE / 2,
|
||||
StartTime = 5000,
|
||||
},
|
||||
},
|
||||
Breaks =
|
||||
{
|
||||
new BreakPeriod(2000, 3000)
|
||||
}
|
||||
});
|
||||
|
||||
var p = new ScoreAccessibleSoloPlayer();
|
||||
|
||||
LoadScreen(currentPlayer = p);
|
||||
});
|
||||
|
||||
AddUntilStep("Beatmap at 0", () => Beatmap.Value.Track.CurrentTime == 0);
|
||||
AddUntilStep("Wait until player is loaded", () => currentPlayer.IsCurrentScreen());
|
||||
}
|
||||
|
||||
private partial class ScoreAccessibleSoloPlayer : SoloPlayer
|
||||
{
|
||||
public new GameplayClockContainer GameplayClockContainer => base.GameplayClockContainer;
|
||||
|
||||
public new DrawableRuleset DrawableRuleset => base.DrawableRuleset;
|
||||
|
||||
protected override bool PauseOnFocusLost => false;
|
||||
|
||||
public ScoreAccessibleSoloPlayer()
|
||||
: base(new PlayerConfiguration
|
||||
{
|
||||
AllowPause = false,
|
||||
ShowResults = false,
|
||||
})
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -15,22 +15,22 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Osu";
|
||||
|
||||
[TestCase(6.710442985146793d, 206, "diffcalc-test")]
|
||||
[TestCase(1.4386882251130073d, 45, "zero-length-sliders")]
|
||||
[TestCase(0.42506480230838789d, 2, "very-fast-slider")]
|
||||
[TestCase(0.14102693012101306d, 1, "nan-slider")]
|
||||
[TestCase(6.710442985146793d, 239, "diffcalc-test")]
|
||||
[TestCase(1.4386882251130073d, 54, "zero-length-sliders")]
|
||||
[TestCase(0.42506480230838789d, 4, "very-fast-slider")]
|
||||
[TestCase(0.14102693012101306d, 2, "nan-slider")]
|
||||
public void Test(double expectedStarRating, int expectedMaxCombo, string name)
|
||||
=> base.Test(expectedStarRating, expectedMaxCombo, name);
|
||||
|
||||
[TestCase(8.9742952703071666d, 206, "diffcalc-test")]
|
||||
[TestCase(0.55071082800473514d, 2, "very-fast-slider")]
|
||||
[TestCase(1.743180218215227d, 45, "zero-length-sliders")]
|
||||
[TestCase(8.9742952703071666d, 239, "diffcalc-test")]
|
||||
[TestCase(1.743180218215227d, 54, "zero-length-sliders")]
|
||||
[TestCase(0.55071082800473514d, 4, "very-fast-slider")]
|
||||
public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name)
|
||||
=> Test(expectedStarRating, expectedMaxCombo, name, new OsuModDoubleTime());
|
||||
|
||||
[TestCase(6.710442985146793d, 239, "diffcalc-test")]
|
||||
[TestCase(0.42506480230838789d, 4, "very-fast-slider")]
|
||||
[TestCase(1.4386882251130073d, 54, "zero-length-sliders")]
|
||||
[TestCase(0.42506480230838789d, 4, "very-fast-slider")]
|
||||
public void TestClassicMod(double expectedStarRating, int expectedMaxCombo, string name)
|
||||
=> Test(expectedStarRating, expectedMaxCombo, name, new OsuModClassic());
|
||||
|
||||
|
@ -26,9 +26,9 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
ControlPoints =
|
||||
{
|
||||
new PathControlPoint(new Vector2(), PathType.Linear),
|
||||
new PathControlPoint(new Vector2(-64, -128), PathType.Linear), // absolute position: (64, 0)
|
||||
new PathControlPoint(new Vector2(-128, 0), PathType.Linear) // absolute position: (0, 128)
|
||||
new PathControlPoint(new Vector2(), PathType.LINEAR),
|
||||
new PathControlPoint(new Vector2(-64, -128), PathType.LINEAR), // absolute position: (64, 0)
|
||||
new PathControlPoint(new Vector2(-128, 0), PathType.LINEAR) // absolute position: (0, 128)
|
||||
}
|
||||
},
|
||||
RepeatCount = 1
|
||||
|
@ -167,7 +167,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
StartTime = Time.Current + 500,
|
||||
Position = new Vector2(250),
|
||||
Path = new SliderPath(PathType.Linear, new[]
|
||||
Path = new SliderPath(PathType.LINEAR, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(0, 100),
|
||||
|
@ -264,7 +264,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
StartTime = time_slider,
|
||||
Position = positionSlider,
|
||||
Path = new SliderPath(PathType.Linear, new[]
|
||||
Path = new SliderPath(PathType.LINEAR, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(50, 0),
|
||||
@ -308,7 +308,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
StartTime = time_slider,
|
||||
Position = positionSlider,
|
||||
Path = new SliderPath(PathType.Linear, new[]
|
||||
Path = new SliderPath(PathType.LINEAR, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(50, 0),
|
||||
@ -391,7 +391,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
StartTime = time_slider,
|
||||
Position = positionSlider,
|
||||
Path = new SliderPath(PathType.Linear, new[]
|
||||
Path = new SliderPath(PathType.LINEAR, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(25, 0),
|
||||
@ -428,7 +428,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
StartTime = time_first_slider,
|
||||
Position = positionFirstSlider,
|
||||
Path = new SliderPath(PathType.Linear, new[]
|
||||
Path = new SliderPath(PathType.LINEAR, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(25, 0),
|
||||
@ -438,7 +438,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
StartTime = time_second_slider,
|
||||
Position = positionSecondSlider,
|
||||
Path = new SliderPath(PathType.Linear, new[]
|
||||
Path = new SliderPath(PathType.LINEAR, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(25, 0),
|
||||
@ -521,7 +521,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
StartTime = time_first_slider,
|
||||
Position = positionFirstSlider,
|
||||
Path = new SliderPath(PathType.Linear, new[]
|
||||
Path = new SliderPath(PathType.LINEAR, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(25, 0),
|
||||
@ -531,7 +531,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
StartTime = time_second_slider,
|
||||
Position = positionSecondSlider,
|
||||
Path = new SliderPath(PathType.Linear, new[]
|
||||
Path = new SliderPath(PathType.LINEAR, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(25, 0),
|
||||
@ -571,7 +571,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
StartTime = time_first_slider,
|
||||
Position = positionFirstSlider,
|
||||
Path = new SliderPath(PathType.Linear, new[]
|
||||
Path = new SliderPath(PathType.LINEAR, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(25, 0),
|
||||
@ -581,7 +581,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
StartTime = time_second_slider,
|
||||
Position = positionSecondSlider,
|
||||
Path = new SliderPath(PathType.Linear, new[]
|
||||
Path = new SliderPath(PathType.LINEAR, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(25, 0),
|
||||
|
@ -219,7 +219,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
StartTime = Time.Current + time_offset,
|
||||
Position = new Vector2(239, 176),
|
||||
Path = new SliderPath(PathType.PerfectCurve, new[]
|
||||
Path = new SliderPath(PathType.PERFECT_CURVE, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(154, 28),
|
||||
@ -255,7 +255,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
SliderVelocityMultiplier = speedMultiplier,
|
||||
StartTime = Time.Current + time_offset,
|
||||
Position = new Vector2(0, -(distance / 2)),
|
||||
Path = new SliderPath(PathType.PerfectCurve, new[]
|
||||
Path = new SliderPath(PathType.PERFECT_CURVE, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(0, distance),
|
||||
@ -273,7 +273,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
StartTime = Time.Current + time_offset,
|
||||
Position = new Vector2(-max_length / 2, 0),
|
||||
Path = new SliderPath(PathType.PerfectCurve, new[]
|
||||
Path = new SliderPath(PathType.PERFECT_CURVE, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(max_length / 2, max_length / 2),
|
||||
@ -293,7 +293,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
StartTime = Time.Current + time_offset,
|
||||
Position = new Vector2(-max_length / 2, 0),
|
||||
Path = new SliderPath(PathType.Linear, new[]
|
||||
Path = new SliderPath(PathType.LINEAR, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(max_length * 0.375f, max_length * 0.18f),
|
||||
@ -316,7 +316,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
StartTime = Time.Current + time_offset,
|
||||
Position = new Vector2(-max_length / 2, 0),
|
||||
Path = new SliderPath(PathType.Bezier, new[]
|
||||
Path = new SliderPath(PathType.BEZIER, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(max_length * 0.375f, max_length * 0.18f),
|
||||
@ -338,7 +338,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
StartTime = Time.Current + time_offset,
|
||||
Position = new Vector2(0, 0),
|
||||
Path = new SliderPath(PathType.Linear, new[]
|
||||
Path = new SliderPath(PathType.LINEAR, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(-max_length / 2, 0),
|
||||
@ -365,7 +365,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
StartTime = Time.Current + time_offset,
|
||||
Position = new Vector2(-max_length / 4, 0),
|
||||
Path = new SliderPath(PathType.Catmull, new[]
|
||||
Path = new SliderPath(PathType.CATMULL, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(max_length * 0.125f, max_length * 0.125f),
|
||||
|
@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
Position = new Vector2(256, 192),
|
||||
IndexInCurrentCombo = 0,
|
||||
StartTime = Time.Current,
|
||||
Path = new SliderPath(PathType.Linear, new[]
|
||||
Path = new SliderPath(PathType.LINEAR, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(150, 100),
|
||||
@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
Position = new Vector2(256, 192),
|
||||
ComboIndex = 1,
|
||||
StartTime = dho.HitObject.StartTime,
|
||||
Path = new SliderPath(PathType.Bezier, new[]
|
||||
Path = new SliderPath(PathType.BEZIER, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(150, 100),
|
||||
@ -80,7 +80,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
Position = new Vector2(256, 192),
|
||||
IndexInCurrentCombo = 0,
|
||||
StartTime = Time.Current,
|
||||
Path = new SliderPath(PathType.Linear, new[]
|
||||
Path = new SliderPath(PathType.LINEAR, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(150, 100),
|
||||
|
@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
StartTime = time_slider_start,
|
||||
Position = new Vector2(0, 0),
|
||||
SliderVelocityMultiplier = velocity,
|
||||
Path = new SliderPath(PathType.Linear, new[]
|
||||
Path = new SliderPath(PathType.LINEAR, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(followCircleRadius, 0),
|
||||
|
@ -62,7 +62,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
Position = new Vector2(0, 0),
|
||||
SliderVelocityMultiplier = 10f,
|
||||
RepeatCount = repeatCount,
|
||||
Path = new SliderPath(PathType.Linear, new[]
|
||||
Path = new SliderPath(PathType.LINEAR, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(sliderLength, 0),
|
||||
@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
Position = new Vector2(0, 0),
|
||||
SliderVelocityMultiplier = 10f,
|
||||
RepeatCount = repeatCount,
|
||||
Path = new SliderPath(PathType.Linear, new[]
|
||||
Path = new SliderPath(PathType.LINEAR, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(sliderLength, 0),
|
||||
@ -145,7 +145,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
StartTime = time_slider_start,
|
||||
Position = new Vector2(0, 0),
|
||||
SliderVelocityMultiplier = 10f,
|
||||
Path = new SliderPath(PathType.Linear, new[]
|
||||
Path = new SliderPath(PathType.LINEAR, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(slider_path_length * 10, 0),
|
||||
@ -157,7 +157,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
if (hit)
|
||||
assertAllMaxJudgements();
|
||||
else
|
||||
AddAssert("Tracking dropped", assertMidSliderJudgementFail);
|
||||
assertMidSliderJudgementFail();
|
||||
|
||||
AddAssert("Head judgement is first", () => judgementResults.First().HitObject is SliderHeadCircle);
|
||||
|
||||
@ -197,7 +197,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_during_slide_1 },
|
||||
});
|
||||
|
||||
AddAssert("Tracking lost", assertMidSliderJudgementFail);
|
||||
assertMidSliderJudgementFail();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -278,7 +278,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_before_slider },
|
||||
});
|
||||
|
||||
AddAssert("Tracking retained, sliderhead miss", assertHeadMissTailTracked);
|
||||
assertHeadMissTailTracked();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -302,7 +302,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_during_slide_4 },
|
||||
});
|
||||
|
||||
AddAssert("Tracking re-acquired", assertMidSliderJudgements);
|
||||
assertMidSliderJudgements();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -328,7 +328,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_during_slide_4 },
|
||||
});
|
||||
|
||||
AddAssert("Tracking lost", assertMidSliderJudgementFail);
|
||||
assertMidSliderJudgementFail();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -350,7 +350,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_during_slide_4 },
|
||||
});
|
||||
|
||||
AddAssert("Tracking acquired", assertMidSliderJudgements);
|
||||
assertMidSliderJudgements();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -373,7 +373,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_during_slide_2 },
|
||||
});
|
||||
|
||||
AddAssert("Tracking acquired", assertMidSliderJudgements);
|
||||
assertMidSliderJudgements();
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -387,7 +387,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.RightButton }, Time = time_during_slide_2 },
|
||||
});
|
||||
|
||||
AddAssert("Tracking acquired", assertMidSliderJudgements);
|
||||
assertMidSliderJudgements();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -412,7 +412,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_during_slide_4 },
|
||||
});
|
||||
|
||||
AddAssert("Tracking acquired", assertMidSliderJudgements);
|
||||
assertMidSliderJudgements();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -454,7 +454,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
new OsuReplayFrame { Position = new Vector2(slider_path_length, OsuHitObject.OBJECT_RADIUS * 1.201f), Actions = { OsuAction.LeftButton }, Time = time_slider_end },
|
||||
});
|
||||
|
||||
AddAssert("Tracking dropped", assertMidSliderJudgementFail);
|
||||
assertMidSliderJudgementFail();
|
||||
}
|
||||
|
||||
private void assertAllMaxJudgements()
|
||||
@ -465,11 +465,21 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
}, () => Is.EqualTo(judgementResults.Select(j => (j.HitObject, j.Judgement.MaxResult))));
|
||||
}
|
||||
|
||||
private bool assertHeadMissTailTracked() => judgementResults[^2].Type == HitResult.SmallTickHit && !judgementResults.First().IsHit;
|
||||
private void assertHeadMissTailTracked()
|
||||
{
|
||||
AddAssert("Tracking retained", () => judgementResults[^2].Type, () => Is.EqualTo(HitResult.LargeTickHit));
|
||||
AddAssert("Slider head missed", () => judgementResults.First().IsHit, () => Is.False);
|
||||
}
|
||||
|
||||
private bool assertMidSliderJudgements() => judgementResults[^2].Type == HitResult.SmallTickHit;
|
||||
private void assertMidSliderJudgements()
|
||||
{
|
||||
AddAssert("Tracking acquired", () => judgementResults[^2].Type, () => Is.EqualTo(HitResult.LargeTickHit));
|
||||
}
|
||||
|
||||
private bool assertMidSliderJudgementFail() => judgementResults[^2].Type == HitResult.SmallTickMiss;
|
||||
private void assertMidSliderJudgementFail()
|
||||
{
|
||||
AddAssert("Tracking lost", () => judgementResults[^2].Type, () => Is.EqualTo(HitResult.IgnoreMiss));
|
||||
}
|
||||
|
||||
private void performTest(List<ReplayFrame> frames, Slider? slider = null, double? bpm = null, int? tickRate = null)
|
||||
{
|
||||
@ -478,7 +488,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
StartTime = time_slider_start,
|
||||
Position = new Vector2(0, 0),
|
||||
SliderVelocityMultiplier = 0.1f,
|
||||
Path = new SliderPath(PathType.PerfectCurve, new[]
|
||||
Path = new SliderPath(PathType.PERFECT_CURVE, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(slider_path_length, 0),
|
||||
|
@ -217,7 +217,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
StartTime = 3000,
|
||||
Position = new Vector2(100, 100),
|
||||
Path = new SliderPath(PathType.PerfectCurve, new[]
|
||||
Path = new SliderPath(PathType.PERFECT_CURVE, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(300, 200)
|
||||
@ -227,7 +227,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
StartTime = 13000,
|
||||
Position = new Vector2(100, 100),
|
||||
Path = new SliderPath(PathType.PerfectCurve, new[]
|
||||
Path = new SliderPath(PathType.PERFECT_CURVE, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(300, 200)
|
||||
@ -238,7 +238,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
StartTime = 23000,
|
||||
Position = new Vector2(100, 100),
|
||||
Path = new SliderPath(PathType.PerfectCurve, new[]
|
||||
Path = new SliderPath(PathType.PERFECT_CURVE, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(300, 200)
|
||||
|
@ -196,7 +196,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
StartTime = time_slider,
|
||||
Position = positionSlider,
|
||||
Path = new SliderPath(PathType.Linear, new[]
|
||||
Path = new SliderPath(PathType.LINEAR, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(25, 0),
|
||||
@ -238,7 +238,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
StartTime = time_slider,
|
||||
Position = positionSlider,
|
||||
Path = new SliderPath(PathType.Linear, new[]
|
||||
Path = new SliderPath(PathType.LINEAR, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(25, 0),
|
||||
@ -318,7 +318,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
StartTime = time_slider,
|
||||
Position = positionSlider,
|
||||
Path = new SliderPath(PathType.Linear, new[]
|
||||
Path = new SliderPath(PathType.LINEAR, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(25, 0),
|
||||
@ -352,7 +352,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
StartTime = time_first_slider,
|
||||
Position = positionFirstSlider,
|
||||
Path = new SliderPath(PathType.Linear, new[]
|
||||
Path = new SliderPath(PathType.LINEAR, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(25, 0),
|
||||
@ -362,7 +362,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
StartTime = time_second_slider,
|
||||
Position = positionSecondSlider,
|
||||
Path = new SliderPath(PathType.Linear, new[]
|
||||
Path = new SliderPath(PathType.LINEAR, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(25, 0),
|
||||
|
@ -95,7 +95,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
yield return (ATTRIB_ID_APPROACH_RATE, ApproachRate);
|
||||
yield return (ATTRIB_ID_DIFFICULTY, StarRating);
|
||||
|
||||
if (ShouldSerializeFlashlightRating())
|
||||
if (ShouldSerializeFlashlightDifficulty())
|
||||
yield return (ATTRIB_ID_FLASHLIGHT, FlashlightDifficulty);
|
||||
|
||||
yield return (ATTRIB_ID_SLIDER_FACTOR, SliderFactor);
|
||||
@ -128,7 +128,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
// unless the fields are also renamed.
|
||||
|
||||
[UsedImplicitly]
|
||||
public bool ShouldSerializeFlashlightRating() => Mods.Any(m => m is ModFlashlight);
|
||||
public bool ShouldSerializeFlashlightDifficulty() => Mods.Any(m => m is ModFlashlight);
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
@ -221,11 +221,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
/// </summary>
|
||||
private void updatePathType()
|
||||
{
|
||||
if (ControlPoint.Type != PathType.PerfectCurve)
|
||||
if (ControlPoint.Type != PathType.PERFECT_CURVE)
|
||||
return;
|
||||
|
||||
if (PointsInSegment.Count > 3)
|
||||
ControlPoint.Type = PathType.Bezier;
|
||||
ControlPoint.Type = PathType.BEZIER;
|
||||
|
||||
if (PointsInSegment.Count != 3)
|
||||
return;
|
||||
@ -233,7 +233,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
ReadOnlySpan<Vector2> points = PointsInSegment.Select(p => p.Position).ToArray();
|
||||
RectangleF boundingBox = PathApproximator.CircularArcBoundingBox(points);
|
||||
if (boundingBox.Width >= 640 || boundingBox.Height >= 480)
|
||||
ControlPoint.Type = PathType.Bezier;
|
||||
ControlPoint.Type = PathType.BEZIER;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -256,18 +256,22 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
|
||||
private Color4 getColourFromNodeType()
|
||||
{
|
||||
if (!(ControlPoint.Type is PathType pathType))
|
||||
if (ControlPoint.Type is not PathType pathType)
|
||||
return colours.Yellow;
|
||||
|
||||
switch (pathType)
|
||||
switch (pathType.Type)
|
||||
{
|
||||
case PathType.Catmull:
|
||||
case SplineType.Catmull:
|
||||
return colours.SeaFoam;
|
||||
|
||||
case PathType.Bezier:
|
||||
return colours.Pink;
|
||||
case SplineType.BSpline:
|
||||
if (!pathType.Degree.HasValue)
|
||||
return colours.PinkLighter;
|
||||
|
||||
case PathType.PerfectCurve:
|
||||
int idx = Math.Clamp(pathType.Degree.Value, 0, 3);
|
||||
return new[] { colours.PinkDarker, colours.PinkDark, colours.Pink, colours.PinkLight }[idx];
|
||||
|
||||
case SplineType.PerfectCurve:
|
||||
return colours.PurpleDark;
|
||||
|
||||
default:
|
||||
@ -275,6 +279,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
}
|
||||
}
|
||||
|
||||
public LocalisableString TooltipText => ControlPoint.Type.ToString() ?? string.Empty;
|
||||
public LocalisableString TooltipText => ControlPoint.Type?.Description ?? string.Empty;
|
||||
}
|
||||
}
|
||||
|
@ -242,18 +242,15 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
{
|
||||
int indexInSegment = piece.PointsInSegment.IndexOf(piece.ControlPoint);
|
||||
|
||||
switch (type)
|
||||
if (type?.Type == SplineType.PerfectCurve)
|
||||
{
|
||||
case PathType.PerfectCurve:
|
||||
// Can't always create a circular arc out of 4 or more points,
|
||||
// so we split the segment into one 3-point circular arc segment
|
||||
// and one segment of the previous type.
|
||||
int thirdPointIndex = indexInSegment + 2;
|
||||
// Can't always create a circular arc out of 4 or more points,
|
||||
// so we split the segment into one 3-point circular arc segment
|
||||
// and one segment of the previous type.
|
||||
int thirdPointIndex = indexInSegment + 2;
|
||||
|
||||
if (piece.PointsInSegment.Count > thirdPointIndex + 1)
|
||||
piece.PointsInSegment[thirdPointIndex].Type = piece.PointsInSegment[0].Type;
|
||||
|
||||
break;
|
||||
if (piece.PointsInSegment.Count > thirdPointIndex + 1)
|
||||
piece.PointsInSegment[thirdPointIndex].Type = piece.PointsInSegment[0].Type;
|
||||
}
|
||||
|
||||
hitObject.Path.ExpectedDistance.Value = null;
|
||||
@ -367,13 +364,19 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
List<MenuItem> curveTypeItems = new List<MenuItem>();
|
||||
|
||||
if (!selectedPieces.Contains(Pieces[0]))
|
||||
{
|
||||
curveTypeItems.Add(createMenuItemForPathType(null));
|
||||
curveTypeItems.Add(new OsuMenuItemSpacer());
|
||||
}
|
||||
|
||||
// todo: hide/disable items which aren't valid for selected points
|
||||
curveTypeItems.Add(createMenuItemForPathType(PathType.Linear));
|
||||
curveTypeItems.Add(createMenuItemForPathType(PathType.PerfectCurve));
|
||||
curveTypeItems.Add(createMenuItemForPathType(PathType.Bezier));
|
||||
curveTypeItems.Add(createMenuItemForPathType(PathType.Catmull));
|
||||
curveTypeItems.Add(createMenuItemForPathType(PathType.LINEAR));
|
||||
curveTypeItems.Add(createMenuItemForPathType(PathType.PERFECT_CURVE));
|
||||
curveTypeItems.Add(createMenuItemForPathType(PathType.BEZIER));
|
||||
curveTypeItems.Add(createMenuItemForPathType(PathType.BSpline(3)));
|
||||
|
||||
if (selectedPieces.Any(piece => piece.ControlPoint.Type?.Type == SplineType.Catmull))
|
||||
curveTypeItems.Add(createMenuItemForPathType(PathType.CATMULL));
|
||||
|
||||
var menuItems = new List<MenuItem>
|
||||
{
|
||||
@ -405,7 +408,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
int totalCount = Pieces.Count(p => p.IsSelected.Value);
|
||||
int countOfState = Pieces.Where(p => p.IsSelected.Value).Count(p => p.ControlPoint.Type == type);
|
||||
|
||||
var item = new TernaryStateRadioMenuItem(type == null ? "Inherit" : type.ToString().Humanize(), MenuItemType.Standard, _ =>
|
||||
var item = new TernaryStateRadioMenuItem(type?.Description ?? "Inherit", MenuItemType.Standard, _ =>
|
||||
{
|
||||
foreach (var p in Pieces.Where(p => p.IsSelected.Value))
|
||||
updatePathType(p, type);
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
@ -10,6 +11,7 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
@ -44,6 +46,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
[Resolved(CanBeNull = true)]
|
||||
private IDistanceSnapProvider distanceSnapProvider { get; set; }
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
private FreehandSliderToolboxGroup freehandToolboxGroup { get; set; }
|
||||
|
||||
private readonly IncrementalBSplineBuilder bSplineBuilder = new IncrementalBSplineBuilder();
|
||||
|
||||
protected override bool IsValidForPlacement => HitObject.Path.HasValidLength;
|
||||
|
||||
public SliderPlacementBlueprint()
|
||||
@ -51,7 +58,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
HitObject.Path.ControlPoints.Add(segmentStart = new PathControlPoint(Vector2.Zero, PathType.Linear));
|
||||
HitObject.Path.ControlPoints.Add(segmentStart = new PathControlPoint(Vector2.Zero, PathType.LINEAR));
|
||||
currentSegmentLength = 1;
|
||||
}
|
||||
|
||||
@ -66,13 +73,28 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
controlPointVisualiser = new PathControlPointVisualiser<Slider>(HitObject, false)
|
||||
};
|
||||
|
||||
setState(SliderPlacementState.Initial);
|
||||
state = SliderPlacementState.Initial;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
inputManager = GetContainingInputManager();
|
||||
|
||||
if (freehandToolboxGroup != null)
|
||||
{
|
||||
freehandToolboxGroup.Tolerance.BindValueChanged(e =>
|
||||
{
|
||||
bSplineBuilder.Tolerance = e.NewValue;
|
||||
Scheduler.AddOnce(updateSliderPathFromBSplineBuilder);
|
||||
}, true);
|
||||
|
||||
freehandToolboxGroup.CornerThreshold.BindValueChanged(e =>
|
||||
{
|
||||
bSplineBuilder.CornerThreshold = e.NewValue;
|
||||
Scheduler.AddOnce(updateSliderPathFromBSplineBuilder);
|
||||
}, true);
|
||||
}
|
||||
}
|
||||
|
||||
[Resolved]
|
||||
@ -87,8 +109,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
case SliderPlacementState.Initial:
|
||||
BeginPlacement();
|
||||
|
||||
double? nearestSliderVelocity = (editorBeatmap.HitObjects
|
||||
.LastOrDefault(h => h is Slider && h.GetEndTime() < HitObject.StartTime) as Slider)?.SliderVelocityMultiplier;
|
||||
double? nearestSliderVelocity = (editorBeatmap
|
||||
.HitObjects
|
||||
.LastOrDefault(h => h is Slider && h.GetEndTime() < HitObject.StartTime) as Slider)?.SliderVelocityMultiplier;
|
||||
|
||||
HitObject.SliderVelocityMultiplier = nearestSliderVelocity ?? 1;
|
||||
HitObject.Position = ToLocalSpace(result.ScreenSpacePosition);
|
||||
@ -98,7 +121,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
ApplyDefaultsToHitObject();
|
||||
break;
|
||||
|
||||
case SliderPlacementState.Body:
|
||||
case SliderPlacementState.ControlPoints:
|
||||
updateCursor();
|
||||
break;
|
||||
}
|
||||
@ -115,7 +138,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
beginCurve();
|
||||
break;
|
||||
|
||||
case SliderPlacementState.Body:
|
||||
case SliderPlacementState.ControlPoints:
|
||||
if (canPlaceNewControlPoint(out var lastPoint))
|
||||
{
|
||||
// Place a new point by detatching the current cursor.
|
||||
@ -128,7 +151,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
Debug.Assert(lastPoint != null);
|
||||
|
||||
segmentStart = lastPoint;
|
||||
segmentStart.Type = PathType.Linear;
|
||||
segmentStart.Type = PathType.LINEAR;
|
||||
|
||||
currentSegmentLength = 1;
|
||||
}
|
||||
@ -139,25 +162,50 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool OnDragStart(DragStartEvent e)
|
||||
{
|
||||
if (e.Button != MouseButton.Left)
|
||||
return base.OnDragStart(e);
|
||||
|
||||
if (state != SliderPlacementState.ControlPoints)
|
||||
return base.OnDragStart(e);
|
||||
|
||||
// Only enter drawing mode if no additional control points have been placed.
|
||||
int controlPointCount = HitObject.Path.ControlPoints.Count;
|
||||
if (controlPointCount > 2 || (controlPointCount == 2 && HitObject.Path.ControlPoints.Last() != cursor))
|
||||
return base.OnDragStart(e);
|
||||
|
||||
bSplineBuilder.AddLinearPoint(ToLocalSpace(e.ScreenSpaceMouseDownPosition) - HitObject.Position);
|
||||
state = SliderPlacementState.Drawing;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void OnDrag(DragEvent e)
|
||||
{
|
||||
base.OnDrag(e);
|
||||
|
||||
if (state == SliderPlacementState.Drawing)
|
||||
{
|
||||
bSplineBuilder.AddLinearPoint(ToLocalSpace(e.ScreenSpaceMousePosition) - HitObject.Position);
|
||||
Scheduler.AddOnce(updateSliderPathFromBSplineBuilder);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnDragEnd(DragEndEvent e)
|
||||
{
|
||||
base.OnDragEnd(e);
|
||||
|
||||
if (state == SliderPlacementState.Drawing)
|
||||
endCurve();
|
||||
}
|
||||
|
||||
protected override void OnMouseUp(MouseUpEvent e)
|
||||
{
|
||||
if (state == SliderPlacementState.Body && e.Button == MouseButton.Right)
|
||||
if (state == SliderPlacementState.ControlPoints && e.Button == MouseButton.Right)
|
||||
endCurve();
|
||||
base.OnMouseUp(e);
|
||||
}
|
||||
|
||||
private void beginCurve()
|
||||
{
|
||||
BeginPlacement(commitStart: true);
|
||||
setState(SliderPlacementState.Body);
|
||||
}
|
||||
|
||||
private void endCurve()
|
||||
{
|
||||
updateSlider();
|
||||
EndPlacement(true);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
@ -167,21 +215,39 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
updatePathType();
|
||||
}
|
||||
|
||||
private void beginCurve()
|
||||
{
|
||||
BeginPlacement(commitStart: true);
|
||||
state = SliderPlacementState.ControlPoints;
|
||||
}
|
||||
|
||||
private void endCurve()
|
||||
{
|
||||
updateSlider();
|
||||
EndPlacement(true);
|
||||
}
|
||||
|
||||
private void updatePathType()
|
||||
{
|
||||
if (state == SliderPlacementState.Drawing)
|
||||
{
|
||||
segmentStart.Type = PathType.BSpline(3);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (currentSegmentLength)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
segmentStart.Type = PathType.Linear;
|
||||
segmentStart.Type = PathType.LINEAR;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
segmentStart.Type = PathType.PerfectCurve;
|
||||
segmentStart.Type = PathType.PERFECT_CURVE;
|
||||
break;
|
||||
|
||||
default:
|
||||
segmentStart.Type = PathType.Bezier;
|
||||
segmentStart.Type = PathType.BEZIER;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -195,13 +261,13 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
{
|
||||
HitObject.Path.ControlPoints.Add(cursor = new PathControlPoint { Position = Vector2.Zero });
|
||||
|
||||
// The path type should be adjusted in the progression of updatePathType() (Linear -> PC -> Bezier).
|
||||
// The path type should be adjusted in the progression of updatePathType() (LINEAR -> PC -> BEZIER).
|
||||
currentSegmentLength++;
|
||||
updatePathType();
|
||||
}
|
||||
|
||||
// Update the cursor position.
|
||||
var result = positionSnapProvider?.FindSnappedPositionAndTime(inputManager.CurrentState.Mouse.Position, state == SliderPlacementState.Body ? SnapType.GlobalGrids : SnapType.All);
|
||||
var result = positionSnapProvider?.FindSnappedPositionAndTime(inputManager.CurrentState.Mouse.Position, state == SliderPlacementState.ControlPoints ? SnapType.GlobalGrids : SnapType.All);
|
||||
cursor.Position = ToLocalSpace(result?.ScreenSpacePosition ?? inputManager.CurrentState.Mouse.Position) - HitObject.Position;
|
||||
}
|
||||
else if (cursor != null)
|
||||
@ -210,7 +276,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
HitObject.Path.ControlPoints.Remove(cursor);
|
||||
cursor = null;
|
||||
|
||||
// The path type should be adjusted in the reverse progression of updatePathType() (Bezier -> PC -> Linear).
|
||||
// The path type should be adjusted in the reverse progression of updatePathType() (BEZIER -> PC -> LINEAR).
|
||||
currentSegmentLength--;
|
||||
updatePathType();
|
||||
}
|
||||
@ -240,15 +306,55 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
tailCirclePiece.UpdateFrom(HitObject.TailCircle);
|
||||
}
|
||||
|
||||
private void setState(SliderPlacementState newState)
|
||||
private void updateSliderPathFromBSplineBuilder()
|
||||
{
|
||||
state = newState;
|
||||
IReadOnlyList<Vector2> builderPoints = bSplineBuilder.ControlPoints;
|
||||
|
||||
if (builderPoints.Count == 0)
|
||||
return;
|
||||
|
||||
int lastSegmentStart = 0;
|
||||
PathType? lastPathType = null;
|
||||
|
||||
HitObject.Path.ControlPoints.Clear();
|
||||
|
||||
// Iterate through generated points, finding each segment and adding non-inheriting path types where appropriate.
|
||||
// Importantly, the B-Spline builder returns three Vector2s at the same location when a new segment is to be started.
|
||||
for (int i = 0; i < builderPoints.Count; i++)
|
||||
{
|
||||
bool isLastPoint = i == builderPoints.Count - 1;
|
||||
bool isNewSegment = i < builderPoints.Count - 2 && builderPoints[i] == builderPoints[i + 1] && builderPoints[i] == builderPoints[i + 2];
|
||||
|
||||
if (isNewSegment || isLastPoint)
|
||||
{
|
||||
int pointsInSegment = i - lastSegmentStart;
|
||||
|
||||
// Where possible, we can use the simpler LINEAR path type.
|
||||
PathType? pathType = pointsInSegment == 1 ? PathType.LINEAR : PathType.BSpline(3);
|
||||
|
||||
// Linear segments can be combined, as two adjacent linear sections are computationally the same as one with the points combined.
|
||||
if (lastPathType == pathType && lastPathType == PathType.LINEAR)
|
||||
pathType = null;
|
||||
|
||||
HitObject.Path.ControlPoints.Add(new PathControlPoint(builderPoints[lastSegmentStart], pathType));
|
||||
for (int j = lastSegmentStart + 1; j < i; j++)
|
||||
HitObject.Path.ControlPoints.Add(new PathControlPoint(builderPoints[j]));
|
||||
|
||||
if (isLastPoint)
|
||||
HitObject.Path.ControlPoints.Add(new PathControlPoint(builderPoints[i]));
|
||||
|
||||
// Skip the redundant duplicated points (see isNewSegment above) which have been coalesced into a path type.
|
||||
lastSegmentStart = (i += 2);
|
||||
if (pathType != null) lastPathType = pathType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enum SliderPlacementState
|
||||
{
|
||||
Initial,
|
||||
Body,
|
||||
ControlPoints,
|
||||
Drawing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
100
osu.Game.Rulesets.Osu/Edit/FreehandSliderToolboxGroup.cs
Normal file
100
osu.Game.Rulesets.Osu/Edit/FreehandSliderToolboxGroup.cs
Normal file
@ -0,0 +1,100 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Edit
|
||||
{
|
||||
public partial class FreehandSliderToolboxGroup : EditorToolboxGroup
|
||||
{
|
||||
public FreehandSliderToolboxGroup()
|
||||
: base("slider")
|
||||
{
|
||||
}
|
||||
|
||||
public BindableFloat Tolerance { get; } = new BindableFloat(1.5f)
|
||||
{
|
||||
MinValue = 0.05f,
|
||||
MaxValue = 3f,
|
||||
Precision = 0.01f
|
||||
};
|
||||
|
||||
public BindableFloat CornerThreshold { get; } = new BindableFloat(0.4f)
|
||||
{
|
||||
MinValue = 0.05f,
|
||||
MaxValue = 1f,
|
||||
Precision = 0.01f
|
||||
};
|
||||
|
||||
// We map internal ranges to a more standard range of values for display to the user.
|
||||
private readonly BindableInt displayTolerance = new BindableInt(40)
|
||||
{
|
||||
MinValue = 5,
|
||||
MaxValue = 100
|
||||
};
|
||||
|
||||
private readonly BindableInt displayCornerThreshold = new BindableInt(40)
|
||||
{
|
||||
MinValue = 5,
|
||||
MaxValue = 100
|
||||
};
|
||||
|
||||
private ExpandableSlider<int> toleranceSlider = null!;
|
||||
private ExpandableSlider<int> cornerThresholdSlider = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
toleranceSlider = new ExpandableSlider<int>
|
||||
{
|
||||
Current = displayTolerance
|
||||
},
|
||||
cornerThresholdSlider = new ExpandableSlider<int>
|
||||
{
|
||||
Current = displayCornerThreshold
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
displayTolerance.BindValueChanged(tolerance =>
|
||||
{
|
||||
toleranceSlider.ContractedLabelText = $"C. P. S.: {tolerance.NewValue:N0}";
|
||||
toleranceSlider.ExpandedLabelText = $"Control Point Spacing: {tolerance.NewValue:N0}";
|
||||
|
||||
Tolerance.Value = displayToInternalTolerance(tolerance.NewValue);
|
||||
}, true);
|
||||
|
||||
displayCornerThreshold.BindValueChanged(threshold =>
|
||||
{
|
||||
cornerThresholdSlider.ContractedLabelText = $"C. T.: {threshold.NewValue:N0}";
|
||||
cornerThresholdSlider.ExpandedLabelText = $"Corner Threshold: {threshold.NewValue:N0}";
|
||||
|
||||
CornerThreshold.Value = displayToInternalCornerThreshold(threshold.NewValue);
|
||||
}, true);
|
||||
|
||||
Tolerance.BindValueChanged(tolerance =>
|
||||
displayTolerance.Value = internalToDisplayTolerance(tolerance.NewValue)
|
||||
);
|
||||
CornerThreshold.BindValueChanged(threshold =>
|
||||
displayCornerThreshold.Value = internalToDisplayCornerThreshold(threshold.NewValue)
|
||||
);
|
||||
|
||||
float displayToInternalTolerance(float v) => v / 33f;
|
||||
int internalToDisplayTolerance(float v) => (int)Math.Round(v * 33f);
|
||||
|
||||
float displayToInternalCornerThreshold(float v) => v / 100f;
|
||||
int internalToDisplayCornerThreshold(float v) => (int)Math.Round(v * 100f);
|
||||
}
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Caching;
|
||||
@ -63,6 +64,9 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
[Cached(typeof(IDistanceSnapProvider))]
|
||||
protected readonly OsuDistanceSnapProvider DistanceSnapProvider = new OsuDistanceSnapProvider();
|
||||
|
||||
[Cached]
|
||||
protected readonly FreehandSliderToolboxGroup FreehandlSliderToolboxGroup = new FreehandSliderToolboxGroup();
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
@ -94,10 +98,12 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
// we may be entering the screen with a selection already active
|
||||
updateDistanceSnapGrid();
|
||||
|
||||
RightToolbox.Add(new TransformToolboxGroup
|
||||
{
|
||||
RotationHandler = BlueprintContainer.SelectionHandler.RotationHandler
|
||||
});
|
||||
RightToolbox.AddRange(new EditorToolboxGroup[]
|
||||
{
|
||||
new TransformToolboxGroup { RotationHandler = BlueprintContainer.SelectionHandler.RotationHandler, },
|
||||
FreehandlSliderToolboxGroup
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
protected override ComposeBlueprintContainer CreateBlueprintContainer()
|
||||
@ -106,6 +112,34 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
public override string ConvertSelectionToString()
|
||||
=> string.Join(',', selectedHitObjects.Cast<OsuHitObject>().OrderBy(h => h.StartTime).Select(h => (h.IndexInCurrentCombo + 1).ToString()));
|
||||
|
||||
// 1,2,3,4 ...
|
||||
private static readonly Regex selection_regex = new Regex(@"^\d+(,\d+)*$", RegexOptions.Compiled);
|
||||
|
||||
public override void SelectFromTimestamp(double timestamp, string objectDescription)
|
||||
{
|
||||
if (!selection_regex.IsMatch(objectDescription))
|
||||
return;
|
||||
|
||||
List<OsuHitObject> remainingHitObjects = EditorBeatmap.HitObjects.Cast<OsuHitObject>().Where(h => h.StartTime >= timestamp).ToList();
|
||||
string[] splitDescription = objectDescription.Split(',').ToArray();
|
||||
|
||||
for (int i = 0; i < splitDescription.Length; i++)
|
||||
{
|
||||
if (!int.TryParse(splitDescription[i], out int combo) || combo < 1)
|
||||
continue;
|
||||
|
||||
OsuHitObject current = remainingHitObjects.FirstOrDefault(h => h.IndexInCurrentCombo + 1 == combo);
|
||||
|
||||
if (current == null)
|
||||
continue;
|
||||
|
||||
EditorBeatmap.SelectedHitObjects.Add(current);
|
||||
|
||||
if (i < splitDescription.Length - 1)
|
||||
remainingHitObjects = remainingHitObjects.Where(h => h != current && h.StartTime >= current.StartTime).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
private DistanceSnapGrid distanceSnapGrid;
|
||||
private Container distanceSnapGridContainer;
|
||||
|
||||
|
@ -42,6 +42,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
|
||||
SelectionBox.CanFlipX = SelectionBox.CanScaleX = quad.Width > 0;
|
||||
SelectionBox.CanFlipY = SelectionBox.CanScaleY = quad.Height > 0;
|
||||
SelectionBox.CanScaleDiagonally = SelectionBox.CanScaleX && SelectionBox.CanScaleY;
|
||||
SelectionBox.CanReverse = EditorBeatmap.SelectedHitObjects.Count > 1 || EditorBeatmap.SelectedHitObjects.Any(s => s is Slider);
|
||||
}
|
||||
|
||||
@ -320,7 +321,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
|
||||
if (mergedHitObject.Path.ControlPoints.Count == 0)
|
||||
{
|
||||
mergedHitObject.Path.ControlPoints.Add(new PathControlPoint(Vector2.Zero, PathType.Linear));
|
||||
mergedHitObject.Path.ControlPoints.Add(new PathControlPoint(Vector2.Zero, PathType.LINEAR));
|
||||
}
|
||||
|
||||
// Merge all the selected hit objects into one slider path.
|
||||
@ -350,7 +351,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
// Turn the last control point into a linear type if this is the first merging circle in a sequence, so the subsequent control points can be inherited path type.
|
||||
if (!lastCircle)
|
||||
{
|
||||
mergedHitObject.Path.ControlPoints.Last().Type = PathType.Linear;
|
||||
mergedHitObject.Path.ControlPoints.Last().Type = PathType.LINEAR;
|
||||
}
|
||||
|
||||
mergedHitObject.Path.ControlPoints.Add(new PathControlPoint(selectedMergeableObject.Position - mergedHitObject.Position));
|
||||
|
@ -33,7 +33,8 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
typeof(ModNoFail),
|
||||
typeof(ModAutoplay),
|
||||
typeof(OsuModMagnetised),
|
||||
typeof(OsuModRepel)
|
||||
typeof(OsuModRepel),
|
||||
typeof(ModTouchDevice)
|
||||
};
|
||||
|
||||
public bool PerformFail() => false;
|
||||
|
@ -1,18 +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.
|
||||
|
||||
using osu.Framework.Localisation;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
public class OsuModTouchDevice : Mod
|
||||
public class OsuModTouchDevice : ModTouchDevice
|
||||
{
|
||||
public override string Name => "Touch Device";
|
||||
public override string Acronym => "TD";
|
||||
public override LocalisableString Description => "Automatically applied to plays on devices with a touchscreen.";
|
||||
public override double ScoreMultiplier => 1;
|
||||
|
||||
public override ModType Type => ModType.System;
|
||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot) }).ToArray();
|
||||
}
|
||||
}
|
||||
|
@ -135,6 +135,8 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
classicSliderBehaviour = value;
|
||||
if (HeadCircle != null)
|
||||
HeadCircle.ClassicSliderBehaviour = value;
|
||||
if (TailCircle != null)
|
||||
TailCircle.ClassicSliderBehaviour = value;
|
||||
}
|
||||
}
|
||||
|
||||
@ -218,6 +220,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
StartTime = e.Time,
|
||||
Position = EndPosition,
|
||||
StackHeight = StackHeight,
|
||||
ClassicSliderBehaviour = ClassicSliderBehaviour,
|
||||
});
|
||||
break;
|
||||
|
||||
@ -273,9 +276,9 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
}
|
||||
|
||||
public override Judgement CreateJudgement() => ClassicSliderBehaviour
|
||||
// See logic in `DrawableSlider.CheckForResult()`
|
||||
// Final combo is provided by the slider itself - see logic in `DrawableSlider.CheckForResult()`
|
||||
? new OsuJudgement()
|
||||
// Of note, this creates a combo discrepancy for non-classic-mod sliders (there is no combo increase for tail or slider judgement).
|
||||
// Final combo is provided by the tail circle - see `SliderTailCircle`
|
||||
: new OsuIgnoreJudgement();
|
||||
|
||||
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Osu.Judgements;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects
|
||||
@ -43,5 +45,12 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
}
|
||||
|
||||
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
|
||||
|
||||
public override Judgement CreateJudgement() => new SliderEndJudgement();
|
||||
|
||||
public class SliderEndJudgement : OsuJudgement
|
||||
{
|
||||
public override HitResult MaxResult => HitResult.LargeTickHit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,6 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Osu.Judgements;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects
|
||||
{
|
||||
public class SliderRepeat : SliderEndCircle
|
||||
@ -13,12 +9,5 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
: base(slider)
|
||||
{
|
||||
}
|
||||
|
||||
public override Judgement CreateJudgement() => new SliderRepeatJudgement();
|
||||
|
||||
public class SliderRepeatJudgement : OsuJudgement
|
||||
{
|
||||
public override HitResult MaxResult => HitResult.LargeTickHit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,16 +9,28 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
{
|
||||
public class SliderTailCircle : SliderEndCircle
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether to treat this <see cref="SliderHeadCircle"/> as a normal <see cref="HitCircle"/> for judgement purposes.
|
||||
/// If <c>false</c>, this <see cref="SliderHeadCircle"/> will be judged as a <see cref="SliderTick"/> instead.
|
||||
/// </summary>
|
||||
public bool ClassicSliderBehaviour;
|
||||
|
||||
public SliderTailCircle(Slider slider)
|
||||
: base(slider)
|
||||
{
|
||||
}
|
||||
|
||||
public override Judgement CreateJudgement() => new SliderTailJudgement();
|
||||
public override Judgement CreateJudgement() => ClassicSliderBehaviour ? new LegacyTailJudgement() : new TailJudgement();
|
||||
|
||||
public class SliderTailJudgement : OsuJudgement
|
||||
public class LegacyTailJudgement : OsuJudgement
|
||||
{
|
||||
public override HitResult MaxResult => HitResult.SmallTickHit;
|
||||
}
|
||||
|
||||
public class TailJudgement : SliderEndJudgement
|
||||
{
|
||||
public override HitResult MaxResult => HitResult.LargeTickHit;
|
||||
public override HitResult MinResult => HitResult.IgnoreMiss;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -339,6 +339,11 @@ namespace osu.Game.Rulesets.Osu.Replays
|
||||
|
||||
AddFrameToReplay(startFrame);
|
||||
|
||||
// 0.05 rad/ms, or ~477 RPM, as per stable.
|
||||
// the redundant conversion from RPM to rad/ms is here for ease of testing custom SPM specs.
|
||||
const float spin_rpm = 0.05f / (2 * MathF.PI) * 60000;
|
||||
float radsPerMillisecond = MathUtils.DegreesToRadians(spin_rpm * 360) / 60000;
|
||||
|
||||
switch (h)
|
||||
{
|
||||
// We add intermediate frames for spinning / following a slider here.
|
||||
@ -354,7 +359,7 @@ namespace osu.Game.Rulesets.Osu.Replays
|
||||
for (double nextFrame = h.StartTime + GetFrameDelay(h.StartTime); nextFrame < spinner.EndTime; nextFrame += GetFrameDelay(nextFrame))
|
||||
{
|
||||
t = ApplyModsToTimeDelta(previousFrame, nextFrame) * spinnerDirection;
|
||||
angle += (float)t / 20;
|
||||
angle += (float)t * radsPerMillisecond;
|
||||
|
||||
Vector2 pos = SPINNER_CENTRE + CirclePosition(angle, SPIN_RADIUS);
|
||||
AddFrameToReplay(new OsuReplayFrame((int)nextFrame, new Vector2(pos.X, pos.Y), action));
|
||||
@ -363,7 +368,7 @@ namespace osu.Game.Rulesets.Osu.Replays
|
||||
}
|
||||
|
||||
t = ApplyModsToTimeDelta(previousFrame, spinner.EndTime) * spinnerDirection;
|
||||
angle += (float)t / 20;
|
||||
angle += (float)t * radsPerMillisecond;
|
||||
|
||||
Vector2 endPosition = SPINNER_CENTRE + CirclePosition(angle, SPIN_RADIUS);
|
||||
|
||||
|
@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Osu.Scoring
|
||||
double currentHp;
|
||||
double currentHpUncapped;
|
||||
|
||||
do
|
||||
while (true)
|
||||
{
|
||||
currentHp = 1;
|
||||
currentHpUncapped = 1;
|
||||
@ -56,7 +56,6 @@ namespace osu.Game.Rulesets.Osu.Scoring
|
||||
double lastTime = DrainStartTime;
|
||||
int currentBreak = 0;
|
||||
bool fail = false;
|
||||
string failReason = string.Empty;
|
||||
|
||||
for (int i = 0; i < Beatmap.HitObjects.Count; i++)
|
||||
{
|
||||
@ -82,7 +81,7 @@ namespace osu.Game.Rulesets.Osu.Scoring
|
||||
{
|
||||
fail = true;
|
||||
testDrop *= 0.96;
|
||||
failReason = $"hp too low ({currentHp} < {lowestHpEver})";
|
||||
OnIterationFail?.Invoke($"FAILED drop {testDrop}: hp too low ({currentHp} < {lowestHpEver})");
|
||||
break;
|
||||
}
|
||||
|
||||
@ -90,15 +89,21 @@ namespace osu.Game.Rulesets.Osu.Scoring
|
||||
double hpOverkill = Math.Max(0, hpReduction - currentHp);
|
||||
reduceHp(hpReduction);
|
||||
|
||||
if (h is Slider slider)
|
||||
switch (h)
|
||||
{
|
||||
foreach (var nested in slider.NestedHitObjects)
|
||||
increaseHp(nested);
|
||||
}
|
||||
else if (h is Spinner spinner)
|
||||
{
|
||||
foreach (var nested in spinner.NestedHitObjects.Where(t => t is not SpinnerBonusTick))
|
||||
increaseHp(nested);
|
||||
case Slider slider:
|
||||
{
|
||||
foreach (var nested in slider.NestedHitObjects)
|
||||
increaseHp(nested);
|
||||
break;
|
||||
}
|
||||
|
||||
case Spinner spinner:
|
||||
{
|
||||
foreach (var nested in spinner.NestedHitObjects.Where(t => t is not SpinnerBonusTick))
|
||||
increaseHp(nested);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Note: Because HP is capped during the above increases, long sliders (with many ticks) or spinners
|
||||
@ -107,7 +112,7 @@ namespace osu.Game.Rulesets.Osu.Scoring
|
||||
{
|
||||
fail = true;
|
||||
testDrop *= 0.96;
|
||||
failReason = $"overkill ({currentHp} - {hpOverkill} <= {lowestHpEver})";
|
||||
OnIterationFail?.Invoke($"FAILED drop {testDrop}: overkill ({currentHp} - {hpOverkill} <= {lowestHpEver})");
|
||||
break;
|
||||
}
|
||||
|
||||
@ -119,7 +124,7 @@ namespace osu.Game.Rulesets.Osu.Scoring
|
||||
fail = true;
|
||||
testDrop *= 0.94;
|
||||
hpMultiplierNormal *= 1.01;
|
||||
failReason = $"end hp too low ({currentHp} < {lowestHpEnd})";
|
||||
OnIterationFail?.Invoke($"FAILED drop {testDrop}: end hp too low ({currentHp} < {lowestHpEnd})");
|
||||
}
|
||||
|
||||
double recovery = (currentHpUncapped - 1) / Beatmap.HitObjects.Count;
|
||||
@ -129,18 +134,15 @@ namespace osu.Game.Rulesets.Osu.Scoring
|
||||
fail = true;
|
||||
testDrop *= 0.96;
|
||||
hpMultiplierNormal *= 1.01;
|
||||
failReason = $"recovery too low ({recovery} < {hpRecoveryAvailable})";
|
||||
OnIterationFail?.Invoke($"FAILED drop {testDrop}: recovery too low ({recovery} < {hpRecoveryAvailable})");
|
||||
}
|
||||
|
||||
if (fail)
|
||||
if (!fail)
|
||||
{
|
||||
OnIterationFail?.Invoke($"FAILED drop {testDrop}: {failReason}");
|
||||
continue;
|
||||
OnIterationSuccess?.Invoke($"PASSED drop {testDrop}");
|
||||
return testDrop;
|
||||
}
|
||||
|
||||
OnIterationSuccess?.Invoke($"PASSED drop {testDrop}");
|
||||
return testDrop;
|
||||
} while (true);
|
||||
}
|
||||
|
||||
void reduceHp(double amount)
|
||||
{
|
||||
@ -160,7 +162,7 @@ namespace osu.Game.Rulesets.Osu.Scoring
|
||||
|
||||
private double healthIncreaseFor(HitObject hitObject, HitResult result)
|
||||
{
|
||||
double increase;
|
||||
double increase = 0;
|
||||
|
||||
switch (result)
|
||||
{
|
||||
@ -191,19 +193,10 @@ namespace osu.Game.Rulesets.Osu.Scoring
|
||||
increase = 0.011;
|
||||
break;
|
||||
|
||||
case HitResult.Good:
|
||||
increase = 0.024;
|
||||
break;
|
||||
|
||||
case HitResult.Great:
|
||||
increase = 0.03;
|
||||
break;
|
||||
|
||||
case HitResult.Perfect:
|
||||
// 1.1 * Great. Unused.
|
||||
increase = 0.033;
|
||||
break;
|
||||
|
||||
case HitResult.SmallBonus:
|
||||
increase = 0.0085;
|
||||
break;
|
||||
@ -211,10 +204,6 @@ namespace osu.Game.Rulesets.Osu.Scoring
|
||||
case HitResult.LargeBonus:
|
||||
increase = 0.01;
|
||||
break;
|
||||
|
||||
default:
|
||||
increase = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return hpMultiplierNormal * increase;
|
||||
|
@ -88,9 +88,12 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
|
||||
|
||||
protected override void OnSliderTick()
|
||||
{
|
||||
this.ScaleTo(DrawableSliderBall.FOLLOW_AREA * 1.08f, 40, Easing.OutQuint)
|
||||
.Then()
|
||||
.ScaleTo(DrawableSliderBall.FOLLOW_AREA, 200f, Easing.OutQuint);
|
||||
if (Scale.X >= DrawableSliderBall.FOLLOW_AREA * 0.98f)
|
||||
{
|
||||
this.ScaleTo(DrawableSliderBall.FOLLOW_AREA * 1.08f, 40, Easing.OutQuint)
|
||||
.Then()
|
||||
.ScaleTo(DrawableSliderBall.FOLLOW_AREA, 200f, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnSliderBreak()
|
||||
|
@ -59,9 +59,12 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
|
||||
protected override void OnSliderTick()
|
||||
{
|
||||
this.ScaleTo(DrawableSliderBall.FOLLOW_AREA * 1.08f, 40, Easing.OutQuint)
|
||||
.Then()
|
||||
.ScaleTo(DrawableSliderBall.FOLLOW_AREA, 200f, Easing.OutQuint);
|
||||
if (Scale.X >= DrawableSliderBall.FOLLOW_AREA * 0.98f)
|
||||
{
|
||||
this.ScaleTo(DrawableSliderBall.FOLLOW_AREA * 1.08f, 40, Easing.OutQuint)
|
||||
.Then()
|
||||
.ScaleTo(DrawableSliderBall.FOLLOW_AREA, 200f, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnSliderBreak()
|
||||
|
@ -44,8 +44,11 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
|
||||
protected override void OnSliderTick()
|
||||
{
|
||||
this.ScaleTo(2.2f)
|
||||
.ScaleTo(2f, 200);
|
||||
if (Scale.X >= 2f)
|
||||
{
|
||||
this.ScaleTo(2.2f)
|
||||
.ScaleTo(2f, 200);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnSliderBreak()
|
||||
|
@ -116,7 +116,7 @@ namespace osu.Game.Rulesets.Taiko.Objects
|
||||
double IHasDistance.Distance => Duration * Velocity;
|
||||
|
||||
SliderPath IHasPath.Path
|
||||
=> new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(1) }, ((IHasDistance)this).Distance / LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER);
|
||||
=> new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(1) }, ((IHasDistance)this).Distance / LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER);
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
@ -663,7 +663,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
assertObjectHasBanks(hitObjects[9], HitSampleInfo.BANK_DRUM, HitSampleInfo.BANK_NORMAL);
|
||||
}
|
||||
|
||||
void assertObjectHasBanks(HitObject hitObject, string normalBank, string? additionsBank = null)
|
||||
static void assertObjectHasBanks(HitObject hitObject, string normalBank, string? additionsBank = null)
|
||||
{
|
||||
Assert.AreEqual(normalBank, hitObject.Samples[0].Bank);
|
||||
|
||||
@ -808,14 +808,14 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
var first = ((IHasPath)decoded.HitObjects[0]).Path;
|
||||
|
||||
Assert.That(first.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero));
|
||||
Assert.That(first.ControlPoints[0].Type, Is.EqualTo(PathType.PerfectCurve));
|
||||
Assert.That(first.ControlPoints[0].Type, Is.EqualTo(PathType.PERFECT_CURVE));
|
||||
Assert.That(first.ControlPoints[1].Position, Is.EqualTo(new Vector2(161, -244)));
|
||||
Assert.That(first.ControlPoints[1].Type, Is.EqualTo(null));
|
||||
|
||||
// ReSharper disable once HeuristicUnreachableCode
|
||||
// weird one, see https://youtrack.jetbrains.com/issue/RIDER-70159.
|
||||
Assert.That(first.ControlPoints[2].Position, Is.EqualTo(new Vector2(376, -3)));
|
||||
Assert.That(first.ControlPoints[2].Type, Is.EqualTo(PathType.Bezier));
|
||||
Assert.That(first.ControlPoints[2].Type, Is.EqualTo(PathType.BEZIER));
|
||||
Assert.That(first.ControlPoints[3].Position, Is.EqualTo(new Vector2(68, 15)));
|
||||
Assert.That(first.ControlPoints[3].Type, Is.EqualTo(null));
|
||||
Assert.That(first.ControlPoints[4].Position, Is.EqualTo(new Vector2(259, -132)));
|
||||
@ -827,7 +827,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
var second = ((IHasPath)decoded.HitObjects[1]).Path;
|
||||
|
||||
Assert.That(second.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero));
|
||||
Assert.That(second.ControlPoints[0].Type, Is.EqualTo(PathType.PerfectCurve));
|
||||
Assert.That(second.ControlPoints[0].Type, Is.EqualTo(PathType.PERFECT_CURVE));
|
||||
Assert.That(second.ControlPoints[1].Position, Is.EqualTo(new Vector2(161, -244)));
|
||||
Assert.That(second.ControlPoints[1].Type, Is.EqualTo(null));
|
||||
Assert.That(second.ControlPoints[2].Position, Is.EqualTo(new Vector2(376, -3)));
|
||||
@ -837,14 +837,14 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
var third = ((IHasPath)decoded.HitObjects[2]).Path;
|
||||
|
||||
Assert.That(third.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero));
|
||||
Assert.That(third.ControlPoints[0].Type, Is.EqualTo(PathType.Bezier));
|
||||
Assert.That(third.ControlPoints[0].Type, Is.EqualTo(PathType.BEZIER));
|
||||
Assert.That(third.ControlPoints[1].Position, Is.EqualTo(new Vector2(0, 192)));
|
||||
Assert.That(third.ControlPoints[1].Type, Is.EqualTo(null));
|
||||
Assert.That(third.ControlPoints[2].Position, Is.EqualTo(new Vector2(224, 192)));
|
||||
Assert.That(third.ControlPoints[2].Type, Is.EqualTo(null));
|
||||
|
||||
Assert.That(third.ControlPoints[3].Position, Is.EqualTo(new Vector2(224, 0)));
|
||||
Assert.That(third.ControlPoints[3].Type, Is.EqualTo(PathType.Bezier));
|
||||
Assert.That(third.ControlPoints[3].Type, Is.EqualTo(PathType.BEZIER));
|
||||
Assert.That(third.ControlPoints[4].Position, Is.EqualTo(new Vector2(224, -192)));
|
||||
Assert.That(third.ControlPoints[4].Type, Is.EqualTo(null));
|
||||
Assert.That(third.ControlPoints[5].Position, Is.EqualTo(new Vector2(480, -192)));
|
||||
@ -856,7 +856,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
var fourth = ((IHasPath)decoded.HitObjects[3]).Path;
|
||||
|
||||
Assert.That(fourth.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero));
|
||||
Assert.That(fourth.ControlPoints[0].Type, Is.EqualTo(PathType.Bezier));
|
||||
Assert.That(fourth.ControlPoints[0].Type, Is.EqualTo(PathType.BEZIER));
|
||||
Assert.That(fourth.ControlPoints[1].Position, Is.EqualTo(new Vector2(1, 1)));
|
||||
Assert.That(fourth.ControlPoints[1].Type, Is.EqualTo(null));
|
||||
Assert.That(fourth.ControlPoints[2].Position, Is.EqualTo(new Vector2(2, 2)));
|
||||
@ -870,7 +870,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
var fifth = ((IHasPath)decoded.HitObjects[4]).Path;
|
||||
|
||||
Assert.That(fifth.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero));
|
||||
Assert.That(fifth.ControlPoints[0].Type, Is.EqualTo(PathType.Bezier));
|
||||
Assert.That(fifth.ControlPoints[0].Type, Is.EqualTo(PathType.BEZIER));
|
||||
Assert.That(fifth.ControlPoints[1].Position, Is.EqualTo(new Vector2(1, 1)));
|
||||
Assert.That(fifth.ControlPoints[1].Type, Is.EqualTo(null));
|
||||
Assert.That(fifth.ControlPoints[2].Position, Is.EqualTo(new Vector2(2, 2)));
|
||||
@ -881,7 +881,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
Assert.That(fifth.ControlPoints[4].Type, Is.EqualTo(null));
|
||||
|
||||
Assert.That(fifth.ControlPoints[5].Position, Is.EqualTo(new Vector2(4, 4)));
|
||||
Assert.That(fifth.ControlPoints[5].Type, Is.EqualTo(PathType.Bezier));
|
||||
Assert.That(fifth.ControlPoints[5].Type, Is.EqualTo(PathType.BEZIER));
|
||||
Assert.That(fifth.ControlPoints[6].Position, Is.EqualTo(new Vector2(5, 5)));
|
||||
Assert.That(fifth.ControlPoints[6].Type, Is.EqualTo(null));
|
||||
|
||||
@ -889,12 +889,12 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
var sixth = ((IHasPath)decoded.HitObjects[5]).Path;
|
||||
|
||||
Assert.That(sixth.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero));
|
||||
Assert.That(sixth.ControlPoints[0].Type == PathType.Bezier);
|
||||
Assert.That(sixth.ControlPoints[0].Type == PathType.BEZIER);
|
||||
Assert.That(sixth.ControlPoints[1].Position, Is.EqualTo(new Vector2(75, 145)));
|
||||
Assert.That(sixth.ControlPoints[1].Type == null);
|
||||
Assert.That(sixth.ControlPoints[2].Position, Is.EqualTo(new Vector2(170, 75)));
|
||||
|
||||
Assert.That(sixth.ControlPoints[2].Type == PathType.Bezier);
|
||||
Assert.That(sixth.ControlPoints[2].Type == PathType.BEZIER);
|
||||
Assert.That(sixth.ControlPoints[3].Position, Is.EqualTo(new Vector2(300, 145)));
|
||||
Assert.That(sixth.ControlPoints[3].Type == null);
|
||||
Assert.That(sixth.ControlPoints[4].Position, Is.EqualTo(new Vector2(410, 20)));
|
||||
@ -904,12 +904,12 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
var seventh = ((IHasPath)decoded.HitObjects[6]).Path;
|
||||
|
||||
Assert.That(seventh.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero));
|
||||
Assert.That(seventh.ControlPoints[0].Type == PathType.PerfectCurve);
|
||||
Assert.That(seventh.ControlPoints[0].Type == PathType.PERFECT_CURVE);
|
||||
Assert.That(seventh.ControlPoints[1].Position, Is.EqualTo(new Vector2(75, 145)));
|
||||
Assert.That(seventh.ControlPoints[1].Type == null);
|
||||
Assert.That(seventh.ControlPoints[2].Position, Is.EqualTo(new Vector2(170, 75)));
|
||||
|
||||
Assert.That(seventh.ControlPoints[2].Type == PathType.PerfectCurve);
|
||||
Assert.That(seventh.ControlPoints[2].Type == PathType.PERFECT_CURVE);
|
||||
Assert.That(seventh.ControlPoints[3].Position, Is.EqualTo(new Vector2(300, 145)));
|
||||
Assert.That(seventh.ControlPoints[3].Type == null);
|
||||
Assert.That(seventh.ControlPoints[4].Position, Is.EqualTo(new Vector2(410, 20)));
|
||||
@ -1016,7 +1016,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
var controlPoints = ((IHasPath)decoded.HitObjects[0]).Path.ControlPoints;
|
||||
|
||||
Assert.That(controlPoints.Count, Is.EqualTo(6));
|
||||
Assert.That(controlPoints.Single(c => c.Type != null).Type, Is.EqualTo(PathType.Catmull));
|
||||
Assert.That(controlPoints.Single(c => c.Type != null).Type, Is.EqualTo(PathType.CATMULL));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1032,9 +1032,9 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
var controlPoints = ((IHasPath)decoded.HitObjects[0]).Path.ControlPoints;
|
||||
|
||||
Assert.That(controlPoints.Count, Is.EqualTo(4));
|
||||
Assert.That(controlPoints[0].Type, Is.EqualTo(PathType.Catmull));
|
||||
Assert.That(controlPoints[1].Type, Is.EqualTo(PathType.Catmull));
|
||||
Assert.That(controlPoints[2].Type, Is.EqualTo(PathType.Catmull));
|
||||
Assert.That(controlPoints[0].Type, Is.EqualTo(PathType.CATMULL));
|
||||
Assert.That(controlPoints[1].Type, Is.EqualTo(PathType.CATMULL));
|
||||
Assert.That(controlPoints[2].Type, Is.EqualTo(PathType.CATMULL));
|
||||
Assert.That(controlPoints[3].Type, Is.Null);
|
||||
}
|
||||
}
|
||||
@ -1051,7 +1051,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
var controlPoints = ((IHasPath)decoded.HitObjects[0]).Path.ControlPoints;
|
||||
|
||||
Assert.That(controlPoints.Count, Is.EqualTo(4));
|
||||
Assert.That(controlPoints[0].Type, Is.EqualTo(PathType.Catmull));
|
||||
Assert.That(controlPoints[0].Type, Is.EqualTo(PathType.CATMULL));
|
||||
Assert.That(controlPoints[0].Position, Is.EqualTo(Vector2.Zero));
|
||||
Assert.That(controlPoints[1].Type, Is.Null);
|
||||
Assert.That(controlPoints[1].Position, Is.Not.EqualTo(Vector2.Zero));
|
||||
|
@ -77,7 +77,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
|
||||
compareBeatmaps(decoded, decodedAfterEncode);
|
||||
|
||||
ControlPointInfo removeLegacyControlPointTypes(ControlPointInfo controlPointInfo)
|
||||
static ControlPointInfo removeLegacyControlPointTypes(ControlPointInfo controlPointInfo)
|
||||
{
|
||||
// emulate non-legacy control points by cloning the non-legacy portion.
|
||||
// the assertion is that the encoder can recreate this losslessly from hitobject data.
|
||||
@ -113,6 +113,33 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
Assert.IsTrue(areComboColoursEqual(expected.skin.Configuration, actual.skin.Configuration));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestEncodeBSplineCurveType()
|
||||
{
|
||||
var beatmap = new Beatmap
|
||||
{
|
||||
HitObjects =
|
||||
{
|
||||
new Slider
|
||||
{
|
||||
Path = new SliderPath(new[]
|
||||
{
|
||||
new PathControlPoint(Vector2.Zero, PathType.BSpline(3)),
|
||||
new PathControlPoint(new Vector2(50)),
|
||||
new PathControlPoint(new Vector2(100), PathType.BSpline(3)),
|
||||
new PathControlPoint(new Vector2(150))
|
||||
})
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
var decodedAfterEncode = decodeFromLegacy(encodeToLegacy((beatmap, new TestLegacySkin(beatmaps_resource_store, string.Empty))), string.Empty);
|
||||
var decodedSlider = (Slider)decodedAfterEncode.beatmap.HitObjects[0];
|
||||
Assert.That(decodedSlider.Path.ControlPoints.Count, Is.EqualTo(4));
|
||||
Assert.That(decodedSlider.Path.ControlPoints[0].Type, Is.EqualTo(PathType.BSpline(3)));
|
||||
Assert.That(decodedSlider.Path.ControlPoints[2].Type, Is.EqualTo(PathType.BSpline(3)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestEncodeMultiSegmentSliderWithFloatingPointError()
|
||||
{
|
||||
@ -125,10 +152,10 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
Position = new Vector2(0.6f),
|
||||
Path = new SliderPath(new[]
|
||||
{
|
||||
new PathControlPoint(Vector2.Zero, PathType.Bezier),
|
||||
new PathControlPoint(Vector2.Zero, PathType.BEZIER),
|
||||
new PathControlPoint(new Vector2(0.5f)),
|
||||
new PathControlPoint(new Vector2(0.51f)), // This is actually on the same position as the previous one in legacy beatmaps (truncated to int).
|
||||
new PathControlPoint(new Vector2(1f), PathType.Bezier),
|
||||
new PathControlPoint(new Vector2(1f), PathType.BEZIER),
|
||||
new PathControlPoint(new Vector2(2f))
|
||||
})
|
||||
},
|
||||
@ -174,8 +201,8 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
|
||||
private class TestLegacySkin : LegacySkin
|
||||
{
|
||||
public TestLegacySkin(IResourceStore<byte[]> storage, string fileName)
|
||||
: base(new SkinInfo { Name = "Test Skin", Creator = "Craftplacer" }, null, storage, fileName)
|
||||
public TestLegacySkin(IResourceStore<byte[]> fallbackStore, string fileName)
|
||||
: base(new SkinInfo { Name = "Test Skin", Creator = "Craftplacer" }, null, fallbackStore, fileName)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -162,7 +162,7 @@ namespace osu.Game.Tests.Editing
|
||||
{
|
||||
new PathControlPoint(Vector2.Zero),
|
||||
new PathControlPoint(Vector2.One),
|
||||
new PathControlPoint(new Vector2(2), PathType.Bezier),
|
||||
new PathControlPoint(new Vector2(2), PathType.BEZIER),
|
||||
new PathControlPoint(new Vector2(3)),
|
||||
}, 50)
|
||||
},
|
||||
@ -179,7 +179,7 @@ namespace osu.Game.Tests.Editing
|
||||
StartTime = 2000,
|
||||
Path = new SliderPath(new[]
|
||||
{
|
||||
new PathControlPoint(Vector2.Zero, PathType.Bezier),
|
||||
new PathControlPoint(Vector2.Zero, PathType.BEZIER),
|
||||
new PathControlPoint(new Vector2(4)),
|
||||
new PathControlPoint(new Vector2(5)),
|
||||
}, 100)
|
||||
|
@ -147,11 +147,11 @@ namespace osu.Game.Tests.Mods
|
||||
new Mod[] { new OsuModDeflate(), new OsuModApproachDifferent() },
|
||||
new[] { typeof(OsuModDeflate), typeof(OsuModApproachDifferent) }
|
||||
},
|
||||
// system mod.
|
||||
// system mod not applicable in lazer.
|
||||
new object[]
|
||||
{
|
||||
new Mod[] { new OsuModHidden(), new OsuModTouchDevice() },
|
||||
new[] { typeof(OsuModTouchDevice) }
|
||||
new Mod[] { new OsuModHidden(), new ModScoreV2() },
|
||||
new[] { typeof(ModScoreV2) }
|
||||
},
|
||||
// multi mod.
|
||||
new object[]
|
||||
|
@ -20,7 +20,7 @@ namespace osu.Game.Tests.NonVisual.Ranking
|
||||
public void TestDistributedHits()
|
||||
{
|
||||
var events = Enumerable.Range(-5, 11)
|
||||
.Select(t => new HitEvent(t - 5, HitResult.Great, new HitObject(), null, null));
|
||||
.Select(t => new HitEvent(t - 5, 1.0, HitResult.Great, new HitObject(), null, null));
|
||||
|
||||
var unstableRate = new UnstableRate(events);
|
||||
|
||||
@ -33,14 +33,46 @@ namespace osu.Game.Tests.NonVisual.Ranking
|
||||
{
|
||||
var events = new[]
|
||||
{
|
||||
new HitEvent(-100, HitResult.Miss, new HitObject(), null, null),
|
||||
new HitEvent(0, HitResult.Great, new HitObject(), null, null),
|
||||
new HitEvent(200, HitResult.Meh, new HitObject { HitWindows = HitWindows.Empty }, null, null),
|
||||
new HitEvent(-100, 1.0, HitResult.Miss, new HitObject(), null, null),
|
||||
new HitEvent(0, 1.0, HitResult.Great, new HitObject(), null, null),
|
||||
new HitEvent(200, 1.0, HitResult.Meh, new HitObject { HitWindows = HitWindows.Empty }, null, null),
|
||||
};
|
||||
|
||||
var unstableRate = new UnstableRate(events);
|
||||
|
||||
Assert.AreEqual(0, unstableRate.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStaticRateChange()
|
||||
{
|
||||
var events = new[]
|
||||
{
|
||||
new HitEvent(-150, 1.5, HitResult.Great, new HitObject(), null, null),
|
||||
new HitEvent(-150, 1.5, HitResult.Great, new HitObject(), null, null),
|
||||
new HitEvent(150, 1.5, HitResult.Great, new HitObject(), null, null),
|
||||
new HitEvent(150, 1.5, HitResult.Great, new HitObject(), null, null),
|
||||
};
|
||||
|
||||
var unstableRate = new UnstableRate(events);
|
||||
|
||||
Assert.AreEqual(10 * 100, unstableRate.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDynamicRateChange()
|
||||
{
|
||||
var events = new[]
|
||||
{
|
||||
new HitEvent(-50, 0.5, HitResult.Great, new HitObject(), null, null),
|
||||
new HitEvent(75, 0.75, HitResult.Great, new HitObject(), null, null),
|
||||
new HitEvent(-100, 1.0, HitResult.Great, new HitObject(), null, null),
|
||||
new HitEvent(125, 1.25, HitResult.Great, new HitObject(), null, null),
|
||||
};
|
||||
|
||||
var unstableRate = new UnstableRate(events);
|
||||
|
||||
Assert.AreEqual(10 * 100, unstableRate.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
BIN
osu.Game.Tests/Resources/Archives/modified-argon-20231108.osk
Normal file
BIN
osu.Game.Tests/Resources/Archives/modified-argon-20231108.osk
Normal file
Binary file not shown.
@ -11,9 +11,9 @@ namespace osu.Game.Tests.Rulesets.Scoring
|
||||
[TestFixture]
|
||||
public class HitResultTest
|
||||
{
|
||||
[TestCase(new[] { HitResult.Perfect, HitResult.Great, HitResult.Good, HitResult.Ok, HitResult.Meh }, new[] { HitResult.Miss })]
|
||||
[TestCase(new[] { HitResult.LargeTickHit }, new[] { HitResult.LargeTickMiss })]
|
||||
[TestCase(new[] { HitResult.SmallTickHit }, new[] { HitResult.SmallTickMiss })]
|
||||
[TestCase(new[] { HitResult.Perfect, HitResult.Great, HitResult.Good, HitResult.Ok, HitResult.Meh }, new[] { HitResult.Miss, HitResult.IgnoreMiss })]
|
||||
[TestCase(new[] { HitResult.LargeTickHit }, new[] { HitResult.LargeTickMiss, HitResult.IgnoreMiss })]
|
||||
[TestCase(new[] { HitResult.SmallTickHit }, new[] { HitResult.SmallTickMiss, HitResult.IgnoreMiss })]
|
||||
[TestCase(new[] { HitResult.LargeBonus, HitResult.SmallBonus }, new[] { HitResult.IgnoreMiss })]
|
||||
[TestCase(new[] { HitResult.IgnoreHit }, new[] { HitResult.IgnoreMiss, HitResult.ComboBreak })]
|
||||
public void TestValidResultPairs(HitResult[] maxResults, HitResult[] minResults)
|
||||
|
@ -10,14 +10,17 @@ using NUnit.Framework;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Catch;
|
||||
using osu.Game.Rulesets.Difficulty;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Mania;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Judgements;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.Taiko;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Scoring.Legacy;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
@ -117,6 +120,35 @@ namespace osu.Game.Tests.Rulesets.Scoring
|
||||
Assert.That(scoreProcessor.GetDisplayScore(scoringMode), Is.EqualTo(expectedScore).Within(0.5d));
|
||||
}
|
||||
|
||||
[TestCase(typeof(OsuRuleset))]
|
||||
[TestCase(typeof(TaikoRuleset))]
|
||||
[TestCase(typeof(CatchRuleset))]
|
||||
[TestCase(typeof(ManiaRuleset))]
|
||||
public void TestBeatmapWithALotOfObjectsDoesNotOverflowClassicScore(Type rulesetType)
|
||||
{
|
||||
const int object_count = 999999;
|
||||
|
||||
var ruleset = (Ruleset)Activator.CreateInstance(rulesetType)!;
|
||||
scoreProcessor = new ScoreProcessor(ruleset);
|
||||
|
||||
var largeBeatmap = new TestBeatmap(ruleset.RulesetInfo)
|
||||
{
|
||||
HitObjects = new List<HitObject>(Enumerable.Repeat(new TestHitObject(HitResult.Great), object_count))
|
||||
};
|
||||
scoreProcessor.ApplyBeatmap(largeBeatmap);
|
||||
|
||||
for (int i = 0; i < object_count; ++i)
|
||||
{
|
||||
var judgementResult = new JudgementResult(largeBeatmap.HitObjects[i], largeBeatmap.HitObjects[i].CreateJudgement())
|
||||
{
|
||||
Type = HitResult.Great
|
||||
};
|
||||
scoreProcessor.ApplyResult(judgementResult);
|
||||
}
|
||||
|
||||
Assert.That(scoreProcessor.GetDisplayScore(ScoringMode.Classic), Is.GreaterThan(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestEmptyBeatmap(
|
||||
[Values(ScoringMode.Standardised, ScoringMode.Classic)]
|
||||
|
@ -13,6 +13,7 @@ using osu.Game.Database;
|
||||
using osu.Game.Extensions;
|
||||
using osu.Game.IO;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Tests.Resources;
|
||||
using SharpCompress.Archives.Zip;
|
||||
|
||||
namespace osu.Game.Tests.Skins.IO
|
||||
@ -21,6 +22,25 @@ namespace osu.Game.Tests.Skins.IO
|
||||
{
|
||||
#region Testing filename metadata inclusion
|
||||
|
||||
[TestCase("Archives/modified-classic-20220723.osk")]
|
||||
[TestCase("Archives/modified-default-20230117.osk")]
|
||||
[TestCase("Archives/modified-argon-20231106.osk")]
|
||||
public Task TestImportModifiedSkinHasResources(string archive) => runSkinTest(async osu =>
|
||||
{
|
||||
using (var stream = TestResources.OpenResource(archive))
|
||||
{
|
||||
var imported = await loadSkinIntoOsu(osu, new ImportTask(stream, "skin.osk"));
|
||||
|
||||
// When the import filename doesn't match, it should be appended (and update the skin.ini).
|
||||
|
||||
var skinManager = osu.Dependencies.Get<SkinManager>();
|
||||
|
||||
skinManager.CurrentSkinInfo.Value = imported;
|
||||
|
||||
Assert.That(skinManager.CurrentSkin.Value.LayoutInfos.Count, Is.EqualTo(2));
|
||||
}
|
||||
});
|
||||
|
||||
[Test]
|
||||
public Task TestSingleImportDifferentFilename() => runSkinTest(async osu =>
|
||||
{
|
||||
|
@ -15,6 +15,7 @@ using osu.Game.IO.Archives;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osu.Game.Screens.Play.HUD.HitErrorMeters;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Skinning.Components;
|
||||
using osu.Game.Tests.Resources;
|
||||
|
||||
namespace osu.Game.Tests.Skins
|
||||
@ -57,6 +58,8 @@ namespace osu.Game.Tests.Skins
|
||||
"Archives/modified-argon-pro-20231001.osk",
|
||||
// Covers player name text component.
|
||||
"Archives/modified-argon-20231106.osk",
|
||||
// Covers "Argon" accuracy/score/combo counters, and wedges
|
||||
"Archives/modified-argon-20231108.osk",
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@ -100,6 +103,20 @@ namespace osu.Game.Tests.Skins
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDeserialiseModifiedArgon()
|
||||
{
|
||||
using (var stream = TestResources.OpenResource("Archives/modified-argon-20231106.osk"))
|
||||
using (var storage = new ZipArchiveReader(stream))
|
||||
{
|
||||
var skin = new TestSkin(new SkinInfo(), null, storage);
|
||||
|
||||
Assert.That(skin.LayoutInfos, Has.Count.EqualTo(2));
|
||||
Assert.That(skin.LayoutInfos[SkinComponentsContainerLookup.TargetArea.MainHUDComponents].AllDrawables.ToArray(), Has.Length.EqualTo(10));
|
||||
Assert.That(skin.LayoutInfos[SkinComponentsContainerLookup.TargetArea.MainHUDComponents].AllDrawables.Select(i => i.Type), Contains.Item(typeof(PlayerName)));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDeserialiseModifiedClassic()
|
||||
{
|
||||
@ -132,8 +149,8 @@ namespace osu.Game.Tests.Skins
|
||||
|
||||
private class TestSkin : Skin
|
||||
{
|
||||
public TestSkin(SkinInfo skin, IStorageResourceProvider? resources, IResourceStore<byte[]>? storage = null, string configurationFilename = "skin.ini")
|
||||
: base(skin, resources, storage, configurationFilename)
|
||||
public TestSkin(SkinInfo skin, IStorageResourceProvider? resources, IResourceStore<byte[]>? fallbackStore = null, string configurationFilename = "skin.ini")
|
||||
: base(skin, resources, fallbackStore, configurationFilename)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -95,8 +95,8 @@ namespace osu.Game.Tests.Skins
|
||||
{
|
||||
public const string SAMPLE_NAME = "test-sample";
|
||||
|
||||
public TestSkin(SkinInfo skin, IStorageResourceProvider? resources, IResourceStore<byte[]>? storage = null, string configurationFilename = "skin.ini")
|
||||
: base(skin, resources, storage, configurationFilename)
|
||||
public TestSkin(SkinInfo skin, IStorageResourceProvider? resources, IResourceStore<byte[]>? fallbackStore = null, string configurationFilename = "skin.ini")
|
||||
: base(skin, resources, fallbackStore, configurationFilename)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -181,6 +181,54 @@ namespace osu.Game.Tests.Visual.Background
|
||||
AddStep("restore default beatmap", () => Beatmap.SetDefault());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBeatmapBackgroundWithStoryboardUnloadedOnSuspension()
|
||||
{
|
||||
BackgroundScreenBeatmap nestedScreen = null;
|
||||
|
||||
setSupporter(true);
|
||||
setSourceMode(BackgroundSource.BeatmapWithStoryboard);
|
||||
|
||||
AddStep("change beatmap", () => Beatmap.Value = createTestWorkingBeatmapWithStoryboard());
|
||||
AddAssert("background changed", () => screen.CheckLastLoadChange() == true);
|
||||
AddUntilStep("wait for beatmap background to be loaded", () => getCurrentBackground()?.GetType() == typeof(BeatmapBackgroundWithStoryboard));
|
||||
|
||||
AddUntilStep("storyboard present", () => screen.ChildrenOfType<DrawableStoryboard>().SingleOrDefault()?.IsLoaded == true);
|
||||
|
||||
AddStep("push new background to stack", () => stack.Push(nestedScreen = new BackgroundScreenBeatmap(Beatmap.Value)));
|
||||
AddUntilStep("wait for screen to load", () => nestedScreen.IsLoaded && nestedScreen.IsCurrentScreen());
|
||||
|
||||
AddUntilStep("storyboard unloaded", () => !screen.ChildrenOfType<DrawableStoryboard>().Any());
|
||||
|
||||
AddStep("go back", () => screen.MakeCurrent());
|
||||
|
||||
AddUntilStep("storyboard reloaded", () => screen.ChildrenOfType<DrawableStoryboard>().SingleOrDefault()?.IsLoaded == true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBeatmapBackgroundWithStoryboardButBeatmapHasNone()
|
||||
{
|
||||
BackgroundScreenBeatmap nestedScreen = null;
|
||||
|
||||
setSupporter(true);
|
||||
setSourceMode(BackgroundSource.BeatmapWithStoryboard);
|
||||
|
||||
AddStep("change beatmap", () => Beatmap.Value = createTestWorkingBeatmapWithUniqueBackground());
|
||||
AddAssert("background changed", () => screen.CheckLastLoadChange() == true);
|
||||
AddUntilStep("wait for beatmap background to be loaded", () => getCurrentBackground()?.GetType() == typeof(BeatmapBackgroundWithStoryboard));
|
||||
|
||||
AddUntilStep("no storyboard loaded", () => !screen.ChildrenOfType<DrawableStoryboard>().Any());
|
||||
|
||||
AddStep("push new background to stack", () => stack.Push(nestedScreen = new BackgroundScreenBeatmap(Beatmap.Value)));
|
||||
AddUntilStep("wait for screen to load", () => nestedScreen.IsLoaded && nestedScreen.IsCurrentScreen());
|
||||
|
||||
AddUntilStep("still no storyboard", () => !screen.ChildrenOfType<DrawableStoryboard>().Any());
|
||||
|
||||
AddStep("go back", () => screen.MakeCurrent());
|
||||
|
||||
AddUntilStep("still no storyboard", () => !screen.ChildrenOfType<DrawableStoryboard>().Any());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBackgroundTypeSwitch()
|
||||
{
|
||||
|
@ -47,6 +47,7 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
|
||||
CanScaleX = true,
|
||||
CanScaleY = true,
|
||||
CanScaleDiagonally = true,
|
||||
CanFlipX = true,
|
||||
CanFlipY = true,
|
||||
|
||||
|
@ -72,7 +72,7 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
ControlPoints =
|
||||
{
|
||||
new PathControlPoint(),
|
||||
new PathControlPoint(new Vector2(100, 0), PathType.Bezier)
|
||||
new PathControlPoint(new Vector2(100, 0), PathType.BEZIER)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -34,51 +34,51 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
{
|
||||
new MenuItem("File")
|
||||
{
|
||||
Items = new[]
|
||||
Items = new OsuMenuItem[]
|
||||
{
|
||||
new EditorMenuItem("Clear All Notes"),
|
||||
new EditorMenuItem("Open Difficulty..."),
|
||||
new EditorMenuItem("Save"),
|
||||
new EditorMenuItem("Create a new Difficulty..."),
|
||||
new EditorMenuItemSpacer(),
|
||||
new OsuMenuItemSpacer(),
|
||||
new EditorMenuItem("Revert to Saved"),
|
||||
new EditorMenuItem("Revert to Saved (Full)"),
|
||||
new EditorMenuItemSpacer(),
|
||||
new OsuMenuItemSpacer(),
|
||||
new EditorMenuItem("Test Beatmap"),
|
||||
new EditorMenuItem("Open AiMod"),
|
||||
new EditorMenuItemSpacer(),
|
||||
new OsuMenuItemSpacer(),
|
||||
new EditorMenuItem("Upload Beatmap..."),
|
||||
new EditorMenuItem("Export Package"),
|
||||
new EditorMenuItem("Export Map Package"),
|
||||
new EditorMenuItem("Import from..."),
|
||||
new EditorMenuItemSpacer(),
|
||||
new OsuMenuItemSpacer(),
|
||||
new EditorMenuItem("Open Song Folder"),
|
||||
new EditorMenuItem("Open .osu in Notepad"),
|
||||
new EditorMenuItem("Open .osb in Notepad"),
|
||||
new EditorMenuItemSpacer(),
|
||||
new OsuMenuItemSpacer(),
|
||||
new EditorMenuItem("Exit"),
|
||||
}
|
||||
},
|
||||
new MenuItem("Timing")
|
||||
{
|
||||
Items = new[]
|
||||
Items = new OsuMenuItem[]
|
||||
{
|
||||
new EditorMenuItem("Time Signature"),
|
||||
new EditorMenuItem("Metronome Clicks"),
|
||||
new EditorMenuItemSpacer(),
|
||||
new OsuMenuItemSpacer(),
|
||||
new EditorMenuItem("Add Timing Section"),
|
||||
new EditorMenuItem("Add Inheriting Section"),
|
||||
new EditorMenuItem("Reset Current Section"),
|
||||
new EditorMenuItem("Delete Timing Section"),
|
||||
new EditorMenuItem("Resnap Current Section"),
|
||||
new EditorMenuItemSpacer(),
|
||||
new OsuMenuItemSpacer(),
|
||||
new EditorMenuItem("Timing Setup"),
|
||||
new EditorMenuItemSpacer(),
|
||||
new OsuMenuItemSpacer(),
|
||||
new EditorMenuItem("Resnap All Notes", MenuItemType.Destructive),
|
||||
new EditorMenuItem("Move all notes in time...", MenuItemType.Destructive),
|
||||
new EditorMenuItem("Recalculate Slider Lengths", MenuItemType.Destructive),
|
||||
new EditorMenuItem("Delete All Timing Sections", MenuItemType.Destructive),
|
||||
new EditorMenuItemSpacer(),
|
||||
new OsuMenuItemSpacer(),
|
||||
new EditorMenuItem("Set Current Position as Preview Point"),
|
||||
}
|
||||
},
|
||||
|
@ -57,7 +57,7 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
new Slider
|
||||
{
|
||||
Position = new Vector2(128, 256),
|
||||
Path = new SliderPath(PathType.Linear, new[]
|
||||
Path = new SliderPath(PathType.LINEAR, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(216, 0),
|
||||
|
151
osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs
Normal file
151
osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs
Normal file
@ -0,0 +1,151 @@
|
||||
// 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.Extensions;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Localisation;
|
||||
using osu.Game.Online.Chat;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Screens.Edit;
|
||||
using osu.Game.Screens.Menu;
|
||||
using osu.Game.Screens.Select;
|
||||
using osu.Game.Tests.Resources;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Editing
|
||||
{
|
||||
public partial class TestSceneOpenEditorTimestamp : OsuGameTestScene
|
||||
{
|
||||
private Editor editor => (Editor)Game.ScreenStack.CurrentScreen;
|
||||
private EditorBeatmap editorBeatmap => editor.ChildrenOfType<EditorBeatmap>().Single();
|
||||
private EditorClock editorClock => editor.ChildrenOfType<EditorClock>().Single();
|
||||
|
||||
[Test]
|
||||
public void TestErrorNotifications()
|
||||
{
|
||||
RulesetInfo rulesetInfo = new OsuRuleset().RulesetInfo;
|
||||
|
||||
addStepClickLink("00:00:000", waitForSeek: false);
|
||||
AddUntilStep("received 'must be in edit'",
|
||||
() => Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.MustBeInEditorToHandleLinks),
|
||||
() => Is.EqualTo(1));
|
||||
|
||||
AddStep("enter song select", () => Game.ChildrenOfType<ButtonSystem>().Single().OnSolo.Invoke());
|
||||
AddUntilStep("entered song select", () => Game.ScreenStack.CurrentScreen is PlaySongSelect);
|
||||
|
||||
addStepClickLink("00:00:000 (1)", waitForSeek: false);
|
||||
AddUntilStep("received 'must be in edit'",
|
||||
() => Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.MustBeInEditorToHandleLinks),
|
||||
() => Is.EqualTo(2));
|
||||
|
||||
setUpEditor(rulesetInfo);
|
||||
AddAssert("ruleset is osu!", () => editorBeatmap.BeatmapInfo.Ruleset.Equals(rulesetInfo));
|
||||
|
||||
addStepClickLink("00:000", "invalid link", waitForSeek: false);
|
||||
AddUntilStep("received 'failed to process'",
|
||||
() => Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.FailedToParseEditorLink),
|
||||
() => Is.EqualTo(1));
|
||||
|
||||
addStepClickLink("50000:00:000", "too long link", waitForSeek: false);
|
||||
AddUntilStep("received 'failed to process'",
|
||||
() => Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.FailedToParseEditorLink),
|
||||
() => Is.EqualTo(2));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestHandleCurrentScreenChanges()
|
||||
{
|
||||
RulesetInfo rulesetInfo = new OsuRuleset().RulesetInfo;
|
||||
|
||||
setUpEditor(rulesetInfo);
|
||||
AddAssert("is osu! ruleset", () => editorBeatmap.BeatmapInfo.Ruleset.Equals(rulesetInfo));
|
||||
|
||||
addStepClickLink("100:00:000", "long link");
|
||||
AddUntilStep("moved to end of track", () => editorClock.CurrentTime, () => Is.EqualTo(editorClock.TrackLength));
|
||||
|
||||
addStepScreenModeTo(EditorScreenMode.SongSetup);
|
||||
addStepClickLink("00:00:000");
|
||||
assertOnScreenAt(EditorScreenMode.SongSetup, 0);
|
||||
|
||||
addStepClickLink("00:05:000 (0|0)");
|
||||
assertMovedScreenTo(EditorScreenMode.Compose);
|
||||
|
||||
addStepScreenModeTo(EditorScreenMode.Design);
|
||||
addStepClickLink("00:10:000");
|
||||
assertOnScreenAt(EditorScreenMode.Design, 10_000);
|
||||
|
||||
addStepClickLink("00:15:000 (1)");
|
||||
assertMovedScreenTo(EditorScreenMode.Compose);
|
||||
|
||||
addStepScreenModeTo(EditorScreenMode.Timing);
|
||||
addStepClickLink("00:20:000");
|
||||
assertOnScreenAt(EditorScreenMode.Timing, 20_000);
|
||||
|
||||
addStepClickLink("00:25:000 (0,1)");
|
||||
assertMovedScreenTo(EditorScreenMode.Compose);
|
||||
|
||||
addStepScreenModeTo(EditorScreenMode.Verify);
|
||||
addStepClickLink("00:30:000");
|
||||
assertOnScreenAt(EditorScreenMode.Verify, 30_000);
|
||||
|
||||
addStepClickLink("00:35:000 (0,1)");
|
||||
assertMovedScreenTo(EditorScreenMode.Compose);
|
||||
|
||||
addStepClickLink("00:00:000");
|
||||
assertOnScreenAt(EditorScreenMode.Compose, 0);
|
||||
}
|
||||
|
||||
private void addStepClickLink(string timestamp, string step = "", bool waitForSeek = true)
|
||||
{
|
||||
AddStep($"{step} {timestamp}", () =>
|
||||
Game.HandleLink(new LinkDetails(LinkAction.OpenEditorTimestamp, timestamp))
|
||||
);
|
||||
|
||||
if (waitForSeek)
|
||||
AddUntilStep("wait for seek", () => editorClock.SeekingOrStopped.Value);
|
||||
}
|
||||
|
||||
private void addStepScreenModeTo(EditorScreenMode screenMode) =>
|
||||
AddStep("change screen to " + screenMode, () => editor.Mode.Value = screenMode);
|
||||
|
||||
private void assertOnScreenAt(EditorScreenMode screen, double time)
|
||||
{
|
||||
AddAssert($"stayed on {screen} at {time}", () =>
|
||||
editor.Mode.Value == screen
|
||||
&& editorClock.CurrentTime == time
|
||||
);
|
||||
}
|
||||
|
||||
private void assertMovedScreenTo(EditorScreenMode screen, string text = "moved to") =>
|
||||
AddAssert($"{text} {screen}", () => editor.Mode.Value == screen);
|
||||
|
||||
private void setUpEditor(RulesetInfo ruleset)
|
||||
{
|
||||
BeatmapSetInfo beatmapSet = null!;
|
||||
|
||||
AddStep("Import test beatmap", () =>
|
||||
Game.BeatmapManager.Import(TestResources.GetTestBeatmapForImport()).WaitSafely()
|
||||
);
|
||||
AddStep("Retrieve beatmap", () =>
|
||||
beatmapSet = Game.BeatmapManager.QueryBeatmapSet(set => !set.Protected).AsNonNull().Value.Detach()
|
||||
);
|
||||
AddStep("Present beatmap", () => Game.PresentBeatmap(beatmapSet));
|
||||
AddUntilStep("Wait for song select", () =>
|
||||
Game.Beatmap.Value.BeatmapSetInfo.Equals(beatmapSet)
|
||||
&& Game.ScreenStack.CurrentScreen is PlaySongSelect songSelect
|
||||
&& songSelect.IsLoaded
|
||||
);
|
||||
AddStep("Switch ruleset", () => Game.Ruleset.Value = ruleset);
|
||||
AddStep("Open editor for ruleset", () =>
|
||||
((PlaySongSelect)Game.ScreenStack.CurrentScreen)
|
||||
.Edit(beatmapSet.Beatmaps.Last(beatmap => beatmap.Ruleset.Name == ruleset.Name))
|
||||
);
|
||||
AddUntilStep("Wait for editor open", () => editor.ReadyForUse);
|
||||
}
|
||||
}
|
||||
}
|
@ -47,17 +47,17 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
};
|
||||
});
|
||||
|
||||
AddSliderStep("Width", 0, 1f, 1f, val =>
|
||||
{
|
||||
if (healthDisplay.IsNotNull())
|
||||
healthDisplay.BarLength.Value = val;
|
||||
});
|
||||
|
||||
AddSliderStep("Height", 0, 64, 0, val =>
|
||||
{
|
||||
if (healthDisplay.IsNotNull())
|
||||
healthDisplay.BarHeight.Value = val;
|
||||
});
|
||||
|
||||
AddSliderStep("Width", 0, 1f, 0.98f, val =>
|
||||
{
|
||||
if (healthDisplay.IsNotNull())
|
||||
healthDisplay.Width = val;
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -114,23 +114,25 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
}
|
||||
|
||||
[TestCase(PathType.Linear)]
|
||||
[TestCase(PathType.Bezier)]
|
||||
[TestCase(PathType.Catmull)]
|
||||
[TestCase(PathType.PerfectCurve)]
|
||||
public void TestSingleSegment(PathType type)
|
||||
=> AddStep("create path", () => path.ControlPoints.AddRange(createSegment(type, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
|
||||
[TestCase(SplineType.Linear, null)]
|
||||
[TestCase(SplineType.BSpline, null)]
|
||||
[TestCase(SplineType.BSpline, 3)]
|
||||
[TestCase(SplineType.Catmull, null)]
|
||||
[TestCase(SplineType.PerfectCurve, null)]
|
||||
public void TestSingleSegment(SplineType splineType, int? degree)
|
||||
=> AddStep("create path", () => path.ControlPoints.AddRange(createSegment(new PathType { Type = splineType, Degree = degree }, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
|
||||
|
||||
[TestCase(PathType.Linear)]
|
||||
[TestCase(PathType.Bezier)]
|
||||
[TestCase(PathType.Catmull)]
|
||||
[TestCase(PathType.PerfectCurve)]
|
||||
public void TestMultipleSegment(PathType type)
|
||||
[TestCase(SplineType.Linear, null)]
|
||||
[TestCase(SplineType.BSpline, null)]
|
||||
[TestCase(SplineType.BSpline, 3)]
|
||||
[TestCase(SplineType.Catmull, null)]
|
||||
[TestCase(SplineType.PerfectCurve, null)]
|
||||
public void TestMultipleSegment(SplineType splineType, int? degree)
|
||||
{
|
||||
AddStep("create path", () =>
|
||||
{
|
||||
path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero));
|
||||
path.ControlPoints.AddRange(createSegment(type, new Vector2(0, 100), new Vector2(100), Vector2.Zero));
|
||||
path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero));
|
||||
path.ControlPoints.AddRange(createSegment(new PathType { Type = splineType, Degree = degree }, new Vector2(0, 100), new Vector2(100), Vector2.Zero));
|
||||
});
|
||||
}
|
||||
|
||||
@ -139,9 +141,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
AddStep("create path", () =>
|
||||
{
|
||||
path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(100, 0)));
|
||||
path.ControlPoints.AddRange(createSegment(PathType.Bezier, new Vector2(100, 0), new Vector2(150, 30), new Vector2(100, 100)));
|
||||
path.ControlPoints.AddRange(createSegment(PathType.PerfectCurve, new Vector2(100, 100), new Vector2(25, 50), Vector2.Zero));
|
||||
path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(100, 0)));
|
||||
path.ControlPoints.AddRange(createSegment(PathType.BEZIER, new Vector2(100, 0), new Vector2(150, 30), new Vector2(100, 100)));
|
||||
path.ControlPoints.AddRange(createSegment(PathType.PERFECT_CURVE, new Vector2(100, 100), new Vector2(25, 50), Vector2.Zero));
|
||||
});
|
||||
}
|
||||
|
||||
@ -157,7 +159,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
AddStep("create path", () =>
|
||||
{
|
||||
path.ControlPoints.AddRange(createSegment(PathType.PerfectCurve, Vector2.Zero, new Vector2(width / 2, height), new Vector2(width, 0)));
|
||||
path.ControlPoints.AddRange(createSegment(PathType.PERFECT_CURVE, Vector2.Zero, new Vector2(width / 2, height), new Vector2(width, 0)));
|
||||
});
|
||||
}
|
||||
|
||||
@ -170,11 +172,11 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
switch (points)
|
||||
{
|
||||
case 2:
|
||||
path.ControlPoints.AddRange(createSegment(PathType.PerfectCurve, Vector2.Zero, new Vector2(0, 100)));
|
||||
path.ControlPoints.AddRange(createSegment(PathType.PERFECT_CURVE, Vector2.Zero, new Vector2(0, 100)));
|
||||
break;
|
||||
|
||||
case 4:
|
||||
path.ControlPoints.AddRange(createSegment(PathType.PerfectCurve, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0)));
|
||||
path.ControlPoints.AddRange(createSegment(PathType.PERFECT_CURVE, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0)));
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
@ -88,7 +88,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
HitWindows = new HitWindows(),
|
||||
StartTime = t += spacing,
|
||||
Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, Vector2.UnitY * 200 }),
|
||||
Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, Vector2.UnitY * 200 }),
|
||||
Samples = new[] { new HitSampleInfo(HitSampleInfo.HIT_WHISTLE, HitSampleInfo.BANK_SOFT) },
|
||||
},
|
||||
});
|
||||
|
@ -4,6 +4,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
@ -96,7 +97,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
AddStep("Begin drag top left", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(box1.ScreenSpaceDrawQuad.TopLeft - new Vector2(box1.ScreenSpaceDrawQuad.Width / 4));
|
||||
InputManager.MoveMouseTo(box1.ScreenSpaceDrawQuad.TopLeft - new Vector2(box1.ScreenSpaceDrawQuad.Width / 4, box1.ScreenSpaceDrawQuad.Height / 8));
|
||||
InputManager.PressButton(MouseButton.Left);
|
||||
});
|
||||
|
||||
@ -146,8 +147,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
AddStep("Add big black box", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(skinEditor.ChildrenOfType<BigBlackBox>().First());
|
||||
InputManager.Click(MouseButton.Left);
|
||||
skinEditor.ChildrenOfType<SkinComponentToolbox.ToolboxComponentButton>().First(b => b.ChildrenOfType<BigBlackBox>().FirstOrDefault() != null).TriggerClick();
|
||||
});
|
||||
|
||||
AddStep("store box", () =>
|
||||
@ -243,7 +243,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
void revertAndCheckUnchanged()
|
||||
{
|
||||
AddStep("Revert changes", () => changeHandler.RestoreState(int.MinValue));
|
||||
AddAssert("Current state is same as default", () => defaultState.SequenceEqual(changeHandler.GetCurrentState()));
|
||||
AddAssert("Current state is same as default",
|
||||
() => Encoding.UTF8.GetString(defaultState),
|
||||
() => Is.EqualTo(Encoding.UTF8.GetString(changeHandler.GetCurrentState())));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
[Cached]
|
||||
private ScoreProcessor scoreProcessor = new ScoreProcessor(new OsuRuleset());
|
||||
|
||||
protected override Drawable CreateArgonImplementation() => new ArgonAccuracyCounter();
|
||||
protected override Drawable CreateDefaultImplementation() => new DefaultAccuracyCounter();
|
||||
protected override Drawable CreateLegacyImplementation() => new LegacyAccuracyCounter();
|
||||
|
||||
|
@ -17,6 +17,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
[Cached]
|
||||
private ScoreProcessor scoreProcessor = new ScoreProcessor(new OsuRuleset());
|
||||
|
||||
protected override Drawable CreateArgonImplementation() => new ArgonComboCounter();
|
||||
protected override Drawable CreateDefaultImplementation() => new DefaultComboCounter();
|
||||
protected override Drawable CreateLegacyImplementation() => new LegacyComboCounter();
|
||||
|
||||
|
@ -21,7 +21,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
[Cached(typeof(HealthProcessor))]
|
||||
private HealthProcessor healthProcessor = new DrainingHealthProcessor(0);
|
||||
|
||||
protected override Drawable CreateArgonImplementation() => new ArgonHealthDisplay { Scale = new Vector2(0.6f) };
|
||||
protected override Drawable CreateArgonImplementation() => new ArgonHealthDisplay { Scale = new Vector2(0.6f), Width = 1f };
|
||||
protected override Drawable CreateDefaultImplementation() => new DefaultHealthDisplay { Scale = new Vector2(0.6f) };
|
||||
protected override Drawable CreateLegacyImplementation() => new LegacyHealthDisplay { Scale = new Vector2(0.6f) };
|
||||
|
||||
|
@ -17,6 +17,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
[Cached(typeof(ScoreProcessor))]
|
||||
private ScoreProcessor scoreProcessor = TestGameplayState.Create(new OsuRuleset()).ScoreProcessor;
|
||||
|
||||
protected override Drawable CreateArgonImplementation() => new ArgonScoreCounter();
|
||||
protected override Drawable CreateDefaultImplementation() => new DefaultScoreCounter();
|
||||
protected override Drawable CreateLegacyImplementation() => new LegacyScoreCounter();
|
||||
|
||||
|
@ -52,59 +52,68 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
}
|
||||
|
||||
[TestCase(PathType.Linear)]
|
||||
[TestCase(PathType.Bezier)]
|
||||
[TestCase(PathType.Catmull)]
|
||||
[TestCase(PathType.PerfectCurve)]
|
||||
public void TestSingleSegment(PathType type)
|
||||
=> AddStep("create path", () => path.ControlPoints.AddRange(createSegment(type, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
|
||||
[TestCase(SplineType.Linear, null)]
|
||||
[TestCase(SplineType.BSpline, null)]
|
||||
[TestCase(SplineType.BSpline, 3)]
|
||||
[TestCase(SplineType.Catmull, null)]
|
||||
[TestCase(SplineType.PerfectCurve, null)]
|
||||
public void TestSingleSegment(SplineType splineType, int? degree)
|
||||
=> AddStep("create path", () => path.ControlPoints.AddRange(createSegment(
|
||||
new PathType { Type = splineType, Degree = degree },
|
||||
Vector2.Zero,
|
||||
new Vector2(0, 100),
|
||||
new Vector2(100),
|
||||
new Vector2(0, 200),
|
||||
new Vector2(200)
|
||||
)));
|
||||
|
||||
[TestCase(PathType.Linear)]
|
||||
[TestCase(PathType.Bezier)]
|
||||
[TestCase(PathType.Catmull)]
|
||||
[TestCase(PathType.PerfectCurve)]
|
||||
public void TestMultipleSegment(PathType type)
|
||||
[TestCase(SplineType.Linear, null)]
|
||||
[TestCase(SplineType.BSpline, null)]
|
||||
[TestCase(SplineType.BSpline, 3)]
|
||||
[TestCase(SplineType.Catmull, null)]
|
||||
[TestCase(SplineType.PerfectCurve, null)]
|
||||
public void TestMultipleSegment(SplineType splineType, int? degree)
|
||||
{
|
||||
AddStep("create path", () =>
|
||||
{
|
||||
path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero));
|
||||
path.ControlPoints.AddRange(createSegment(type, new Vector2(0, 100), new Vector2(100), Vector2.Zero));
|
||||
path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero));
|
||||
path.ControlPoints.AddRange(createSegment(new PathType { Type = splineType, Degree = degree }, new Vector2(0, 100), new Vector2(100), Vector2.Zero));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAddControlPoint()
|
||||
{
|
||||
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100))));
|
||||
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100))));
|
||||
AddStep("add point", () => path.ControlPoints.Add(new PathControlPoint { Position = new Vector2(100) }));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestInsertControlPoint()
|
||||
{
|
||||
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(100))));
|
||||
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(100))));
|
||||
AddStep("insert point", () => path.ControlPoints.Insert(1, new PathControlPoint { Position = new Vector2(0, 100) }));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRemoveControlPoint()
|
||||
{
|
||||
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
|
||||
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
|
||||
AddStep("remove second point", () => path.ControlPoints.RemoveAt(1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestChangePathType()
|
||||
{
|
||||
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
|
||||
AddStep("change type to bezier", () => path.ControlPoints[0].Type = PathType.Bezier);
|
||||
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
|
||||
AddStep("change type to bezier", () => path.ControlPoints[0].Type = PathType.BEZIER);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAddSegmentByChangingType()
|
||||
{
|
||||
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0))));
|
||||
AddStep("change second point type to bezier", () => path.ControlPoints[1].Type = PathType.Bezier);
|
||||
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0))));
|
||||
AddStep("change second point type to bezier", () => path.ControlPoints[1].Type = PathType.BEZIER);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -112,8 +121,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
AddStep("create path", () =>
|
||||
{
|
||||
path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0)));
|
||||
path.ControlPoints[1].Type = PathType.Bezier;
|
||||
path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0)));
|
||||
path.ControlPoints[1].Type = PathType.BEZIER;
|
||||
});
|
||||
|
||||
AddStep("change second point type to null", () => path.ControlPoints[1].Type = null);
|
||||
@ -124,8 +133,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
AddStep("create path", () =>
|
||||
{
|
||||
path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0)));
|
||||
path.ControlPoints[1].Type = PathType.Bezier;
|
||||
path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0)));
|
||||
path.ControlPoints[1].Type = PathType.BEZIER;
|
||||
});
|
||||
|
||||
AddStep("remove second point", () => path.ControlPoints.RemoveAt(1));
|
||||
@ -140,11 +149,11 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
switch (points)
|
||||
{
|
||||
case 2:
|
||||
path.ControlPoints.AddRange(createSegment(PathType.PerfectCurve, Vector2.Zero, new Vector2(0, 100)));
|
||||
path.ControlPoints.AddRange(createSegment(PathType.PERFECT_CURVE, Vector2.Zero, new Vector2(0, 100)));
|
||||
break;
|
||||
|
||||
case 4:
|
||||
path.ControlPoints.AddRange(createSegment(PathType.PerfectCurve, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0)));
|
||||
path.ControlPoints.AddRange(createSegment(PathType.PERFECT_CURVE, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0)));
|
||||
break;
|
||||
}
|
||||
});
|
||||
@ -153,35 +162,35 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
[Test]
|
||||
public void TestLengthenLastSegment()
|
||||
{
|
||||
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
|
||||
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
|
||||
AddStep("lengthen last segment", () => path.ExpectedDistance.Value = 300);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestShortenLastSegment()
|
||||
{
|
||||
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
|
||||
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
|
||||
AddStep("shorten last segment", () => path.ExpectedDistance.Value = 150);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestShortenFirstSegment()
|
||||
{
|
||||
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
|
||||
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
|
||||
AddStep("shorten first segment", () => path.ExpectedDistance.Value = 50);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestShortenToZeroLength()
|
||||
{
|
||||
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
|
||||
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
|
||||
AddStep("shorten to 0 length", () => path.ExpectedDistance.Value = 0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestShortenToNegativeLength()
|
||||
{
|
||||
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
|
||||
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
|
||||
AddStep("shorten to -10 length", () => path.ExpectedDistance.Value = -10);
|
||||
}
|
||||
|
||||
@ -197,7 +206,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
};
|
||||
double[] distances = { 100d, 200d, 300d };
|
||||
|
||||
AddStep("create path", () => path.ControlPoints.AddRange(positions.Select(p => new PathControlPoint(p, PathType.Linear))));
|
||||
AddStep("create path", () => path.ControlPoints.AddRange(positions.Select(p => new PathControlPoint(p, PathType.LINEAR))));
|
||||
AddAssert("segment ends are correct", () => path.GetSegmentEnds(), () => Is.EqualTo(distances.Select(d => d / 300)));
|
||||
AddAssert("segment end positions recovered", () => path.GetSegmentEnds().Select(p => path.PositionAt(p)), () => Is.EqualTo(positions.Skip(1)));
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
@ -56,6 +57,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
scoreProcessor.RevertResult(
|
||||
new JudgementResult(new HitCircle { HitWindows = hitWindows }, new Judgement())
|
||||
{
|
||||
GameplayRate = 1.0,
|
||||
TimeOffset = 25,
|
||||
Type = HitResult.Perfect,
|
||||
});
|
||||
@ -80,6 +82,27 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddUntilStep("UR = 250", () => counter.Current.Value == 250.0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStaticRateChange()
|
||||
{
|
||||
AddStep("Create Display", recreateDisplay);
|
||||
|
||||
AddRepeatStep("Set UR to 250 at 1.5x", () => applyJudgement(25, true, 1.5), 4);
|
||||
|
||||
AddUntilStep("UR = 250/1.5", () => counter.Current.Value == Math.Round(250.0 / 1.5));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDynamicRateChange()
|
||||
{
|
||||
AddStep("Create Display", recreateDisplay);
|
||||
|
||||
AddRepeatStep("Set UR to 100 at 1.0x", () => applyJudgement(10, true, 1.0), 4);
|
||||
AddRepeatStep("Bring UR to 100 at 1.5x", () => applyJudgement(15, true, 1.5), 4);
|
||||
|
||||
AddUntilStep("UR = 100", () => counter.Current.Value == 100.0);
|
||||
}
|
||||
|
||||
private void recreateDisplay()
|
||||
{
|
||||
Clear();
|
||||
@ -92,7 +115,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
});
|
||||
}
|
||||
|
||||
private void applyJudgement(double offsetMs, bool alt)
|
||||
private void applyJudgement(double offsetMs, bool alt, double gameplayRate = 1.0)
|
||||
{
|
||||
double placement = offsetMs;
|
||||
|
||||
@ -105,6 +128,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
scoreProcessor.ApplyResult(new JudgementResult(new HitCircle { HitWindows = hitWindows }, new Judgement())
|
||||
{
|
||||
TimeOffset = placement,
|
||||
GameplayRate = gameplayRate,
|
||||
Type = HitResult.Perfect,
|
||||
});
|
||||
}
|
||||
|
@ -133,6 +133,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
private bool assertAllAvailableModsSelected()
|
||||
{
|
||||
var allAvailableMods = availableMods.Value
|
||||
.Where(pair => pair.Key != ModType.System)
|
||||
.SelectMany(pair => pair.Value)
|
||||
.Where(mod => mod.UserPlayable && mod.HasImplementation)
|
||||
.ToList();
|
||||
|
@ -12,6 +12,7 @@ using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
@ -835,6 +836,110 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
AddAssert("exit dialog is shown", () => Game.Dependencies.Get<IDialogOverlay>().CurrentDialog is ConfirmExitDialog);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTouchScreenDetectionAtSongSelect()
|
||||
{
|
||||
AddStep("touch logo", () =>
|
||||
{
|
||||
var button = Game.ChildrenOfType<OsuLogo>().Single();
|
||||
var touch = new Touch(TouchSource.Touch1, button.ScreenSpaceDrawQuad.Centre);
|
||||
InputManager.BeginTouch(touch);
|
||||
InputManager.EndTouch(touch);
|
||||
});
|
||||
AddAssert("touch screen detected active", () => Game.Dependencies.Get<SessionStatics>().Get<bool>(Static.TouchInputActive), () => Is.True);
|
||||
|
||||
AddStep("click settings button", () =>
|
||||
{
|
||||
var button = Game.ChildrenOfType<MainMenuButton>().Last();
|
||||
InputManager.MoveMouseTo(button);
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
AddAssert("touch screen detected inactive", () => Game.Dependencies.Get<SessionStatics>().Get<bool>(Static.TouchInputActive), () => Is.False);
|
||||
|
||||
AddStep("close settings sidebar", () => InputManager.Key(Key.Escape));
|
||||
|
||||
Screens.Select.SongSelect songSelect = null;
|
||||
AddRepeatStep("go to solo", () => InputManager.Key(Key.P), 3);
|
||||
AddUntilStep("wait for song select", () => (songSelect = Game.ScreenStack.CurrentScreen as Screens.Select.SongSelect) != null);
|
||||
AddUntilStep("wait for beatmap sets loaded", () => songSelect.BeatmapSetsLoaded);
|
||||
|
||||
AddStep("switch to osu! ruleset", () =>
|
||||
{
|
||||
InputManager.PressKey(Key.LControl);
|
||||
InputManager.Key(Key.Number1);
|
||||
InputManager.ReleaseKey(Key.LControl);
|
||||
});
|
||||
AddStep("touch beatmap wedge", () =>
|
||||
{
|
||||
var wedge = Game.ChildrenOfType<BeatmapInfoWedge>().Single();
|
||||
var touch = new Touch(TouchSource.Touch2, wedge.ScreenSpaceDrawQuad.Centre);
|
||||
InputManager.BeginTouch(touch);
|
||||
InputManager.EndTouch(touch);
|
||||
});
|
||||
AddUntilStep("touch device mod activated", () => Game.SelectedMods.Value, () => Has.One.InstanceOf<ModTouchDevice>());
|
||||
|
||||
AddStep("switch to mania ruleset", () =>
|
||||
{
|
||||
InputManager.PressKey(Key.LControl);
|
||||
InputManager.Key(Key.Number4);
|
||||
InputManager.ReleaseKey(Key.LControl);
|
||||
});
|
||||
AddUntilStep("touch device mod not activated", () => Game.SelectedMods.Value, () => Has.None.InstanceOf<ModTouchDevice>());
|
||||
AddStep("touch beatmap wedge", () =>
|
||||
{
|
||||
var wedge = Game.ChildrenOfType<BeatmapInfoWedge>().Single();
|
||||
var touch = new Touch(TouchSource.Touch2, wedge.ScreenSpaceDrawQuad.Centre);
|
||||
InputManager.BeginTouch(touch);
|
||||
InputManager.EndTouch(touch);
|
||||
});
|
||||
AddUntilStep("touch device mod not activated", () => Game.SelectedMods.Value, () => Has.None.InstanceOf<ModTouchDevice>());
|
||||
|
||||
AddStep("switch to osu! ruleset", () =>
|
||||
{
|
||||
InputManager.PressKey(Key.LControl);
|
||||
InputManager.Key(Key.Number1);
|
||||
InputManager.ReleaseKey(Key.LControl);
|
||||
});
|
||||
AddUntilStep("touch device mod activated", () => Game.SelectedMods.Value, () => Has.One.InstanceOf<ModTouchDevice>());
|
||||
|
||||
AddStep("click beatmap wedge", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(Game.ChildrenOfType<BeatmapInfoWedge>().Single());
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
AddUntilStep("touch device mod not activated", () => Game.SelectedMods.Value, () => Has.None.InstanceOf<ModTouchDevice>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTouchScreenDetectionInGame()
|
||||
{
|
||||
PushAndConfirm(() => new TestPlaySongSelect());
|
||||
AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely());
|
||||
AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault);
|
||||
AddStep("select", () => InputManager.Key(Key.Enter));
|
||||
|
||||
Player player = null;
|
||||
|
||||
AddUntilStep("wait for player", () =>
|
||||
{
|
||||
DismissAnyNotifications();
|
||||
return (player = Game.ScreenStack.CurrentScreen as Player) != null;
|
||||
});
|
||||
|
||||
AddUntilStep("wait for track playing", () => Game.Beatmap.Value.Track.IsRunning);
|
||||
|
||||
AddStep("touch", () =>
|
||||
{
|
||||
var touch = new Touch(TouchSource.Touch2, Game.ScreenSpaceDrawQuad.Centre);
|
||||
InputManager.BeginTouch(touch);
|
||||
InputManager.EndTouch(touch);
|
||||
});
|
||||
AddUntilStep("touch device mod added to score", () => player.Score.ScoreInfo.Mods, () => Has.One.InstanceOf<ModTouchDevice>());
|
||||
|
||||
AddStep("exit player", () => player.Exit());
|
||||
AddUntilStep("touch device mod still active", () => Game.SelectedMods.Value, () => Has.One.InstanceOf<ModTouchDevice>());
|
||||
}
|
||||
|
||||
private Func<Player> playToResults()
|
||||
{
|
||||
var player = playToCompletion();
|
||||
|
@ -64,7 +64,7 @@ namespace osu.Game.Tests.Visual.Online
|
||||
new[] { "Plain", "This is plain comment" },
|
||||
new[] { "Pinned", "This is pinned comment" },
|
||||
new[] { "Link", "Please visit https://osu.ppy.sh" },
|
||||
new[] { "Big Image", "![](Backgrounds/bg1)" },
|
||||
new[] { "Big Image", "![](Backgrounds/bg1 \"Big Image\")" },
|
||||
new[] { "Small Image", "![](Cursor/cursortrail)" },
|
||||
new[]
|
||||
{
|
||||
|
86
osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs
Normal file
86
osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs
Normal file
@ -0,0 +1,86 @@
|
||||
// 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.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Effects;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Graphics.Cursor;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Users;
|
||||
using osu.Game.Users.Drawables;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Online
|
||||
{
|
||||
public partial class TestSceneUserClickableAvatar : OsuManualInputManagerTestScene
|
||||
{
|
||||
[SetUp]
|
||||
public void SetUp() => Schedule(() =>
|
||||
{
|
||||
Child = new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Spacing = new Vector2(10f),
|
||||
Children = new[]
|
||||
{
|
||||
generateUser(@"peppy", 2, CountryCode.AU, @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", false, "99EB47"),
|
||||
generateUser(@"flyte", 3103765, CountryCode.JP, @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg", true),
|
||||
generateUser(@"joshika39", 17032217, CountryCode.RS, @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", false),
|
||||
new UpdateableAvatar(),
|
||||
new UpdateableAvatar()
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
[Test]
|
||||
public void TestClickableAvatarHover()
|
||||
{
|
||||
AddStep("hover avatar with user panel", () => InputManager.MoveMouseTo(this.ChildrenOfType<ClickableAvatar>().ElementAt(1)));
|
||||
AddUntilStep("wait for tooltip to show", () => this.ChildrenOfType<ClickableAvatar.UserCardTooltip>().FirstOrDefault()?.State.Value == Visibility.Visible);
|
||||
AddStep("hover out", () => InputManager.MoveMouseTo(new Vector2(0)));
|
||||
AddUntilStep("wait for tooltip to hide", () => this.ChildrenOfType<ClickableAvatar.UserCardTooltip>().FirstOrDefault()?.State.Value == Visibility.Hidden);
|
||||
|
||||
AddStep("hover avatar without user panel", () => InputManager.MoveMouseTo(this.ChildrenOfType<ClickableAvatar>().ElementAt(0)));
|
||||
AddUntilStep("wait for tooltip to show", () => this.ChildrenOfType<OsuTooltipContainer.OsuTooltip>().FirstOrDefault()?.State.Value == Visibility.Visible);
|
||||
AddStep("hover out", () => InputManager.MoveMouseTo(new Vector2(0)));
|
||||
AddUntilStep("wait for tooltip to hide", () => this.ChildrenOfType<OsuTooltipContainer.OsuTooltip>().FirstOrDefault()?.State.Value == Visibility.Hidden);
|
||||
}
|
||||
|
||||
private Drawable generateUser(string username, int id, CountryCode countryCode, string cover, bool showPanel, string? color = null)
|
||||
{
|
||||
var user = new APIUser
|
||||
{
|
||||
Username = username,
|
||||
Id = id,
|
||||
CountryCode = countryCode,
|
||||
CoverUrl = cover,
|
||||
Colour = color ?? "000000",
|
||||
Status =
|
||||
{
|
||||
Value = new UserStatusOnline()
|
||||
},
|
||||
};
|
||||
|
||||
return new ClickableAvatar(user, showPanel)
|
||||
{
|
||||
Width = 50,
|
||||
Height = 50,
|
||||
CornerRadius = 10,
|
||||
Masking = true,
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Radius = 1,
|
||||
Colour = Color4.Black.Opacity(0.2f),
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -78,6 +78,31 @@ namespace osu.Game.Tests.Visual.Online
|
||||
AddStep("complete request", () => pendingRequest.TriggerSuccess(TEST_USER));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLogin()
|
||||
{
|
||||
GetUserRequest pendingRequest = null!;
|
||||
|
||||
AddStep("set up request handling", () =>
|
||||
{
|
||||
dummyAPI.HandleRequest = req =>
|
||||
{
|
||||
if (dummyAPI.State.Value == APIState.Online && req is GetUserRequest getUserRequest)
|
||||
{
|
||||
pendingRequest = getUserRequest;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
});
|
||||
AddStep("logout", () => dummyAPI.Logout());
|
||||
AddStep("show user", () => profile.ShowUser(new APIUser { Id = 1 }));
|
||||
AddStep("login", () => dummyAPI.Login("username", "password"));
|
||||
AddWaitStep("wait some", 3);
|
||||
AddStep("complete request", () => pendingRequest.TriggerSuccess(TEST_USER));
|
||||
}
|
||||
|
||||
public static readonly APIUser TEST_USER = new APIUser
|
||||
{
|
||||
Username = @"Somebody",
|
||||
|
@ -10,7 +10,6 @@ using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Containers.Markdown;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Testing;
|
||||
@ -298,7 +297,7 @@ This is a line after the fenced code block!
|
||||
{
|
||||
public LinkInline Link;
|
||||
|
||||
public override MarkdownTextFlowContainer CreateTextFlow() => new TestMarkdownTextFlowContainer
|
||||
public override OsuMarkdownTextFlowContainer CreateTextFlow() => new TestMarkdownTextFlowContainer
|
||||
{
|
||||
UrlAdded = link => Link = link,
|
||||
};
|
||||
|
@ -49,7 +49,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
||||
[Test]
|
||||
public void TestAroundCentre()
|
||||
{
|
||||
createTest(Enumerable.Range(-150, 300).Select(i => new HitEvent(i / 50f, HitResult.Perfect, placeholder_object, placeholder_object, null)).ToList());
|
||||
createTest(Enumerable.Range(-150, 300).Select(i => new HitEvent(i / 50f, 1.0, HitResult.Perfect, placeholder_object, placeholder_object, null)).ToList());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -57,12 +57,12 @@ namespace osu.Game.Tests.Visual.Ranking
|
||||
{
|
||||
createTest(new List<HitEvent>
|
||||
{
|
||||
new HitEvent(-7, HitResult.Perfect, placeholder_object, placeholder_object, null),
|
||||
new HitEvent(-6, HitResult.Perfect, placeholder_object, placeholder_object, null),
|
||||
new HitEvent(-5, HitResult.Perfect, placeholder_object, placeholder_object, null),
|
||||
new HitEvent(5, HitResult.Perfect, placeholder_object, placeholder_object, null),
|
||||
new HitEvent(6, HitResult.Perfect, placeholder_object, placeholder_object, null),
|
||||
new HitEvent(7, HitResult.Perfect, placeholder_object, placeholder_object, null),
|
||||
new HitEvent(-7, 1.0, HitResult.Perfect, placeholder_object, placeholder_object, null),
|
||||
new HitEvent(-6, 1.0, HitResult.Perfect, placeholder_object, placeholder_object, null),
|
||||
new HitEvent(-5, 1.0, HitResult.Perfect, placeholder_object, placeholder_object, null),
|
||||
new HitEvent(5, 1.0, HitResult.Perfect, placeholder_object, placeholder_object, null),
|
||||
new HitEvent(6, 1.0, HitResult.Perfect, placeholder_object, placeholder_object, null),
|
||||
new HitEvent(7, 1.0, HitResult.Perfect, placeholder_object, placeholder_object, null),
|
||||
});
|
||||
}
|
||||
|
||||
@ -78,7 +78,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
||||
: offset > 16 ? HitResult.Good
|
||||
: offset > 8 ? HitResult.Great
|
||||
: HitResult.Perfect;
|
||||
return new HitEvent(h.TimeOffset, result, placeholder_object, placeholder_object, null);
|
||||
return new HitEvent(h.TimeOffset, 1.0, result, placeholder_object, placeholder_object, null);
|
||||
}).ToList());
|
||||
}
|
||||
|
||||
@ -95,7 +95,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
||||
: offset > 8 ? HitResult.Great
|
||||
: HitResult.Perfect;
|
||||
|
||||
return new HitEvent(h.TimeOffset, result, placeholder_object, placeholder_object, null);
|
||||
return new HitEvent(h.TimeOffset, 1.0, result, placeholder_object, placeholder_object, null);
|
||||
});
|
||||
var narrow = CreateDistributedHitEvents(0, 50).Select(h =>
|
||||
{
|
||||
@ -106,7 +106,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
||||
: offset > 10 ? HitResult.Good
|
||||
: offset > 5 ? HitResult.Great
|
||||
: HitResult.Perfect;
|
||||
return new HitEvent(h.TimeOffset, result, placeholder_object, placeholder_object, null);
|
||||
return new HitEvent(h.TimeOffset, 1.0, result, placeholder_object, placeholder_object, null);
|
||||
});
|
||||
createTest(wide.Concat(narrow).ToList());
|
||||
}
|
||||
@ -114,7 +114,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
||||
[Test]
|
||||
public void TestZeroTimeOffset()
|
||||
{
|
||||
createTest(Enumerable.Range(0, 100).Select(_ => new HitEvent(0, HitResult.Perfect, placeholder_object, placeholder_object, null)).ToList());
|
||||
createTest(Enumerable.Range(0, 100).Select(_ => new HitEvent(0, 1.0, HitResult.Perfect, placeholder_object, placeholder_object, null)).ToList());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -129,9 +129,9 @@ namespace osu.Game.Tests.Visual.Ranking
|
||||
createTest(Enumerable.Range(0, 100).Select(i =>
|
||||
{
|
||||
if (i % 2 == 0)
|
||||
return new HitEvent(0, HitResult.Perfect, placeholder_object, placeholder_object, null);
|
||||
return new HitEvent(0, 1.0, HitResult.Perfect, placeholder_object, placeholder_object, null);
|
||||
|
||||
return new HitEvent(30, HitResult.Miss, placeholder_object, placeholder_object, null);
|
||||
return new HitEvent(30, 1.0, HitResult.Miss, placeholder_object, placeholder_object, null);
|
||||
}).ToList());
|
||||
}
|
||||
|
||||
@ -162,7 +162,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
||||
int count = (int)(Math.Pow(range - Math.Abs(i - range), 2)) / 10;
|
||||
|
||||
for (int j = 0; j < count; j++)
|
||||
hitEvents.Add(new HitEvent(centre + i - range, HitResult.Perfect, placeholder_object, placeholder_object, null));
|
||||
hitEvents.Add(new HitEvent(centre + i - range, 1.0, HitResult.Perfect, placeholder_object, placeholder_object, null));
|
||||
}
|
||||
|
||||
return hitEvents;
|
||||
|
@ -203,6 +203,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
||||
|
||||
public IBeatmap Beatmap { get; }
|
||||
|
||||
// ReSharper disable once NotNullOrRequiredMemberIsNotInitialized
|
||||
public TestBeatmapConverter(IBeatmap beatmap)
|
||||
{
|
||||
Beatmap = beatmap;
|
||||
|
@ -167,7 +167,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAddingFlow()
|
||||
public void TestAddingFlow([Values] bool withSystemModActive)
|
||||
{
|
||||
ModPresetColumn modPresetColumn = null!;
|
||||
|
||||
@ -181,7 +181,13 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
AddUntilStep("items loaded", () => modPresetColumn.IsLoaded && modPresetColumn.ItemsLoaded);
|
||||
AddAssert("add preset button disabled", () => !this.ChildrenOfType<AddPresetButton>().Single().Enabled.Value);
|
||||
|
||||
AddStep("set mods", () => SelectedMods.Value = new Mod[] { new OsuModDaycore(), new OsuModClassic() });
|
||||
AddStep("set mods", () =>
|
||||
{
|
||||
var newMods = new Mod[] { new OsuModDaycore(), new OsuModClassic() };
|
||||
if (withSystemModActive)
|
||||
newMods = newMods.Append(new OsuModTouchDevice()).ToArray();
|
||||
SelectedMods.Value = newMods;
|
||||
});
|
||||
AddAssert("add preset button enabled", () => this.ChildrenOfType<AddPresetButton>().Single().Enabled.Value);
|
||||
|
||||
AddStep("click add preset button", () =>
|
||||
@ -209,6 +215,9 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
});
|
||||
AddUntilStep("popover closed", () => !this.ChildrenOfType<OsuPopover>().Any());
|
||||
AddUntilStep("preset creation occurred", () => this.ChildrenOfType<ModPresetPanel>().Count() == 4);
|
||||
AddAssert("preset has correct mods",
|
||||
() => this.ChildrenOfType<ModPresetPanel>().Single(panel => panel.Preset.Value.Name == "new preset").Preset.Value.Mods,
|
||||
() => Has.Count.EqualTo(2));
|
||||
|
||||
AddStep("click add preset button", () =>
|
||||
{
|
||||
|
@ -86,6 +86,10 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
|
||||
AddStep("set mods to HD+HR+DT", () => SelectedMods.Value = new Mod[] { new OsuModHidden(), new OsuModHardRock(), new OsuModDoubleTime() });
|
||||
AddAssert("panel is not active", () => !panel.AsNonNull().Active.Value);
|
||||
|
||||
// system mods are not included in presets.
|
||||
AddStep("set mods to HR+DT+TD", () => SelectedMods.Value = new Mod[] { new OsuModHardRock(), new OsuModDoubleTime(), new OsuModTouchDevice() });
|
||||
AddAssert("panel is active", () => panel.AsNonNull().Active.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -113,6 +117,10 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
AddStep("set customised mod", () => SelectedMods.Value = new[] { new OsuModDoubleTime { SpeedChange = { Value = 1.25 } } });
|
||||
AddStep("activate panel", () => panel.AsNonNull().TriggerClick());
|
||||
assertSelectedModsEquivalentTo(new Mod[] { new OsuModHardRock(), new OsuModDoubleTime { SpeedChange = { Value = 1.5 } } });
|
||||
|
||||
AddStep("set system mod", () => SelectedMods.Value = new[] { new OsuModTouchDevice() });
|
||||
AddStep("activate panel", () => panel.AsNonNull().TriggerClick());
|
||||
assertSelectedModsEquivalentTo(new Mod[] { new OsuModTouchDevice(), new OsuModHardRock(), new OsuModDoubleTime { SpeedChange = { Value = 1.5 } } });
|
||||
}
|
||||
|
||||
private void assertSelectedModsEquivalentTo(IEnumerable<Mod> mods)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user