1
0
mirror of https://github.com/ppy/osu.git synced 2024-09-22 01:27:29 +08:00

Merge branch 'master' into fix-score-import-fail-fail-fail

This commit is contained in:
Bartłomiej Dach 2023-08-21 17:28:10 +02:00
commit 99c862f894
No known key found for this signature in database
6 changed files with 193 additions and 9 deletions

View File

@ -0,0 +1,110 @@
// 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.Utils;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.UI;
using osu.Game.Tests.Beatmaps;
using osuTK;
using osuTK.Input;
namespace osu.Game.Rulesets.Osu.Tests.Editor
{
public partial class TestSceneSliderReversal : TestSceneOsuEditor
{
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(Ruleset.Value, false);
private readonly PathControlPoint[][] paths =
{
createPathSegment(
PathType.PerfectCurve,
new Vector2(200, -50),
new Vector2(250, 0)
),
createPathSegment(
PathType.Linear,
new Vector2(100, 0),
new Vector2(100, 100)
)
};
private static PathControlPoint[] createPathSegment(PathType type, params Vector2[] positions)
{
return positions.Select(p => new PathControlPoint
{
Position = p
}).Prepend(new PathControlPoint
{
Type = type
}).ToArray();
}
private Slider selectedSlider => (Slider)EditorBeatmap.SelectedHitObjects[0];
[TestCase(0, 250)]
[TestCase(0, 200)]
[TestCase(1, 120)]
[TestCase(1, 80)]
public void TestSliderReversal(int pathIndex, double length)
{
var controlPoints = paths[pathIndex];
Vector2 oldStartPos = default;
Vector2 oldEndPos = default;
double oldDistance = default;
var oldControlPointTypes = controlPoints.Select(p => p.Type);
AddStep("Add slider", () =>
{
var slider = new Slider
{
Position = new Vector2(OsuPlayfield.BASE_SIZE.X / 2, OsuPlayfield.BASE_SIZE.Y / 2),
Path = new SliderPath(controlPoints)
{
ExpectedDistance = { Value = length }
}
};
EditorBeatmap.Add(slider);
oldStartPos = slider.Position;
oldEndPos = slider.EndPosition;
oldDistance = slider.Path.Distance;
});
AddStep("Select slider", () =>
{
var slider = (Slider)EditorBeatmap.HitObjects[0];
EditorBeatmap.SelectedHitObjects.Add(slider);
});
AddStep("Reverse slider", () =>
{
InputManager.PressKey(Key.LControl);
InputManager.Key(Key.G);
InputManager.ReleaseKey(Key.LControl);
});
AddAssert("Slider has correct length", () =>
Precision.AlmostEquals(selectedSlider.Path.Distance, oldDistance));
AddAssert("Slider has correct start position", () =>
Vector2.Distance(selectedSlider.Position, oldEndPos) < 1);
AddAssert("Slider has correct end position", () =>
Vector2.Distance(selectedSlider.EndPosition, oldStartPos) < 1);
AddAssert("Control points have correct types", () =>
{
var newControlPointTypes = selectedSlider.Path.ControlPoints.Select(p => p.Type).ToArray();
return oldControlPointTypes.Take(newControlPointTypes.Length).SequenceEqual(newControlPointTypes);
});
}
}
}

View File

@ -44,7 +44,7 @@ namespace osu.Game.Tests.Visual.Menus
foreach (var fountain in Children.OfType<StarFountain>())
{
if (RNG.NextSingle() > 0.8f)
fountain.Shoot();
fountain.Shoot(RNG.Next(-1, 2));
}
}, 150);
}

View File

@ -40,8 +40,14 @@ namespace osu.Game.Graphics.UserInterface
AddInternal(hoverClickSounds = new HoverClickSounds());
updateTextColour();
}
protected override void LoadComplete()
{
base.LoadComplete();
Item.Action.BindDisabledChanged(_ => updateState(), true);
FinishTransforms();
}
private void updateTextColour()

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Linq;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects.Types;
@ -25,6 +26,53 @@ namespace osu.Game.Rulesets.Objects
/// <param name="sliderPath">The <see cref="SliderPath"/>.</param>
/// <param name="positionalOffset">The positional offset of the resulting path. It should be added to the start position of this path.</param>
public static void Reverse(this SliderPath sliderPath, out Vector2 positionalOffset)
{
var controlPoints = sliderPath.ControlPoints;
var inheritedLinearPoints = controlPoints.Where(p => sliderPath.PointsInSegment(p)[0].Type == PathType.Linear && p.Type is null).ToList();
// Inherited points after a linear point, as well as the first control point if it inherited,
// should be treated as linear points, so their types are temporarily changed to linear.
inheritedLinearPoints.ForEach(p => p.Type = PathType.Linear);
double[] segmentEnds = sliderPath.GetSegmentEnds().ToArray();
// Remove segments after the end of the slider.
for (int numSegmentsToRemove = segmentEnds.Count(se => se >= 1) - 1; numSegmentsToRemove > 0 && controlPoints.Count > 0;)
{
if (controlPoints.Last().Type is not null)
{
numSegmentsToRemove--;
segmentEnds = segmentEnds[..^1];
}
controlPoints.RemoveAt(controlPoints.Count - 1);
}
// Restore original control point types.
inheritedLinearPoints.ForEach(p => p.Type = null);
// Recalculate middle perfect curve control points at the end of the slider path.
if (controlPoints.Count >= 3 && controlPoints[^3].Type == PathType.PerfectCurve && controlPoints[^2].Type is null && segmentEnds.Any())
{
double lastSegmentStart = segmentEnds.Length > 1 ? segmentEnds[^2] : 0;
double lastSegmentEnd = segmentEnds[^1];
var circleArcPath = new List<Vector2>();
sliderPath.GetPathToProgress(circleArcPath, lastSegmentStart / lastSegmentEnd, 1);
controlPoints[^2].Position = circleArcPath[circleArcPath.Count / 2];
}
sliderPath.reverseControlPoints(out positionalOffset);
}
/// <summary>
/// Reverses the order of the provided <see cref="SliderPath"/>'s <see cref="PathControlPoint"/>s.
/// </summary>
/// <param name="sliderPath">The <see cref="SliderPath"/>.</param>
/// <param name="positionalOffset">The positional offset of the resulting path. It should be added to the start position of this path.</param>
private static void reverseControlPoints(this SliderPath sliderPath, out Vector2 positionalOffset)
{
var points = sliderPath.ControlPoints.ToArray();
positionalOffset = sliderPath.PositionAt(1);

View File

@ -2,10 +2,10 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics;
using osu.Framework.Utils;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics.Containers;
@ -13,6 +13,9 @@ namespace osu.Game.Screens.Menu
{
public partial class KiaiMenuFountains : BeatSyncedContainer
{
private StarFountain leftFountain = null!;
private StarFountain rightFountain = null!;
[BackgroundDependencyLoader]
private void load()
{
@ -20,13 +23,13 @@ namespace osu.Game.Screens.Menu
Children = new[]
{
new StarFountain
leftFountain = new StarFountain
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
X = 250,
},
new StarFountain
rightFountain = new StarFountain
{
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
@ -58,8 +61,25 @@ namespace osu.Game.Screens.Menu
if (lastTrigger != null && Clock.CurrentTime - lastTrigger < 500)
return;
foreach (var fountain in Children.OfType<StarFountain>())
fountain.Shoot();
int direction = RNG.Next(-1, 2);
switch (direction)
{
case -1:
leftFountain.Shoot(1);
rightFountain.Shoot(-1);
break;
case 0:
leftFountain.Shoot(0);
rightFountain.Shoot(0);
break;
case 1:
leftFountain.Shoot(-1);
rightFountain.Shoot(1);
break;
}
lastTrigger = Clock.CurrentTime;
}

View File

@ -23,7 +23,7 @@ namespace osu.Game.Screens.Menu
InternalChild = spewer = new StarFountainSpewer();
}
public void Shoot() => spewer.Shoot();
public void Shoot(int direction) => spewer.Shoot(direction);
protected override void SkinChanged(ISkinSource skin)
{
@ -81,10 +81,10 @@ namespace osu.Game.Screens.Menu
return lastShootDirection * x_velocity_from_direction * (float)(1 - 2 * (Clock.CurrentTime - lastShootTime!.Value) / shoot_duration) + getRandomVariance(x_velocity_random_variance);
}
public void Shoot()
public void Shoot(int direction)
{
lastShootTime = Clock.CurrentTime;
lastShootDirection = RNG.Next(-1, 2);
lastShootDirection = direction;
}
private static float getRandomVariance(float variance) => RNG.NextSingle(-variance, variance);