mirror of
https://github.com/ppy/osu.git
synced 2024-12-16 06:23:20 +08:00
Merge branch 'master' of https://github.com/ppy/osu into carousel-perform-selection
This commit is contained in:
commit
f8339f05ee
@ -51,7 +51,7 @@
|
|||||||
<Reference Include="Java.Interop" />
|
<Reference Include="Java.Interop" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.822.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.827.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.819.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.819.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Transitive Dependencies">
|
<ItemGroup Label="Transitive Dependencies">
|
||||||
|
@ -210,9 +210,9 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor
|
|||||||
new Vector2(50, 200),
|
new Vector2(50, 200),
|
||||||
}), 0.5);
|
}), 0.5);
|
||||||
AddAssert("1 vertex per 1 nested HO", () => getVertices().Count == hitObject.NestedHitObjects.Count);
|
AddAssert("1 vertex per 1 nested HO", () => getVertices().Count == hitObject.NestedHitObjects.Count);
|
||||||
AddAssert("slider path not yet changed", () => hitObject.Path.ControlPoints[0].Type.Value == PathType.PerfectCurve);
|
AddAssert("slider path not yet changed", () => hitObject.Path.ControlPoints[0].Type == PathType.PerfectCurve);
|
||||||
addAddVertexSteps(150, 150);
|
addAddVertexSteps(150, 150);
|
||||||
AddAssert("slider path change to linear", () => hitObject.Path.ControlPoints[0].Type.Value == 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", () =>
|
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);
|
} while (rng.Next(2) != 0);
|
||||||
|
|
||||||
int length = sliderPath.ControlPoints.Count - start + 1;
|
int length = sliderPath.ControlPoints.Count - start + 1;
|
||||||
sliderPath.ControlPoints[start].Type.Value = length <= 2 ? PathType.Linear : length == 3 ? PathType.PerfectCurve : PathType.Bezier;
|
sliderPath.ControlPoints[start].Type = length <= 2 ? PathType.Linear : length == 3 ? PathType.PerfectCurve : PathType.Bezier;
|
||||||
} while (rng.Next(3) != 0);
|
} while (rng.Next(3) != 0);
|
||||||
|
|
||||||
if (rng.Next(5) == 0)
|
if (rng.Next(5) == 0)
|
||||||
@ -210,13 +210,13 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
|
|
||||||
path.ConvertToSliderPath(sliderPath, sliderStartY);
|
path.ConvertToSliderPath(sliderPath, sliderStartY);
|
||||||
Assert.That(sliderPath.Distance, Is.EqualTo(path.Distance).Within(1e-3));
|
Assert.That(sliderPath.Distance, Is.EqualTo(path.Distance).Within(1e-3));
|
||||||
Assert.That(sliderPath.ControlPoints[0].Position.Value.X, Is.EqualTo(path.Vertices[0].X));
|
Assert.That(sliderPath.ControlPoints[0].Position.X, Is.EqualTo(path.Vertices[0].X));
|
||||||
assertInvariants(path.Vertices, true);
|
assertInvariants(path.Vertices, true);
|
||||||
|
|
||||||
foreach (var point in sliderPath.ControlPoints)
|
foreach (var point in sliderPath.ControlPoints)
|
||||||
{
|
{
|
||||||
Assert.That(point.Type.Value, Is.EqualTo(PathType.Linear).Or.Null);
|
Assert.That(point.Type, Is.EqualTo(PathType.Linear).Or.Null);
|
||||||
Assert.That(sliderStartY + point.Position.Value.Y, Is.InRange(0, JuiceStreamPath.OSU_PLAYFIELD_HEIGHT));
|
Assert.That(sliderStartY + point.Position.Y, Is.InRange(0, JuiceStreamPath.OSU_PLAYFIELD_HEIGHT));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < 10; i++)
|
for (int i = 0; i < 10; i++)
|
||||||
|
@ -75,7 +75,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
|||||||
|
|
||||||
case JuiceStream juiceStream:
|
case JuiceStream juiceStream:
|
||||||
// Todo: BUG!! Stable used the last control point as the final position of the path, but it should use the computed path instead.
|
// Todo: BUG!! Stable used the last control point as the final position of the path, but it should use the computed path instead.
|
||||||
lastPosition = juiceStream.OriginalX + juiceStream.Path.ControlPoints[^1].Position.Value.X;
|
lastPosition = juiceStream.OriginalX + juiceStream.Path.ControlPoints[^1].Position.X;
|
||||||
|
|
||||||
// Todo: BUG!! Stable attempted to use the end time of the stream, but referenced it too early in execution and used the start time instead.
|
// Todo: BUG!! Stable attempted to use the end time of the stream, but referenced it too early in execution and used the start time instead.
|
||||||
lastStartTime = juiceStream.StartTime;
|
lastStartTime = juiceStream.StartTime;
|
||||||
|
@ -76,7 +76,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components
|
|||||||
path.ConvertFromSliderPath(sliderPath);
|
path.ConvertFromSliderPath(sliderPath);
|
||||||
|
|
||||||
// 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 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.Value != null && p.Type.Value != PathType.Linear))
|
if (sliderPath.ControlPoints.Any(p => p.Type != null && p.Type != PathType.Linear))
|
||||||
{
|
{
|
||||||
path.ResampleVertices(hitObject.NestedHitObjects
|
path.ResampleVertices(hitObject.NestedHitObjects
|
||||||
.Skip(1).TakeWhile(h => !(h is Fruit)) // Only droplets in the first span are used.
|
.Skip(1).TakeWhile(h => !(h is Fruit)) // Only droplets in the first span are used.
|
||||||
|
@ -127,7 +127,7 @@ namespace osu.Game.Rulesets.Catch.Edit
|
|||||||
juiceStream.OriginalX = selectionRange.GetFlippedPosition(juiceStream.OriginalX);
|
juiceStream.OriginalX = selectionRange.GetFlippedPosition(juiceStream.OriginalX);
|
||||||
|
|
||||||
foreach (var point in juiceStream.Path.ControlPoints)
|
foreach (var point in juiceStream.Path.ControlPoints)
|
||||||
point.Position.Value *= new Vector2(-1, 1);
|
point.Position *= new Vector2(-1, 1);
|
||||||
|
|
||||||
EditorBeatmap.Update(juiceStream);
|
EditorBeatmap.Update(juiceStream);
|
||||||
return true;
|
return true;
|
||||||
|
@ -68,9 +68,9 @@ namespace osu.Game.Rulesets.Catch.Mods
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private static void mirrorJuiceStreamPath(JuiceStream juiceStream)
|
private static void mirrorJuiceStreamPath(JuiceStream juiceStream)
|
||||||
{
|
{
|
||||||
var controlPoints = juiceStream.Path.ControlPoints.Select(p => new PathControlPoint(p.Position.Value, p.Type.Value)).ToArray();
|
var controlPoints = juiceStream.Path.ControlPoints.Select(p => new PathControlPoint(p.Position, p.Type)).ToArray();
|
||||||
foreach (var point in controlPoints)
|
foreach (var point in controlPoints)
|
||||||
point.Position.Value = new Vector2(-point.Position.Value.X, point.Position.Value.Y);
|
point.Position = new Vector2(-point.Position.X, point.Position.Y);
|
||||||
|
|
||||||
juiceStream.Path = new SliderPath(controlPoints, juiceStream.Path.ExpectedDistance.Value);
|
juiceStream.Path = new SliderPath(controlPoints, juiceStream.Path.ExpectedDistance.Value);
|
||||||
}
|
}
|
||||||
|
@ -138,7 +138,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
|
|
||||||
if (value != null)
|
if (value != null)
|
||||||
{
|
{
|
||||||
path.ControlPoints.AddRange(value.ControlPoints.Select(c => new PathControlPoint(c.Position.Value, c.Type.Value)));
|
path.ControlPoints.AddRange(value.ControlPoints.Select(c => new PathControlPoint(c.Position, c.Type)));
|
||||||
path.ExpectedDistance.Value = value.ExpectedDistance.Value;
|
path.ExpectedDistance.Value = value.ExpectedDistance.Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -234,7 +234,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
|
|
||||||
for (int i = 1; i < vertices.Count; i++)
|
for (int i = 1; i < vertices.Count; i++)
|
||||||
{
|
{
|
||||||
sliderPath.ControlPoints[^1].Type.Value = PathType.Linear;
|
sliderPath.ControlPoints[^1].Type = PathType.Linear;
|
||||||
|
|
||||||
float deltaX = vertices[i].X - lastPosition.X;
|
float deltaX = vertices[i].X - lastPosition.X;
|
||||||
double length = vertices[i].Distance - currentDistance;
|
double length = vertices[i].Distance - currentDistance;
|
||||||
|
@ -0,0 +1,64 @@
|
|||||||
|
// 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.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Rulesets.Edit;
|
||||||
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
|
using osu.Game.Screens.Edit;
|
||||||
|
using osu.Game.Screens.Edit.Compose;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Tests.Editor
|
||||||
|
{
|
||||||
|
public class TestSceneManiaComposeScreen : EditorClockTestScene
|
||||||
|
{
|
||||||
|
[Resolved]
|
||||||
|
private SkinManager skins { get; set; }
|
||||||
|
|
||||||
|
[SetUpSteps]
|
||||||
|
public void SetUpSteps()
|
||||||
|
{
|
||||||
|
AddStep("setup compose screen", () =>
|
||||||
|
{
|
||||||
|
var editorBeatmap = new EditorBeatmap(new ManiaBeatmap(new StageDefinition { Columns = 4 }))
|
||||||
|
{
|
||||||
|
BeatmapInfo = { Ruleset = new ManiaRuleset().RulesetInfo },
|
||||||
|
};
|
||||||
|
|
||||||
|
Beatmap.Value = CreateWorkingBeatmap(editorBeatmap.PlayableBeatmap);
|
||||||
|
|
||||||
|
Child = new DependencyProvidingContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
CachedDependencies = new (Type, object)[]
|
||||||
|
{
|
||||||
|
(typeof(EditorBeatmap), editorBeatmap),
|
||||||
|
(typeof(IBeatSnapProvider), editorBeatmap),
|
||||||
|
},
|
||||||
|
Child = new ComposeScreen { State = { Value = Visibility.Visible } },
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("wait for composer", () => this.ChildrenOfType<HitObjectComposer>().SingleOrDefault()?.IsLoaded == true);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDefaultSkin()
|
||||||
|
{
|
||||||
|
AddStep("set default skin", () => skins.CurrentSkinInfo.Value = SkinInfo.Default);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestLegacySkin()
|
||||||
|
{
|
||||||
|
AddStep("set legacy skin", () => skins.CurrentSkinInfo.Value = DefaultLegacySkin.Info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
|
|
||||||
AddAssert("last connection displayed", () =>
|
AddAssert("last connection displayed", () =>
|
||||||
{
|
{
|
||||||
var lastConnection = visualiser.Connections.Last(c => c.ControlPoint.Position.Value == new Vector2(300));
|
var lastConnection = visualiser.Connections.Last(c => c.ControlPoint.Position == new Vector2(300));
|
||||||
return lastConnection.DrawWidth > 50;
|
return lastConnection.DrawWidth > 50;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -166,14 +166,14 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
{
|
{
|
||||||
AddStep($"move mouse to control point {index}", () =>
|
AddStep($"move mouse to control point {index}", () =>
|
||||||
{
|
{
|
||||||
Vector2 position = slider.Path.ControlPoints[index].Position.Value;
|
Vector2 position = slider.Path.ControlPoints[index].Position;
|
||||||
InputManager.MoveMouseTo(visualiser.Pieces[0].Parent.ToScreenSpace(position));
|
InputManager.MoveMouseTo(visualiser.Pieces[0].Parent.ToScreenSpace(position));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertControlPointPathType(int controlPointIndex, PathType? type)
|
private void assertControlPointPathType(int controlPointIndex, PathType? type)
|
||||||
{
|
{
|
||||||
AddAssert($"point {controlPointIndex} is {type}", () => slider.Path.ControlPoints[controlPointIndex].Type.Value == type);
|
AddAssert($"point {controlPointIndex} is {type}", () => slider.Path.ControlPoints[controlPointIndex].Type == type);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addContextMenuItemStep(string contextMenuText)
|
private void addContextMenuItemStep(string contextMenuText)
|
||||||
|
@ -108,9 +108,9 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestDragControlPointPathAfterChangingType()
|
public void TestDragControlPointPathAfterChangingType()
|
||||||
{
|
{
|
||||||
AddStep("change type to bezier", () => slider.Path.ControlPoints[2].Type.Value = 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("add point", () => slider.Path.ControlPoints.Add(new PathControlPoint(new Vector2(500, 10))));
|
||||||
AddStep("change type to perfect", () => slider.Path.ControlPoints[3].Type.Value = PathType.PerfectCurve);
|
AddStep("change type to perfect", () => slider.Path.ControlPoints[3].Type = PathType.PerfectCurve);
|
||||||
|
|
||||||
moveMouseToControlPoint(4);
|
moveMouseToControlPoint(4);
|
||||||
AddStep("hold", () => InputManager.PressButton(MouseButton.Left));
|
AddStep("hold", () => InputManager.PressButton(MouseButton.Left));
|
||||||
@ -137,15 +137,15 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
{
|
{
|
||||||
AddStep($"move mouse to control point {index}", () =>
|
AddStep($"move mouse to control point {index}", () =>
|
||||||
{
|
{
|
||||||
Vector2 position = slider.Position + slider.Path.ControlPoints[index].Position.Value;
|
Vector2 position = slider.Position + slider.Path.ControlPoints[index].Position;
|
||||||
InputManager.MoveMouseTo(drawableObject.Parent.ToScreenSpace(position));
|
InputManager.MoveMouseTo(drawableObject.Parent.ToScreenSpace(position));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertControlPointType(int index, PathType type) => AddAssert($"control point {index} is {type}", () => slider.Path.ControlPoints[index].Type.Value == type);
|
private void assertControlPointType(int index, PathType type) => AddAssert($"control point {index} is {type}", () => slider.Path.ControlPoints[index].Type == type);
|
||||||
|
|
||||||
private void assertControlPointPosition(int index, Vector2 position) =>
|
private void assertControlPointPosition(int index, Vector2 position) =>
|
||||||
AddAssert($"control point {index} at {position}", () => Precision.AlmostEquals(position, slider.Path.ControlPoints[index].Position.Value, 1));
|
AddAssert($"control point {index} at {position}", () => Precision.AlmostEquals(position, slider.Path.ControlPoints[index].Position, 1));
|
||||||
|
|
||||||
private class TestSliderBlueprint : SliderSelectionBlueprint
|
private class TestSliderBlueprint : SliderSelectionBlueprint
|
||||||
{
|
{
|
||||||
|
@ -385,10 +385,10 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
|
|
||||||
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 == expected);
|
||||||
|
|
||||||
private void assertControlPointType(int index, PathType type) => AddAssert($"control point {index} is {type}", () => getSlider().Path.ControlPoints[index].Type.Value == type);
|
private void assertControlPointType(int index, PathType type) => AddAssert($"control point {index} is {type}", () => getSlider().Path.ControlPoints[index].Type == type);
|
||||||
|
|
||||||
private void assertControlPointPosition(int index, Vector2 position) =>
|
private void assertControlPointPosition(int index, Vector2 position) =>
|
||||||
AddAssert($"control point {index} at {position}", () => Precision.AlmostEquals(position, getSlider().Path.ControlPoints[index].Position.Value, 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;
|
||||||
|
|
||||||
|
@ -184,7 +184,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
{
|
{
|
||||||
AddStep($"move mouse to control point {index}", () =>
|
AddStep($"move mouse to control point {index}", () =>
|
||||||
{
|
{
|
||||||
Vector2 position = slider.Position + slider.Path.ControlPoints[index].Position.Value;
|
Vector2 position = slider.Position + slider.Path.ControlPoints[index].Position;
|
||||||
InputManager.MoveMouseTo(drawableObject.Parent.ToScreenSpace(position));
|
InputManager.MoveMouseTo(drawableObject.Parent.ToScreenSpace(position));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void updateConnectingPath()
|
private void updateConnectingPath()
|
||||||
{
|
{
|
||||||
Position = slider.StackedPosition + ControlPoint.Position.Value;
|
Position = slider.StackedPosition + ControlPoint.Position;
|
||||||
|
|
||||||
path.ClearVertices();
|
path.ClearVertices();
|
||||||
|
|
||||||
@ -69,7 +69,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
path.AddVertex(Vector2.Zero);
|
path.AddVertex(Vector2.Zero);
|
||||||
path.AddVertex(slider.Path.ControlPoints[nextIndex].Position.Value - ControlPoint.Position.Value);
|
path.AddVertex(slider.Path.ControlPoints[nextIndex].Position - ControlPoint.Position);
|
||||||
|
|
||||||
path.OriginPosition = path.PositionInBoundingBox(Vector2.Zero);
|
path.OriginPosition = path.PositionInBoundingBox(Vector2.Zero);
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
|
|
||||||
private IBindable<Vector2> sliderPosition;
|
private IBindable<Vector2> sliderPosition;
|
||||||
private IBindable<float> sliderScale;
|
private IBindable<float> sliderScale;
|
||||||
private IBindable<Vector2> controlPointPosition;
|
|
||||||
|
|
||||||
public PathControlPointPiece(Slider slider, PathControlPoint controlPoint)
|
public PathControlPointPiece(Slider slider, PathControlPoint controlPoint)
|
||||||
{
|
{
|
||||||
@ -69,7 +68,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
updatePathType();
|
updatePathType();
|
||||||
});
|
});
|
||||||
|
|
||||||
controlPoint.Type.BindValueChanged(_ => updateMarkerDisplay());
|
controlPoint.Changed += updateMarkerDisplay;
|
||||||
|
|
||||||
Origin = Anchor.Centre;
|
Origin = Anchor.Centre;
|
||||||
AutoSizeAxes = Axes.Both;
|
AutoSizeAxes = Axes.Both;
|
||||||
@ -117,9 +116,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
sliderPosition = slider.PositionBindable.GetBoundCopy();
|
sliderPosition = slider.PositionBindable.GetBoundCopy();
|
||||||
sliderPosition.BindValueChanged(_ => updateMarkerDisplay());
|
sliderPosition.BindValueChanged(_ => updateMarkerDisplay());
|
||||||
|
|
||||||
controlPointPosition = ControlPoint.Position.GetBoundCopy();
|
|
||||||
controlPointPosition.BindValueChanged(_ => updateMarkerDisplay());
|
|
||||||
|
|
||||||
sliderScale = slider.ScaleBindable.GetBoundCopy();
|
sliderScale = slider.ScaleBindable.GetBoundCopy();
|
||||||
sliderScale.BindValueChanged(_ => updateMarkerDisplay());
|
sliderScale.BindValueChanged(_ => updateMarkerDisplay());
|
||||||
|
|
||||||
@ -174,8 +170,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
|
|
||||||
if (e.Button == MouseButton.Left)
|
if (e.Button == MouseButton.Left)
|
||||||
{
|
{
|
||||||
dragStartPosition = ControlPoint.Position.Value;
|
dragStartPosition = ControlPoint.Position;
|
||||||
dragPathType = PointsInSegment[0].Type.Value;
|
dragPathType = PointsInSegment[0].Type;
|
||||||
|
|
||||||
changeHandler?.BeginChange();
|
changeHandler?.BeginChange();
|
||||||
return true;
|
return true;
|
||||||
@ -186,7 +182,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
|
|
||||||
protected override void OnDrag(DragEvent e)
|
protected override void OnDrag(DragEvent e)
|
||||||
{
|
{
|
||||||
Vector2[] oldControlPoints = slider.Path.ControlPoints.Select(cp => cp.Position.Value).ToArray();
|
Vector2[] oldControlPoints = slider.Path.ControlPoints.Select(cp => cp.Position).ToArray();
|
||||||
var oldPosition = slider.Position;
|
var oldPosition = slider.Position;
|
||||||
var oldStartTime = slider.StartTime;
|
var oldStartTime = slider.StartTime;
|
||||||
|
|
||||||
@ -202,15 +198,15 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
|
|
||||||
// Since control points are relative to the position of the slider, they all need to be offset backwards by the delta
|
// Since control points are relative to the position of the slider, they all need to be offset backwards by the delta
|
||||||
for (int i = 1; i < slider.Path.ControlPoints.Count; i++)
|
for (int i = 1; i < slider.Path.ControlPoints.Count; i++)
|
||||||
slider.Path.ControlPoints[i].Position.Value -= movementDelta;
|
slider.Path.ControlPoints[i].Position -= movementDelta;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
ControlPoint.Position.Value = dragStartPosition + (e.MousePosition - e.MouseDownPosition);
|
ControlPoint.Position = dragStartPosition + (e.MousePosition - e.MouseDownPosition);
|
||||||
|
|
||||||
if (!slider.Path.HasValidLength)
|
if (!slider.Path.HasValidLength)
|
||||||
{
|
{
|
||||||
for (var i = 0; i < slider.Path.ControlPoints.Count; i++)
|
for (var i = 0; i < slider.Path.ControlPoints.Count; i++)
|
||||||
slider.Path.ControlPoints[i].Position.Value = oldControlPoints[i];
|
slider.Path.ControlPoints[i].Position = oldControlPoints[i];
|
||||||
|
|
||||||
slider.Position = oldPosition;
|
slider.Position = oldPosition;
|
||||||
slider.StartTime = oldStartTime;
|
slider.StartTime = oldStartTime;
|
||||||
@ -218,7 +214,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Maintain the path type in case it got defaulted to bezier at some point during the drag.
|
// Maintain the path type in case it got defaulted to bezier at some point during the drag.
|
||||||
PointsInSegment[0].Type.Value = dragPathType;
|
PointsInSegment[0].Type = dragPathType;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDragEnd(DragEndEvent e) => changeHandler?.EndChange();
|
protected override void OnDragEnd(DragEndEvent e) => changeHandler?.EndChange();
|
||||||
@ -230,19 +226,19 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void updatePathType()
|
private void updatePathType()
|
||||||
{
|
{
|
||||||
if (ControlPoint.Type.Value != PathType.PerfectCurve)
|
if (ControlPoint.Type != PathType.PerfectCurve)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (PointsInSegment.Count > 3)
|
if (PointsInSegment.Count > 3)
|
||||||
ControlPoint.Type.Value = PathType.Bezier;
|
ControlPoint.Type = PathType.Bezier;
|
||||||
|
|
||||||
if (PointsInSegment.Count != 3)
|
if (PointsInSegment.Count != 3)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ReadOnlySpan<Vector2> points = PointsInSegment.Select(p => p.Position.Value).ToArray();
|
ReadOnlySpan<Vector2> points = PointsInSegment.Select(p => p.Position).ToArray();
|
||||||
RectangleF boundingBox = PathApproximator.CircularArcBoundingBox(points);
|
RectangleF boundingBox = PathApproximator.CircularArcBoundingBox(points);
|
||||||
if (boundingBox.Width >= 640 || boundingBox.Height >= 480)
|
if (boundingBox.Width >= 640 || boundingBox.Height >= 480)
|
||||||
ControlPoint.Type.Value = PathType.Bezier;
|
ControlPoint.Type = PathType.Bezier;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -250,7 +246,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void updateMarkerDisplay()
|
private void updateMarkerDisplay()
|
||||||
{
|
{
|
||||||
Position = slider.StackedPosition + ControlPoint.Position.Value;
|
Position = slider.StackedPosition + ControlPoint.Position;
|
||||||
|
|
||||||
markerRing.Alpha = IsSelected.Value ? 1 : 0;
|
markerRing.Alpha = IsSelected.Value ? 1 : 0;
|
||||||
|
|
||||||
@ -265,7 +261,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
|
|
||||||
private Color4 getColourFromNodeType()
|
private Color4 getColourFromNodeType()
|
||||||
{
|
{
|
||||||
if (!(ControlPoint.Type.Value is PathType pathType))
|
if (!(ControlPoint.Type is PathType pathType))
|
||||||
return colours.Yellow;
|
return colours.Yellow;
|
||||||
|
|
||||||
switch (pathType)
|
switch (pathType)
|
||||||
@ -284,6 +280,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public LocalisableString TooltipText => ControlPoint.Type.Value.ToString() ?? string.Empty;
|
public LocalisableString TooltipText => ControlPoint.Type.ToString() ?? string.Empty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -173,12 +173,12 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
int thirdPointIndex = indexInSegment + 2;
|
int thirdPointIndex = indexInSegment + 2;
|
||||||
|
|
||||||
if (piece.PointsInSegment.Count > thirdPointIndex + 1)
|
if (piece.PointsInSegment.Count > thirdPointIndex + 1)
|
||||||
piece.PointsInSegment[thirdPointIndex].Type.Value = piece.PointsInSegment[0].Type.Value;
|
piece.PointsInSegment[thirdPointIndex].Type = piece.PointsInSegment[0].Type;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
piece.ControlPoint.Type.Value = type;
|
piece.ControlPoint.Type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Resolved(CanBeNull = true)]
|
[Resolved(CanBeNull = true)]
|
||||||
@ -241,7 +241,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
private MenuItem createMenuItemForPathType(PathType? type)
|
private MenuItem createMenuItemForPathType(PathType? type)
|
||||||
{
|
{
|
||||||
int totalCount = Pieces.Count(p => p.IsSelected.Value);
|
int totalCount = Pieces.Count(p => p.IsSelected.Value);
|
||||||
int countOfState = Pieces.Where(p => p.IsSelected.Value).Count(p => p.ControlPoint.Type.Value == type);
|
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 == null ? "Inherit" : type.ToString().Humanize(), MenuItemType.Standard, _ =>
|
||||||
{
|
{
|
||||||
|
@ -108,7 +108,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
Debug.Assert(lastPoint != null);
|
Debug.Assert(lastPoint != null);
|
||||||
|
|
||||||
segmentStart = lastPoint;
|
segmentStart = lastPoint;
|
||||||
segmentStart.Type.Value = PathType.Linear;
|
segmentStart.Type = PathType.Linear;
|
||||||
|
|
||||||
currentSegmentLength = 1;
|
currentSegmentLength = 1;
|
||||||
}
|
}
|
||||||
@ -153,15 +153,15 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
{
|
{
|
||||||
case 1:
|
case 1:
|
||||||
case 2:
|
case 2:
|
||||||
segmentStart.Type.Value = PathType.Linear;
|
segmentStart.Type = PathType.Linear;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 3:
|
case 3:
|
||||||
segmentStart.Type.Value = PathType.PerfectCurve;
|
segmentStart.Type = PathType.PerfectCurve;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
segmentStart.Type.Value = PathType.Bezier;
|
segmentStart.Type = PathType.Bezier;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -173,7 +173,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
// The cursor does not overlap a previous control point, so it can be added if not already existing.
|
// The cursor does not overlap a previous control point, so it can be added if not already existing.
|
||||||
if (cursor == null)
|
if (cursor == null)
|
||||||
{
|
{
|
||||||
HitObject.Path.ControlPoints.Add(cursor = new PathControlPoint { Position = { Value = Vector2.Zero } });
|
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++;
|
currentSegmentLength++;
|
||||||
@ -181,7 +181,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update the cursor position.
|
// Update the cursor position.
|
||||||
cursor.Position.Value = ToLocalSpace(inputManager.CurrentState.Mouse.Position) - HitObject.Position;
|
cursor.Position = ToLocalSpace(inputManager.CurrentState.Mouse.Position) - HitObject.Position;
|
||||||
}
|
}
|
||||||
else if (cursor != null)
|
else if (cursor != null)
|
||||||
{
|
{
|
||||||
|
@ -161,7 +161,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
{
|
{
|
||||||
Debug.Assert(placementControlPointIndex != null);
|
Debug.Assert(placementControlPointIndex != null);
|
||||||
|
|
||||||
HitObject.Path.ControlPoints[placementControlPointIndex.Value].Position.Value = e.MousePosition - HitObject.Position;
|
HitObject.Path.ControlPoints[placementControlPointIndex.Value].Position = e.MousePosition - HitObject.Position;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDragEnd(DragEndEvent e)
|
protected override void OnDragEnd(DragEndEvent e)
|
||||||
@ -182,7 +182,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
|
|
||||||
for (int i = 0; i < controlPoints.Count - 1; i++)
|
for (int i = 0; i < controlPoints.Count - 1; i++)
|
||||||
{
|
{
|
||||||
float dist = new Line(controlPoints[i].Position.Value, controlPoints[i + 1].Position.Value).DistanceToPoint(position);
|
float dist = new Line(controlPoints[i].Position, controlPoints[i + 1].Position).DistanceToPoint(position);
|
||||||
|
|
||||||
if (dist < minDistance)
|
if (dist < minDistance)
|
||||||
{
|
{
|
||||||
@ -192,7 +192,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Move the control points from the insertion index onwards to make room for the insertion
|
// Move the control points from the insertion index onwards to make room for the insertion
|
||||||
controlPoints.Insert(insertionIndex, new PathControlPoint { Position = { Value = position } });
|
controlPoints.Insert(insertionIndex, new PathControlPoint { Position = position });
|
||||||
|
|
||||||
return insertionIndex;
|
return insertionIndex;
|
||||||
}
|
}
|
||||||
@ -207,8 +207,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
{
|
{
|
||||||
// The first control point in the slider must have a type, so take it from the previous "first" one
|
// The first control point in the slider must have a type, so take it from the previous "first" one
|
||||||
// Todo: Should be handled within SliderPath itself
|
// Todo: Should be handled within SliderPath itself
|
||||||
if (c == controlPoints[0] && controlPoints.Count > 1 && controlPoints[1].Type.Value == null)
|
if (c == controlPoints[0] && controlPoints.Count > 1 && controlPoints[1].Type == null)
|
||||||
controlPoints[1].Type.Value = controlPoints[0].Type.Value;
|
controlPoints[1].Type = controlPoints[0].Type;
|
||||||
|
|
||||||
controlPoints.Remove(c);
|
controlPoints.Remove(c);
|
||||||
}
|
}
|
||||||
@ -222,9 +222,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
|
|
||||||
// The path will have a non-zero offset if the head is removed, but sliders don't support this behaviour since the head is positioned at the slider's position
|
// The path will have a non-zero offset if the head is removed, but sliders don't support this behaviour since the head is positioned at the slider's position
|
||||||
// So the slider needs to be offset by this amount instead, and all control points offset backwards such that the path is re-positioned at (0, 0)
|
// So the slider needs to be offset by this amount instead, and all control points offset backwards such that the path is re-positioned at (0, 0)
|
||||||
Vector2 first = controlPoints[0].Position.Value;
|
Vector2 first = controlPoints[0].Position;
|
||||||
foreach (var c in controlPoints)
|
foreach (var c in controlPoints)
|
||||||
c.Position.Value -= first;
|
c.Position -= first;
|
||||||
HitObject.Position += first;
|
HitObject.Position += first;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,9 +98,9 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
{
|
{
|
||||||
foreach (var point in slider.Path.ControlPoints)
|
foreach (var point in slider.Path.ControlPoints)
|
||||||
{
|
{
|
||||||
point.Position.Value = new Vector2(
|
point.Position = new Vector2(
|
||||||
(direction == Direction.Horizontal ? -1 : 1) * point.Position.Value.X,
|
(direction == Direction.Horizontal ? -1 : 1) * point.Position.X,
|
||||||
(direction == Direction.Vertical ? -1 : 1) * point.Position.Value.Y
|
(direction == Direction.Vertical ? -1 : 1) * point.Position.Y
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -153,7 +153,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
if (h is IHasPath path)
|
if (h is IHasPath path)
|
||||||
{
|
{
|
||||||
foreach (var point in path.Path.ControlPoints)
|
foreach (var point in path.Path.ControlPoints)
|
||||||
point.Position.Value = RotatePointAroundOrigin(point.Position.Value, Vector2.Zero, delta);
|
point.Position = RotatePointAroundOrigin(point.Position, Vector2.Zero, delta);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,9 +163,9 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
|
|
||||||
private void scaleSlider(Slider slider, Vector2 scale)
|
private void scaleSlider(Slider slider, Vector2 scale)
|
||||||
{
|
{
|
||||||
referencePathTypes ??= slider.Path.ControlPoints.Select(p => p.Type.Value).ToList();
|
referencePathTypes ??= slider.Path.ControlPoints.Select(p => p.Type).ToList();
|
||||||
|
|
||||||
Quad sliderQuad = GetSurroundingQuad(slider.Path.ControlPoints.Select(p => p.Position.Value));
|
Quad sliderQuad = GetSurroundingQuad(slider.Path.ControlPoints.Select(p => p.Position));
|
||||||
|
|
||||||
// Limit minimum distance between control points after scaling to almost 0. Less than 0 causes the slider to flip, exactly 0 causes a crash through division by 0.
|
// Limit minimum distance between control points after scaling to almost 0. Less than 0 causes the slider to flip, exactly 0 causes a crash through division by 0.
|
||||||
scale = Vector2.ComponentMax(new Vector2(Precision.FLOAT_EPSILON), sliderQuad.Size + scale) - sliderQuad.Size;
|
scale = Vector2.ComponentMax(new Vector2(Precision.FLOAT_EPSILON), sliderQuad.Size + scale) - sliderQuad.Size;
|
||||||
@ -178,13 +178,13 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
|
|
||||||
foreach (var point in slider.Path.ControlPoints)
|
foreach (var point in slider.Path.ControlPoints)
|
||||||
{
|
{
|
||||||
oldControlPoints.Enqueue(point.Position.Value);
|
oldControlPoints.Enqueue(point.Position);
|
||||||
point.Position.Value *= pathRelativeDeltaScale;
|
point.Position *= pathRelativeDeltaScale;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Maintain the path types in case they were defaulted to bezier at some point during scaling
|
// Maintain the path types in case they were defaulted to bezier at some point during scaling
|
||||||
for (int i = 0; i < slider.Path.ControlPoints.Count; ++i)
|
for (int i = 0; i < slider.Path.ControlPoints.Count; ++i)
|
||||||
slider.Path.ControlPoints[i].Type.Value = referencePathTypes[i];
|
slider.Path.ControlPoints[i].Type = referencePathTypes[i];
|
||||||
|
|
||||||
//if sliderhead or sliderend end up outside playfield, revert scaling.
|
//if sliderhead or sliderend end up outside playfield, revert scaling.
|
||||||
Quad scaledQuad = getSurroundingQuad(new OsuHitObject[] { slider });
|
Quad scaledQuad = getSurroundingQuad(new OsuHitObject[] { slider });
|
||||||
@ -194,7 +194,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
foreach (var point in slider.Path.ControlPoints)
|
foreach (var point in slider.Path.ControlPoints)
|
||||||
point.Position.Value = oldControlPoints.Dequeue();
|
point.Position = oldControlPoints.Dequeue();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scaleHitObjects(OsuHitObject[] hitObjects, Anchor reference, Vector2 scale)
|
private void scaleHitObjects(OsuHitObject[] hitObjects, Anchor reference, Vector2 scale)
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#nullable enable
|
#nullable enable
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics.Performance;
|
using osu.Framework.Graphics.Performance;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
@ -20,8 +21,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
|||||||
{
|
{
|
||||||
Start = start;
|
Start = start;
|
||||||
LifetimeStart = Start.StartTime;
|
LifetimeStart = Start.StartTime;
|
||||||
|
|
||||||
bindEvents();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private OsuHitObject? end;
|
private OsuHitObject? end;
|
||||||
@ -41,31 +40,39 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool wasBound;
|
||||||
|
|
||||||
private void bindEvents()
|
private void bindEvents()
|
||||||
{
|
{
|
||||||
UnbindEvents();
|
UnbindEvents();
|
||||||
|
|
||||||
|
if (End == null)
|
||||||
|
return;
|
||||||
|
|
||||||
// Note: Positions are bound for instantaneous feedback from positional changes from the editor, before ApplyDefaults() is called on hitobjects.
|
// Note: Positions are bound for instantaneous feedback from positional changes from the editor, before ApplyDefaults() is called on hitobjects.
|
||||||
Start.DefaultsApplied += onDefaultsApplied;
|
Start.DefaultsApplied += onDefaultsApplied;
|
||||||
Start.PositionBindable.ValueChanged += onPositionChanged;
|
Start.PositionBindable.ValueChanged += onPositionChanged;
|
||||||
|
|
||||||
if (End != null)
|
End.DefaultsApplied += onDefaultsApplied;
|
||||||
{
|
End.PositionBindable.ValueChanged += onPositionChanged;
|
||||||
End.DefaultsApplied += onDefaultsApplied;
|
|
||||||
End.PositionBindable.ValueChanged += onPositionChanged;
|
wasBound = true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UnbindEvents()
|
public void UnbindEvents()
|
||||||
{
|
{
|
||||||
|
if (!wasBound)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Debug.Assert(End != null);
|
||||||
|
|
||||||
Start.DefaultsApplied -= onDefaultsApplied;
|
Start.DefaultsApplied -= onDefaultsApplied;
|
||||||
Start.PositionBindable.ValueChanged -= onPositionChanged;
|
Start.PositionBindable.ValueChanged -= onPositionChanged;
|
||||||
|
|
||||||
if (End != null)
|
End.DefaultsApplied -= onDefaultsApplied;
|
||||||
{
|
End.PositionBindable.ValueChanged -= onPositionChanged;
|
||||||
End.DefaultsApplied -= onDefaultsApplied;
|
|
||||||
End.PositionBindable.ValueChanged -= onPositionChanged;
|
wasBound = false;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onDefaultsApplied(HitObject obj) => refreshLifetimes();
|
private void onDefaultsApplied(HitObject obj) => refreshLifetimes();
|
||||||
|
@ -77,7 +77,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
base.PlayAnimation();
|
base.PlayAnimation();
|
||||||
|
|
||||||
if (Result != HitResult.Miss)
|
if (Result != HitResult.Miss)
|
||||||
JudgementText.TransformSpacingTo(Vector2.Zero).Then().TransformSpacingTo(new Vector2(14, 0), 1800, Easing.OutQuint);
|
JudgementText.ScaleTo(new Vector2(0.8f, 1)).Then().ScaleTo(new Vector2(1.2f, 1), 1800, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
|
|
||||||
if (value != null)
|
if (value != null)
|
||||||
{
|
{
|
||||||
path.ControlPoints.AddRange(value.ControlPoints.Select(c => new PathControlPoint(c.Position.Value, c.Type.Value)));
|
path.ControlPoints.AddRange(value.ControlPoints.Select(c => new PathControlPoint(c.Position, c.Type)));
|
||||||
path.ExpectedDistance.Value = value.ExpectedDistance.Value;
|
path.ExpectedDistance.Value = value.ExpectedDistance.Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,9 +119,9 @@ namespace osu.Game.Rulesets.Osu.Utils
|
|||||||
slider.NestedHitObjects.OfType<SliderTick>().ForEach(h => h.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - h.Position.X, h.Position.Y));
|
slider.NestedHitObjects.OfType<SliderTick>().ForEach(h => h.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - h.Position.X, h.Position.Y));
|
||||||
slider.NestedHitObjects.OfType<SliderRepeat>().ForEach(h => h.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - h.Position.X, h.Position.Y));
|
slider.NestedHitObjects.OfType<SliderRepeat>().ForEach(h => h.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - h.Position.X, h.Position.Y));
|
||||||
|
|
||||||
var controlPoints = slider.Path.ControlPoints.Select(p => new PathControlPoint(p.Position.Value, p.Type.Value)).ToArray();
|
var controlPoints = slider.Path.ControlPoints.Select(p => new PathControlPoint(p.Position, p.Type)).ToArray();
|
||||||
foreach (var point in controlPoints)
|
foreach (var point in controlPoints)
|
||||||
point.Position.Value = new Vector2(-point.Position.Value.X, point.Position.Value.Y);
|
point.Position = new Vector2(-point.Position.X, point.Position.Y);
|
||||||
|
|
||||||
slider.Path = new SliderPath(controlPoints, slider.Path.ExpectedDistance.Value);
|
slider.Path = new SliderPath(controlPoints, slider.Path.ExpectedDistance.Value);
|
||||||
}
|
}
|
||||||
@ -140,9 +140,9 @@ namespace osu.Game.Rulesets.Osu.Utils
|
|||||||
slider.NestedHitObjects.OfType<SliderTick>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
|
slider.NestedHitObjects.OfType<SliderTick>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
|
||||||
slider.NestedHitObjects.OfType<SliderRepeat>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
|
slider.NestedHitObjects.OfType<SliderRepeat>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
|
||||||
|
|
||||||
var controlPoints = slider.Path.ControlPoints.Select(p => new PathControlPoint(p.Position.Value, p.Type.Value)).ToArray();
|
var controlPoints = slider.Path.ControlPoints.Select(p => new PathControlPoint(p.Position, p.Type)).ToArray();
|
||||||
foreach (var point in controlPoints)
|
foreach (var point in controlPoints)
|
||||||
point.Position.Value = new Vector2(point.Position.Value.X, -point.Position.Value.Y);
|
point.Position = new Vector2(point.Position.X, -point.Position.Y);
|
||||||
|
|
||||||
slider.Path = new SliderPath(controlPoints, slider.Path.ExpectedDistance.Value);
|
slider.Path = new SliderPath(controlPoints, slider.Path.ExpectedDistance.Value);
|
||||||
}
|
}
|
||||||
|
@ -666,111 +666,111 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
// Multi-segment
|
// Multi-segment
|
||||||
var first = ((IHasPath)decoded.HitObjects[0]).Path;
|
var first = ((IHasPath)decoded.HitObjects[0]).Path;
|
||||||
|
|
||||||
Assert.That(first.ControlPoints[0].Position.Value, Is.EqualTo(Vector2.Zero));
|
Assert.That(first.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero));
|
||||||
Assert.That(first.ControlPoints[0].Type.Value, Is.EqualTo(PathType.PerfectCurve));
|
Assert.That(first.ControlPoints[0].Type, Is.EqualTo(PathType.PerfectCurve));
|
||||||
Assert.That(first.ControlPoints[1].Position.Value, Is.EqualTo(new Vector2(161, -244)));
|
Assert.That(first.ControlPoints[1].Position, Is.EqualTo(new Vector2(161, -244)));
|
||||||
Assert.That(first.ControlPoints[1].Type.Value, Is.EqualTo(null));
|
Assert.That(first.ControlPoints[1].Type, Is.EqualTo(null));
|
||||||
|
|
||||||
Assert.That(first.ControlPoints[2].Position.Value, Is.EqualTo(new Vector2(376, -3)));
|
Assert.That(first.ControlPoints[2].Position, Is.EqualTo(new Vector2(376, -3)));
|
||||||
Assert.That(first.ControlPoints[2].Type.Value, Is.EqualTo(PathType.Bezier));
|
Assert.That(first.ControlPoints[2].Type, Is.EqualTo(PathType.Bezier));
|
||||||
Assert.That(first.ControlPoints[3].Position.Value, Is.EqualTo(new Vector2(68, 15)));
|
Assert.That(first.ControlPoints[3].Position, Is.EqualTo(new Vector2(68, 15)));
|
||||||
Assert.That(first.ControlPoints[3].Type.Value, Is.EqualTo(null));
|
Assert.That(first.ControlPoints[3].Type, Is.EqualTo(null));
|
||||||
Assert.That(first.ControlPoints[4].Position.Value, Is.EqualTo(new Vector2(259, -132)));
|
Assert.That(first.ControlPoints[4].Position, Is.EqualTo(new Vector2(259, -132)));
|
||||||
Assert.That(first.ControlPoints[4].Type.Value, Is.EqualTo(null));
|
Assert.That(first.ControlPoints[4].Type, Is.EqualTo(null));
|
||||||
Assert.That(first.ControlPoints[5].Position.Value, Is.EqualTo(new Vector2(92, -107)));
|
Assert.That(first.ControlPoints[5].Position, Is.EqualTo(new Vector2(92, -107)));
|
||||||
Assert.That(first.ControlPoints[5].Type.Value, Is.EqualTo(null));
|
Assert.That(first.ControlPoints[5].Type, Is.EqualTo(null));
|
||||||
|
|
||||||
// Single-segment
|
// Single-segment
|
||||||
var second = ((IHasPath)decoded.HitObjects[1]).Path;
|
var second = ((IHasPath)decoded.HitObjects[1]).Path;
|
||||||
|
|
||||||
Assert.That(second.ControlPoints[0].Position.Value, Is.EqualTo(Vector2.Zero));
|
Assert.That(second.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero));
|
||||||
Assert.That(second.ControlPoints[0].Type.Value, Is.EqualTo(PathType.PerfectCurve));
|
Assert.That(second.ControlPoints[0].Type, Is.EqualTo(PathType.PerfectCurve));
|
||||||
Assert.That(second.ControlPoints[1].Position.Value, Is.EqualTo(new Vector2(161, -244)));
|
Assert.That(second.ControlPoints[1].Position, Is.EqualTo(new Vector2(161, -244)));
|
||||||
Assert.That(second.ControlPoints[1].Type.Value, Is.EqualTo(null));
|
Assert.That(second.ControlPoints[1].Type, Is.EqualTo(null));
|
||||||
Assert.That(second.ControlPoints[2].Position.Value, Is.EqualTo(new Vector2(376, -3)));
|
Assert.That(second.ControlPoints[2].Position, Is.EqualTo(new Vector2(376, -3)));
|
||||||
Assert.That(second.ControlPoints[2].Type.Value, Is.EqualTo(null));
|
Assert.That(second.ControlPoints[2].Type, Is.EqualTo(null));
|
||||||
|
|
||||||
// Implicit multi-segment
|
// Implicit multi-segment
|
||||||
var third = ((IHasPath)decoded.HitObjects[2]).Path;
|
var third = ((IHasPath)decoded.HitObjects[2]).Path;
|
||||||
|
|
||||||
Assert.That(third.ControlPoints[0].Position.Value, Is.EqualTo(Vector2.Zero));
|
Assert.That(third.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero));
|
||||||
Assert.That(third.ControlPoints[0].Type.Value, Is.EqualTo(PathType.Bezier));
|
Assert.That(third.ControlPoints[0].Type, Is.EqualTo(PathType.Bezier));
|
||||||
Assert.That(third.ControlPoints[1].Position.Value, Is.EqualTo(new Vector2(0, 192)));
|
Assert.That(third.ControlPoints[1].Position, Is.EqualTo(new Vector2(0, 192)));
|
||||||
Assert.That(third.ControlPoints[1].Type.Value, Is.EqualTo(null));
|
Assert.That(third.ControlPoints[1].Type, Is.EqualTo(null));
|
||||||
Assert.That(third.ControlPoints[2].Position.Value, Is.EqualTo(new Vector2(224, 192)));
|
Assert.That(third.ControlPoints[2].Position, Is.EqualTo(new Vector2(224, 192)));
|
||||||
Assert.That(third.ControlPoints[2].Type.Value, Is.EqualTo(null));
|
Assert.That(third.ControlPoints[2].Type, Is.EqualTo(null));
|
||||||
|
|
||||||
Assert.That(third.ControlPoints[3].Position.Value, Is.EqualTo(new Vector2(224, 0)));
|
Assert.That(third.ControlPoints[3].Position, Is.EqualTo(new Vector2(224, 0)));
|
||||||
Assert.That(third.ControlPoints[3].Type.Value, Is.EqualTo(PathType.Bezier));
|
Assert.That(third.ControlPoints[3].Type, Is.EqualTo(PathType.Bezier));
|
||||||
Assert.That(third.ControlPoints[4].Position.Value, Is.EqualTo(new Vector2(224, -192)));
|
Assert.That(third.ControlPoints[4].Position, Is.EqualTo(new Vector2(224, -192)));
|
||||||
Assert.That(third.ControlPoints[4].Type.Value, Is.EqualTo(null));
|
Assert.That(third.ControlPoints[4].Type, Is.EqualTo(null));
|
||||||
Assert.That(third.ControlPoints[5].Position.Value, Is.EqualTo(new Vector2(480, -192)));
|
Assert.That(third.ControlPoints[5].Position, Is.EqualTo(new Vector2(480, -192)));
|
||||||
Assert.That(third.ControlPoints[5].Type.Value, Is.EqualTo(null));
|
Assert.That(third.ControlPoints[5].Type, Is.EqualTo(null));
|
||||||
Assert.That(third.ControlPoints[6].Position.Value, Is.EqualTo(new Vector2(480, 0)));
|
Assert.That(third.ControlPoints[6].Position, Is.EqualTo(new Vector2(480, 0)));
|
||||||
Assert.That(third.ControlPoints[6].Type.Value, Is.EqualTo(null));
|
Assert.That(third.ControlPoints[6].Type, Is.EqualTo(null));
|
||||||
|
|
||||||
// Last control point duplicated
|
// Last control point duplicated
|
||||||
var fourth = ((IHasPath)decoded.HitObjects[3]).Path;
|
var fourth = ((IHasPath)decoded.HitObjects[3]).Path;
|
||||||
|
|
||||||
Assert.That(fourth.ControlPoints[0].Position.Value, Is.EqualTo(Vector2.Zero));
|
Assert.That(fourth.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero));
|
||||||
Assert.That(fourth.ControlPoints[0].Type.Value, Is.EqualTo(PathType.Bezier));
|
Assert.That(fourth.ControlPoints[0].Type, Is.EqualTo(PathType.Bezier));
|
||||||
Assert.That(fourth.ControlPoints[1].Position.Value, Is.EqualTo(new Vector2(1, 1)));
|
Assert.That(fourth.ControlPoints[1].Position, Is.EqualTo(new Vector2(1, 1)));
|
||||||
Assert.That(fourth.ControlPoints[1].Type.Value, Is.EqualTo(null));
|
Assert.That(fourth.ControlPoints[1].Type, Is.EqualTo(null));
|
||||||
Assert.That(fourth.ControlPoints[2].Position.Value, Is.EqualTo(new Vector2(2, 2)));
|
Assert.That(fourth.ControlPoints[2].Position, Is.EqualTo(new Vector2(2, 2)));
|
||||||
Assert.That(fourth.ControlPoints[2].Type.Value, Is.EqualTo(null));
|
Assert.That(fourth.ControlPoints[2].Type, Is.EqualTo(null));
|
||||||
Assert.That(fourth.ControlPoints[3].Position.Value, Is.EqualTo(new Vector2(3, 3)));
|
Assert.That(fourth.ControlPoints[3].Position, Is.EqualTo(new Vector2(3, 3)));
|
||||||
Assert.That(fourth.ControlPoints[3].Type.Value, Is.EqualTo(null));
|
Assert.That(fourth.ControlPoints[3].Type, Is.EqualTo(null));
|
||||||
Assert.That(fourth.ControlPoints[4].Position.Value, Is.EqualTo(new Vector2(3, 3)));
|
Assert.That(fourth.ControlPoints[4].Position, Is.EqualTo(new Vector2(3, 3)));
|
||||||
Assert.That(fourth.ControlPoints[4].Type.Value, Is.EqualTo(null));
|
Assert.That(fourth.ControlPoints[4].Type, Is.EqualTo(null));
|
||||||
|
|
||||||
// Last control point in segment duplicated
|
// Last control point in segment duplicated
|
||||||
var fifth = ((IHasPath)decoded.HitObjects[4]).Path;
|
var fifth = ((IHasPath)decoded.HitObjects[4]).Path;
|
||||||
|
|
||||||
Assert.That(fifth.ControlPoints[0].Position.Value, Is.EqualTo(Vector2.Zero));
|
Assert.That(fifth.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero));
|
||||||
Assert.That(fifth.ControlPoints[0].Type.Value, Is.EqualTo(PathType.Bezier));
|
Assert.That(fifth.ControlPoints[0].Type, Is.EqualTo(PathType.Bezier));
|
||||||
Assert.That(fifth.ControlPoints[1].Position.Value, Is.EqualTo(new Vector2(1, 1)));
|
Assert.That(fifth.ControlPoints[1].Position, Is.EqualTo(new Vector2(1, 1)));
|
||||||
Assert.That(fifth.ControlPoints[1].Type.Value, Is.EqualTo(null));
|
Assert.That(fifth.ControlPoints[1].Type, Is.EqualTo(null));
|
||||||
Assert.That(fifth.ControlPoints[2].Position.Value, Is.EqualTo(new Vector2(2, 2)));
|
Assert.That(fifth.ControlPoints[2].Position, Is.EqualTo(new Vector2(2, 2)));
|
||||||
Assert.That(fifth.ControlPoints[2].Type.Value, Is.EqualTo(null));
|
Assert.That(fifth.ControlPoints[2].Type, Is.EqualTo(null));
|
||||||
Assert.That(fifth.ControlPoints[3].Position.Value, Is.EqualTo(new Vector2(3, 3)));
|
Assert.That(fifth.ControlPoints[3].Position, Is.EqualTo(new Vector2(3, 3)));
|
||||||
Assert.That(fifth.ControlPoints[3].Type.Value, Is.EqualTo(null));
|
Assert.That(fifth.ControlPoints[3].Type, Is.EqualTo(null));
|
||||||
Assert.That(fifth.ControlPoints[4].Position.Value, Is.EqualTo(new Vector2(3, 3)));
|
Assert.That(fifth.ControlPoints[4].Position, Is.EqualTo(new Vector2(3, 3)));
|
||||||
Assert.That(fifth.ControlPoints[4].Type.Value, Is.EqualTo(null));
|
Assert.That(fifth.ControlPoints[4].Type, Is.EqualTo(null));
|
||||||
|
|
||||||
Assert.That(fifth.ControlPoints[5].Position.Value, Is.EqualTo(new Vector2(4, 4)));
|
Assert.That(fifth.ControlPoints[5].Position, Is.EqualTo(new Vector2(4, 4)));
|
||||||
Assert.That(fifth.ControlPoints[5].Type.Value, Is.EqualTo(PathType.Bezier));
|
Assert.That(fifth.ControlPoints[5].Type, Is.EqualTo(PathType.Bezier));
|
||||||
Assert.That(fifth.ControlPoints[6].Position.Value, Is.EqualTo(new Vector2(5, 5)));
|
Assert.That(fifth.ControlPoints[6].Position, Is.EqualTo(new Vector2(5, 5)));
|
||||||
Assert.That(fifth.ControlPoints[6].Type.Value, Is.EqualTo(null));
|
Assert.That(fifth.ControlPoints[6].Type, Is.EqualTo(null));
|
||||||
|
|
||||||
// Implicit perfect-curve multi-segment(Should convert to bezier to match stable)
|
// Implicit perfect-curve multi-segment(Should convert to bezier to match stable)
|
||||||
var sixth = ((IHasPath)decoded.HitObjects[5]).Path;
|
var sixth = ((IHasPath)decoded.HitObjects[5]).Path;
|
||||||
|
|
||||||
Assert.That(sixth.ControlPoints[0].Position.Value, Is.EqualTo(Vector2.Zero));
|
Assert.That(sixth.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero));
|
||||||
Assert.That(sixth.ControlPoints[0].Type.Value == PathType.Bezier);
|
Assert.That(sixth.ControlPoints[0].Type == PathType.Bezier);
|
||||||
Assert.That(sixth.ControlPoints[1].Position.Value, Is.EqualTo(new Vector2(75, 145)));
|
Assert.That(sixth.ControlPoints[1].Position, Is.EqualTo(new Vector2(75, 145)));
|
||||||
Assert.That(sixth.ControlPoints[1].Type.Value == null);
|
Assert.That(sixth.ControlPoints[1].Type == null);
|
||||||
Assert.That(sixth.ControlPoints[2].Position.Value, Is.EqualTo(new Vector2(170, 75)));
|
Assert.That(sixth.ControlPoints[2].Position, Is.EqualTo(new Vector2(170, 75)));
|
||||||
|
|
||||||
Assert.That(sixth.ControlPoints[2].Type.Value == PathType.Bezier);
|
Assert.That(sixth.ControlPoints[2].Type == PathType.Bezier);
|
||||||
Assert.That(sixth.ControlPoints[3].Position.Value, Is.EqualTo(new Vector2(300, 145)));
|
Assert.That(sixth.ControlPoints[3].Position, Is.EqualTo(new Vector2(300, 145)));
|
||||||
Assert.That(sixth.ControlPoints[3].Type.Value == null);
|
Assert.That(sixth.ControlPoints[3].Type == null);
|
||||||
Assert.That(sixth.ControlPoints[4].Position.Value, Is.EqualTo(new Vector2(410, 20)));
|
Assert.That(sixth.ControlPoints[4].Position, Is.EqualTo(new Vector2(410, 20)));
|
||||||
Assert.That(sixth.ControlPoints[4].Type.Value == null);
|
Assert.That(sixth.ControlPoints[4].Type == null);
|
||||||
|
|
||||||
// Explicit perfect-curve multi-segment(Should not convert to bezier)
|
// Explicit perfect-curve multi-segment(Should not convert to bezier)
|
||||||
var seventh = ((IHasPath)decoded.HitObjects[6]).Path;
|
var seventh = ((IHasPath)decoded.HitObjects[6]).Path;
|
||||||
|
|
||||||
Assert.That(seventh.ControlPoints[0].Position.Value, Is.EqualTo(Vector2.Zero));
|
Assert.That(seventh.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero));
|
||||||
Assert.That(seventh.ControlPoints[0].Type.Value == PathType.PerfectCurve);
|
Assert.That(seventh.ControlPoints[0].Type == PathType.PerfectCurve);
|
||||||
Assert.That(seventh.ControlPoints[1].Position.Value, Is.EqualTo(new Vector2(75, 145)));
|
Assert.That(seventh.ControlPoints[1].Position, Is.EqualTo(new Vector2(75, 145)));
|
||||||
Assert.That(seventh.ControlPoints[1].Type.Value == null);
|
Assert.That(seventh.ControlPoints[1].Type == null);
|
||||||
Assert.That(seventh.ControlPoints[2].Position.Value, Is.EqualTo(new Vector2(170, 75)));
|
Assert.That(seventh.ControlPoints[2].Position, Is.EqualTo(new Vector2(170, 75)));
|
||||||
|
|
||||||
Assert.That(seventh.ControlPoints[2].Type.Value == PathType.PerfectCurve);
|
Assert.That(seventh.ControlPoints[2].Type == PathType.PerfectCurve);
|
||||||
Assert.That(seventh.ControlPoints[3].Position.Value, Is.EqualTo(new Vector2(300, 145)));
|
Assert.That(seventh.ControlPoints[3].Position, Is.EqualTo(new Vector2(300, 145)));
|
||||||
Assert.That(seventh.ControlPoints[3].Type.Value == null);
|
Assert.That(seventh.ControlPoints[3].Type == null);
|
||||||
Assert.That(seventh.ControlPoints[4].Position.Value, Is.EqualTo(new Vector2(410, 20)));
|
Assert.That(seventh.ControlPoints[4].Position, Is.EqualTo(new Vector2(410, 20)));
|
||||||
Assert.That(seventh.ControlPoints[4].Type.Value == null);
|
Assert.That(seventh.ControlPoints[4].Type == null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,14 +74,14 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
public void TestAddControlPoint()
|
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 = { Value = new Vector2(100) } }));
|
AddStep("add point", () => path.ControlPoints.Add(new PathControlPoint { Position = new Vector2(100) }));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestInsertControlPoint()
|
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 = { Value = new Vector2(0, 100) } }));
|
AddStep("insert point", () => path.ControlPoints.Insert(1, new PathControlPoint { Position = new Vector2(0, 100) }));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -95,14 +95,14 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
public void TestChangePathType()
|
public void TestChangePathType()
|
||||||
{
|
{
|
||||||
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("change type to bezier", () => path.ControlPoints[0].Type.Value = PathType.Bezier);
|
AddStep("change type to bezier", () => path.ControlPoints[0].Type = PathType.Bezier);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestAddSegmentByChangingType()
|
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("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.Value = PathType.Bezier);
|
AddStep("change second point type to bezier", () => path.ControlPoints[1].Type = PathType.Bezier);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -111,10 +111,10 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddStep("create path", () =>
|
AddStep("create path", () =>
|
||||||
{
|
{
|
||||||
path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0)));
|
path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0)));
|
||||||
path.ControlPoints[1].Type.Value = PathType.Bezier;
|
path.ControlPoints[1].Type = PathType.Bezier;
|
||||||
});
|
});
|
||||||
|
|
||||||
AddStep("change second point type to null", () => path.ControlPoints[1].Type.Value = null);
|
AddStep("change second point type to null", () => path.ControlPoints[1].Type = null);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -123,7 +123,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddStep("create path", () =>
|
AddStep("create path", () =>
|
||||||
{
|
{
|
||||||
path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0)));
|
path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0)));
|
||||||
path.ControlPoints[1].Type.Value = PathType.Bezier;
|
path.ControlPoints[1].Type = PathType.Bezier;
|
||||||
});
|
});
|
||||||
|
|
||||||
AddStep("remove second point", () => path.ControlPoints.RemoveAt(1));
|
AddStep("remove second point", () => path.ControlPoints.RemoveAt(1));
|
||||||
@ -185,8 +185,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
private List<PathControlPoint> createSegment(PathType type, params Vector2[] controlPoints)
|
private List<PathControlPoint> createSegment(PathType type, params Vector2[] controlPoints)
|
||||||
{
|
{
|
||||||
var points = controlPoints.Select(p => new PathControlPoint { Position = { Value = p } }).ToList();
|
var points = controlPoints.Select(p => new PathControlPoint { Position = p }).ToList();
|
||||||
points[0].Type.Value = type;
|
points[0].Type = type;
|
||||||
return points;
|
return points;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
@ -12,6 +13,7 @@ using osu.Framework.Input;
|
|||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Database;
|
using osu.Game.Database;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
@ -94,6 +96,120 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
AddStep("empty step", () => { });
|
AddStep("empty step", () => { });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestLobbyEvents()
|
||||||
|
{
|
||||||
|
createRoom(() => new Room
|
||||||
|
{
|
||||||
|
Name = { Value = "Test Room" },
|
||||||
|
Playlist =
|
||||||
|
{
|
||||||
|
new PlaylistItem
|
||||||
|
{
|
||||||
|
Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.RulesetID == 0)).BeatmapInfo },
|
||||||
|
Ruleset = { Value = new OsuRuleset().RulesetInfo },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
AddRepeatStep("random stuff happens", performRandomAction, 30);
|
||||||
|
|
||||||
|
// ensure we have a handful of players so the ready-up sounds good :9
|
||||||
|
AddRepeatStep("player joins", addRandomPlayer, 5);
|
||||||
|
|
||||||
|
// all ready
|
||||||
|
AddUntilStep("all players ready", () =>
|
||||||
|
{
|
||||||
|
var nextUnready = client.Room?.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Idle);
|
||||||
|
if (nextUnready != null)
|
||||||
|
client.ChangeUserState(nextUnready.UserID, MultiplayerUserState.Ready);
|
||||||
|
|
||||||
|
return client.Room?.Users.All(u => u.State == MultiplayerUserState.Ready) == true;
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("unready all players at once", () =>
|
||||||
|
{
|
||||||
|
Debug.Assert(client.Room != null);
|
||||||
|
|
||||||
|
foreach (var u in client.Room.Users) client.ChangeUserState(u.UserID, MultiplayerUserState.Idle);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("ready all players at once", () =>
|
||||||
|
{
|
||||||
|
Debug.Assert(client.Room != null);
|
||||||
|
|
||||||
|
foreach (var u in client.Room.Users) client.ChangeUserState(u.UserID, MultiplayerUserState.Ready);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addRandomPlayer()
|
||||||
|
{
|
||||||
|
int randomUser = RNG.Next(200000, 500000);
|
||||||
|
client.AddUser(new User { Id = randomUser, Username = $"user {randomUser}" });
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeLastUser()
|
||||||
|
{
|
||||||
|
User lastUser = client.Room?.Users.Last().User;
|
||||||
|
|
||||||
|
if (lastUser == null || lastUser == client.LocalUser?.User)
|
||||||
|
return;
|
||||||
|
|
||||||
|
client.RemoveUser(lastUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void kickLastUser()
|
||||||
|
{
|
||||||
|
User lastUser = client.Room?.Users.Last().User;
|
||||||
|
|
||||||
|
if (lastUser == null || lastUser == client.LocalUser?.User)
|
||||||
|
return;
|
||||||
|
|
||||||
|
client.KickUser(lastUser.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void markNextPlayerReady()
|
||||||
|
{
|
||||||
|
var nextUnready = client.Room?.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Idle);
|
||||||
|
if (nextUnready != null)
|
||||||
|
client.ChangeUserState(nextUnready.UserID, MultiplayerUserState.Ready);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void markNextPlayerIdle()
|
||||||
|
{
|
||||||
|
var nextUnready = client.Room?.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Ready);
|
||||||
|
if (nextUnready != null)
|
||||||
|
client.ChangeUserState(nextUnready.UserID, MultiplayerUserState.Idle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void performRandomAction()
|
||||||
|
{
|
||||||
|
int eventToPerform = RNG.Next(1, 6);
|
||||||
|
|
||||||
|
switch (eventToPerform)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
addRandomPlayer();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
removeLastUser();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
kickLastUser();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
markNextPlayerReady();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 5:
|
||||||
|
markNextPlayerIdle();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestCreateRoomViaKeyboard()
|
public void TestCreateRoomViaKeyboard()
|
||||||
{
|
{
|
||||||
|
@ -323,22 +323,22 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
{
|
{
|
||||||
PathControlPoint point = pathData.Path.ControlPoints[i];
|
PathControlPoint point = pathData.Path.ControlPoints[i];
|
||||||
|
|
||||||
if (point.Type.Value != null)
|
if (point.Type != null)
|
||||||
{
|
{
|
||||||
// We've reached a new (explicit) segment!
|
// We've reached a new (explicit) segment!
|
||||||
|
|
||||||
// Explicit segments have a new format in which the type is injected into the middle of the control point string.
|
// Explicit segments have a new format in which the type is injected into the middle of the control point string.
|
||||||
// To preserve compatibility with osu-stable as much as possible, explicit segments with the same type are converted to use implicit segments by duplicating the control point.
|
// To preserve compatibility with osu-stable as much as possible, explicit segments with the same type are converted to use implicit segments by duplicating the control point.
|
||||||
// One exception are consecutive perfect curves, which aren't supported in osu!stable and can lead to decoding issues if encoded as implicit segments
|
// One exception are consecutive perfect curves, which aren't supported in osu!stable and can lead to decoding issues if encoded as implicit segments
|
||||||
bool needsExplicitSegment = point.Type.Value != lastType || point.Type.Value == PathType.PerfectCurve;
|
bool needsExplicitSegment = point.Type != lastType || point.Type == PathType.PerfectCurve;
|
||||||
|
|
||||||
// Another exception to this is when the last two control points of the last segment were duplicated. This is not a scenario supported by osu!stable.
|
// Another exception to this is when the last two control points of the last segment were duplicated. This is not a scenario supported by osu!stable.
|
||||||
// Lazer does not add implicit segments for the last two control points of _any_ explicit segment, so an explicit segment is forced in order to maintain consistency with the decoder.
|
// Lazer does not add implicit segments for the last two control points of _any_ explicit segment, so an explicit segment is forced in order to maintain consistency with the decoder.
|
||||||
if (i > 1)
|
if (i > 1)
|
||||||
{
|
{
|
||||||
// We need to use the absolute control point position to determine equality, otherwise floating point issues may arise.
|
// We need to use the absolute control point position to determine equality, otherwise floating point issues may arise.
|
||||||
Vector2 p1 = position + pathData.Path.ControlPoints[i - 1].Position.Value;
|
Vector2 p1 = position + pathData.Path.ControlPoints[i - 1].Position;
|
||||||
Vector2 p2 = position + pathData.Path.ControlPoints[i - 2].Position.Value;
|
Vector2 p2 = position + pathData.Path.ControlPoints[i - 2].Position;
|
||||||
|
|
||||||
if ((int)p1.X == (int)p2.X && (int)p1.Y == (int)p2.Y)
|
if ((int)p1.X == (int)p2.X && (int)p1.Y == (int)p2.Y)
|
||||||
needsExplicitSegment = true;
|
needsExplicitSegment = true;
|
||||||
@ -346,7 +346,7 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
|
|
||||||
if (needsExplicitSegment)
|
if (needsExplicitSegment)
|
||||||
{
|
{
|
||||||
switch (point.Type.Value)
|
switch (point.Type)
|
||||||
{
|
{
|
||||||
case PathType.Bezier:
|
case PathType.Bezier:
|
||||||
writer.Write("B|");
|
writer.Write("B|");
|
||||||
@ -365,18 +365,18 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
lastType = point.Type.Value;
|
lastType = point.Type;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// New segment with the same type - duplicate the control point
|
// New segment with the same type - duplicate the control point
|
||||||
writer.Write(FormattableString.Invariant($"{position.X + point.Position.Value.X}:{position.Y + point.Position.Value.Y}|"));
|
writer.Write(FormattableString.Invariant($"{position.X + point.Position.X}:{position.Y + point.Position.Y}|"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i != 0)
|
if (i != 0)
|
||||||
{
|
{
|
||||||
writer.Write(FormattableString.Invariant($"{position.X + point.Position.Value.X}:{position.Y + point.Position.Value.Y}"));
|
writer.Write(FormattableString.Invariant($"{position.X + point.Position.X}:{position.Y + point.Position.Y}"));
|
||||||
writer.Write(i != pathData.Path.ControlPoints.Count - 1 ? "|" : ",");
|
writer.Write(i != pathData.Path.ControlPoints.Count - 1 ? "|" : ",");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,9 +42,24 @@ namespace osu.Game.Input.Handlers
|
|||||||
if (!(state is RulesetInputManagerInputState<T> inputState))
|
if (!(state is RulesetInputManagerInputState<T> inputState))
|
||||||
throw new InvalidOperationException($"{nameof(ReplayState<T>)} should only be applied to a {nameof(RulesetInputManagerInputState<T>)}");
|
throw new InvalidOperationException($"{nameof(ReplayState<T>)} should only be applied to a {nameof(RulesetInputManagerInputState<T>)}");
|
||||||
|
|
||||||
var lastPressed = inputState.LastReplayState?.PressedActions ?? new List<T>();
|
T[] released = Array.Empty<T>();
|
||||||
var released = lastPressed.Except(PressedActions).ToArray();
|
T[] pressed = Array.Empty<T>();
|
||||||
var pressed = PressedActions.Except(lastPressed).ToArray();
|
|
||||||
|
var lastPressed = inputState.LastReplayState?.PressedActions;
|
||||||
|
|
||||||
|
if (lastPressed == null || lastPressed.Count == 0)
|
||||||
|
{
|
||||||
|
pressed = PressedActions.ToArray();
|
||||||
|
}
|
||||||
|
else if (PressedActions.Count == 0)
|
||||||
|
{
|
||||||
|
released = lastPressed.ToArray();
|
||||||
|
}
|
||||||
|
else if (!lastPressed.SequenceEqual(PressedActions))
|
||||||
|
{
|
||||||
|
released = lastPressed.Except(PressedActions).ToArray();
|
||||||
|
pressed = PressedActions.Except(lastPressed).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
inputState.LastReplayState = this;
|
inputState.LastReplayState = this;
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -91,7 +92,7 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
{
|
{
|
||||||
// This is required as SettingsItem relies heavily on this bindable for internal use.
|
// This is required as SettingsItem relies heavily on this bindable for internal use.
|
||||||
// The actual update flow is done via the bindable provided in the constructor.
|
// The actual update flow is done via the bindable provided in the constructor.
|
||||||
private readonly BindableWithCurrent<float?> current = new BindableWithCurrent<float?>();
|
private readonly DifficultyBindableWithCurrent current = new DifficultyBindableWithCurrent();
|
||||||
|
|
||||||
public Bindable<float?> Current
|
public Bindable<float?> Current
|
||||||
{
|
{
|
||||||
@ -114,5 +115,30 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class DifficultyBindableWithCurrent : DifficultyBindable, IHasCurrentValue<float?>
|
||||||
|
{
|
||||||
|
private Bindable<float?> currentBound;
|
||||||
|
|
||||||
|
public Bindable<float?> Current
|
||||||
|
{
|
||||||
|
get => this;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
throw new ArgumentNullException(nameof(value));
|
||||||
|
|
||||||
|
if (currentBound != null) UnbindFrom(currentBound);
|
||||||
|
BindTo(currentBound = value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DifficultyBindableWithCurrent(float? defaultValue = default)
|
||||||
|
: base(defaultValue)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Bindable<float?> CreateInstance() => new DifficultyBindableWithCurrent();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -323,7 +323,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The first control point must have a definite type.
|
// The first control point must have a definite type.
|
||||||
vertices[0].Type.Value = type;
|
vertices[0].Type = type;
|
||||||
|
|
||||||
// A path can have multiple implicit segments of the same type if there are two sequential control points with the same position.
|
// A path can have multiple implicit segments of the same type if there are two sequential control points with the same position.
|
||||||
// To handle such cases, this code may return multiple path segments with the final control point in each segment having a non-null type.
|
// To handle such cases, this code may return multiple path segments with the final control point in each segment having a non-null type.
|
||||||
@ -337,7 +337,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
while (++endIndex < vertices.Length - endPointLength)
|
while (++endIndex < vertices.Length - endPointLength)
|
||||||
{
|
{
|
||||||
// Keep incrementing while an implicit segment doesn't need to be started
|
// Keep incrementing while an implicit segment doesn't need to be started
|
||||||
if (vertices[endIndex].Position.Value != vertices[endIndex - 1].Position.Value)
|
if (vertices[endIndex].Position != vertices[endIndex - 1].Position)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// The last control point of each segment is not allowed to start a new implicit segment.
|
// The last control point of each segment is not allowed to start a new implicit segment.
|
||||||
@ -345,7 +345,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Force a type on the last point, and return the current control point set as a segment.
|
// Force a type on the last point, and return the current control point set as a segment.
|
||||||
vertices[endIndex - 1].Type.Value = type;
|
vertices[endIndex - 1].Type = type;
|
||||||
yield return vertices.AsMemory().Slice(startIndex, endIndex - startIndex);
|
yield return vertices.AsMemory().Slice(startIndex, endIndex - startIndex);
|
||||||
|
|
||||||
// Skip the current control point - as it's the same as the one that's just been returned.
|
// Skip the current control point - as it's the same as the one that's just been returned.
|
||||||
@ -360,11 +360,11 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
string[] vertexSplit = value.Split(':');
|
string[] vertexSplit = value.Split(':');
|
||||||
|
|
||||||
Vector2 pos = new Vector2((int)Parsing.ParseDouble(vertexSplit[0], Parsing.MAX_COORDINATE_VALUE), (int)Parsing.ParseDouble(vertexSplit[1], Parsing.MAX_COORDINATE_VALUE)) - startPos;
|
Vector2 pos = new Vector2((int)Parsing.ParseDouble(vertexSplit[0], Parsing.MAX_COORDINATE_VALUE), (int)Parsing.ParseDouble(vertexSplit[1], Parsing.MAX_COORDINATE_VALUE)) - startPos;
|
||||||
point = new PathControlPoint { Position = { Value = pos } };
|
point = new PathControlPoint { Position = pos };
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isLinear(PathControlPoint[] p) => Precision.AlmostEquals(0, (p[1].Position.Value.Y - p[0].Position.Value.Y) * (p[2].Position.Value.X - p[0].Position.Value.X)
|
static bool isLinear(PathControlPoint[] p) => Precision.AlmostEquals(0, (p[1].Position.Y - p[0].Position.Y) * (p[2].Position.X - p[0].Position.X)
|
||||||
- (p[1].Position.Value.X - p[0].Position.Value.X) * (p[2].Position.Value.Y - p[0].Position.Value.Y));
|
- (p[1].Position.X - p[0].Position.X) * (p[2].Position.Y - p[0].Position.Y));
|
||||||
}
|
}
|
||||||
|
|
||||||
private PathControlPoint[] mergePointsLists(List<Memory<PathControlPoint>> controlPointList)
|
private PathControlPoint[] mergePointsLists(List<Memory<PathControlPoint>> controlPointList)
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using osu.Framework.Bindables;
|
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
@ -11,31 +10,55 @@ namespace osu.Game.Rulesets.Objects
|
|||||||
{
|
{
|
||||||
public class PathControlPoint : IEquatable<PathControlPoint>
|
public class PathControlPoint : IEquatable<PathControlPoint>
|
||||||
{
|
{
|
||||||
|
private Vector2 position;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The position of this <see cref="PathControlPoint"/>.
|
/// The position of this <see cref="PathControlPoint"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty]
|
[JsonProperty]
|
||||||
public readonly Bindable<Vector2> Position = new Bindable<Vector2>();
|
public Vector2 Position
|
||||||
|
{
|
||||||
|
get => position;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == position)
|
||||||
|
return;
|
||||||
|
|
||||||
|
position = value;
|
||||||
|
Changed?.Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private PathType? type;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The type of path segment starting at this <see cref="PathControlPoint"/>.
|
/// The type of path segment starting at this <see cref="PathControlPoint"/>.
|
||||||
/// If null, this <see cref="PathControlPoint"/> will be a part of the previous path segment.
|
/// If null, this <see cref="PathControlPoint"/> will be a part of the previous path segment.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty]
|
[JsonProperty]
|
||||||
public readonly Bindable<PathType?> Type = new Bindable<PathType?>();
|
public PathType? Type
|
||||||
|
{
|
||||||
|
get => type;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == type)
|
||||||
|
return;
|
||||||
|
|
||||||
|
type = value;
|
||||||
|
Changed?.Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invoked when any property of this <see cref="PathControlPoint"/> is changed.
|
/// Invoked when any property of this <see cref="PathControlPoint"/> is changed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal event Action Changed;
|
public event Action Changed;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new <see cref="PathControlPoint"/>.
|
/// Creates a new <see cref="PathControlPoint"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public PathControlPoint()
|
public PathControlPoint()
|
||||||
{
|
{
|
||||||
Position.ValueChanged += _ => Changed?.Invoke();
|
|
||||||
Type.ValueChanged += _ => Changed?.Invoke();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -46,10 +69,10 @@ namespace osu.Game.Rulesets.Objects
|
|||||||
public PathControlPoint(Vector2 position, PathType? type = null)
|
public PathControlPoint(Vector2 position, PathType? type = null)
|
||||||
: this()
|
: this()
|
||||||
{
|
{
|
||||||
Position.Value = position;
|
Position = position;
|
||||||
Type.Value = type;
|
Type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Equals(PathControlPoint other) => Position.Value == other?.Position.Value && Type.Value == other.Type.Value;
|
public bool Equals(PathControlPoint other) => Position == other?.Position && Type == other.Type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -169,7 +169,7 @@ namespace osu.Game.Rulesets.Objects
|
|||||||
|
|
||||||
foreach (PathControlPoint point in ControlPoints)
|
foreach (PathControlPoint point in ControlPoints)
|
||||||
{
|
{
|
||||||
if (point.Type.Value != null)
|
if (point.Type != null)
|
||||||
{
|
{
|
||||||
if (!found)
|
if (!found)
|
||||||
pointsInCurrentSegment.Clear();
|
pointsInCurrentSegment.Clear();
|
||||||
@ -215,18 +215,18 @@ namespace osu.Game.Rulesets.Objects
|
|||||||
|
|
||||||
Vector2[] vertices = new Vector2[ControlPoints.Count];
|
Vector2[] vertices = new Vector2[ControlPoints.Count];
|
||||||
for (int i = 0; i < ControlPoints.Count; i++)
|
for (int i = 0; i < ControlPoints.Count; i++)
|
||||||
vertices[i] = ControlPoints[i].Position.Value;
|
vertices[i] = ControlPoints[i].Position;
|
||||||
|
|
||||||
int start = 0;
|
int start = 0;
|
||||||
|
|
||||||
for (int i = 0; i < ControlPoints.Count; i++)
|
for (int i = 0; i < ControlPoints.Count; i++)
|
||||||
{
|
{
|
||||||
if (ControlPoints[i].Type.Value == null && i < ControlPoints.Count - 1)
|
if (ControlPoints[i].Type == null && i < ControlPoints.Count - 1)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// The current vertex ends the segment
|
// The current vertex ends the segment
|
||||||
var segmentVertices = vertices.AsSpan().Slice(start, i - start + 1);
|
var segmentVertices = vertices.AsSpan().Slice(start, i - start + 1);
|
||||||
var segmentType = ControlPoints[start].Type.Value ?? PathType.Linear;
|
var segmentType = ControlPoints[start].Type ?? PathType.Linear;
|
||||||
|
|
||||||
foreach (Vector2 t in calculateSubPath(segmentVertices, segmentType))
|
foreach (Vector2 t in calculateSubPath(segmentVertices, segmentType))
|
||||||
{
|
{
|
||||||
|
@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Objects
|
|||||||
public static void Reverse(this SliderPath sliderPath, out Vector2 positionalOffset)
|
public static void Reverse(this SliderPath sliderPath, out Vector2 positionalOffset)
|
||||||
{
|
{
|
||||||
var points = sliderPath.ControlPoints.ToArray();
|
var points = sliderPath.ControlPoints.ToArray();
|
||||||
positionalOffset = points.Last().Position.Value;
|
positionalOffset = points.Last().Position;
|
||||||
|
|
||||||
sliderPath.ControlPoints.Clear();
|
sliderPath.ControlPoints.Clear();
|
||||||
|
|
||||||
@ -28,17 +28,13 @@ namespace osu.Game.Rulesets.Objects
|
|||||||
for (var i = 0; i < points.Length; i++)
|
for (var i = 0; i < points.Length; i++)
|
||||||
{
|
{
|
||||||
var p = points[i];
|
var p = points[i];
|
||||||
p.Position.Value -= positionalOffset;
|
p.Position -= positionalOffset;
|
||||||
|
|
||||||
// propagate types forwards to last null type
|
// propagate types forwards to last null type
|
||||||
if (i == points.Length - 1)
|
if (i == points.Length - 1)
|
||||||
p.Type.Value = lastType;
|
p.Type = lastType;
|
||||||
else if (p.Type.Value != null)
|
else if (p.Type != null)
|
||||||
{
|
(p.Type, lastType) = (lastType, p.Type);
|
||||||
var newType = p.Type.Value;
|
|
||||||
p.Type.Value = lastType;
|
|
||||||
lastType = newType;
|
|
||||||
}
|
|
||||||
|
|
||||||
sliderPath.ControlPoints.Insert(0, p);
|
sliderPath.ControlPoints.Insert(0, p);
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Scoring
|
namespace osu.Game.Rulesets.Scoring
|
||||||
@ -171,6 +173,11 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static bool IsScorable(this HitResult result) => result >= HitResult.Miss && result < HitResult.IgnoreMiss;
|
public static bool IsScorable(this HitResult result) => result >= HitResult.Miss && result < HitResult.IgnoreMiss;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An array of all scorable <see cref="HitResult"/>s.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly HitResult[] SCORABLE_TYPES = ((HitResult[])Enum.GetValues(typeof(HitResult))).Where(r => r.IsScorable()).ToArray();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether a <see cref="HitResult"/> is valid within a given <see cref="HitResult"/> range.
|
/// Whether a <see cref="HitResult"/> is valid within a given <see cref="HitResult"/> range.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -339,7 +339,7 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
score.Accuracy = Accuracy.Value;
|
score.Accuracy = Accuracy.Value;
|
||||||
score.Rank = Rank.Value;
|
score.Rank = Rank.Value;
|
||||||
|
|
||||||
foreach (var result in Enum.GetValues(typeof(HitResult)).OfType<HitResult>().Where(r => r.IsScorable()))
|
foreach (var result in HitResultExtensions.SCORABLE_TYPES)
|
||||||
score.Statistics[result] = GetStatistic(result);
|
score.Statistics[result] = GetStatistic(result);
|
||||||
|
|
||||||
score.HitEvents = hitEvents;
|
score.HitEvents = hitEvents;
|
||||||
|
@ -16,7 +16,7 @@ namespace osu.Game.Screens.Edit
|
|||||||
private readonly EditorBeatmapSkin? beatmapSkin;
|
private readonly EditorBeatmapSkin? beatmapSkin;
|
||||||
|
|
||||||
public EditorSkinProvidingContainer(EditorBeatmap editorBeatmap)
|
public EditorSkinProvidingContainer(EditorBeatmap editorBeatmap)
|
||||||
: base(editorBeatmap.PlayableBeatmap.BeatmapInfo.Ruleset.CreateInstance(), editorBeatmap, editorBeatmap.BeatmapSkin)
|
: base(editorBeatmap.PlayableBeatmap.BeatmapInfo.Ruleset.CreateInstance(), editorBeatmap.PlayableBeatmap, editorBeatmap.BeatmapSkin)
|
||||||
{
|
{
|
||||||
beatmapSkin = editorBeatmap.BeatmapSkin;
|
beatmapSkin = editorBeatmap.BeatmapSkin;
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ using osu.Framework.Graphics.Shapes;
|
|||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Online.Rooms;
|
using osu.Game.Online.Rooms;
|
||||||
using osu.Game.Screens.OnlinePlay.Components;
|
using osu.Game.Screens.OnlinePlay.Components;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -75,6 +76,7 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
new HoverClickSounds(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ using osu.Framework.Audio;
|
|||||||
using osu.Framework.Audio.Sample;
|
using osu.Framework.Audio.Sample;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Threading;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Backgrounds;
|
using osu.Game.Graphics.Backgrounds;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
@ -35,12 +36,16 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
|||||||
|
|
||||||
private IBindable<bool> operationInProgress;
|
private IBindable<bool> operationInProgress;
|
||||||
|
|
||||||
private Sample sampleReadyCount;
|
private Sample sampleReady;
|
||||||
|
private Sample sampleReadyAll;
|
||||||
|
private Sample sampleUnready;
|
||||||
|
|
||||||
private readonly ButtonWithTrianglesExposed button;
|
private readonly ButtonWithTrianglesExposed button;
|
||||||
|
|
||||||
private int countReady;
|
private int countReady;
|
||||||
|
|
||||||
|
private ScheduledDelegate readySampleDelegate;
|
||||||
|
|
||||||
public MultiplayerReadyButton()
|
public MultiplayerReadyButton()
|
||||||
{
|
{
|
||||||
InternalChild = button = new ButtonWithTrianglesExposed
|
InternalChild = button = new ButtonWithTrianglesExposed
|
||||||
@ -54,10 +59,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(AudioManager audio)
|
private void load(AudioManager audio)
|
||||||
{
|
{
|
||||||
sampleReadyCount = audio.Samples.Get(@"SongSelect/select-difficulty");
|
|
||||||
|
|
||||||
operationInProgress = ongoingOperationTracker.InProgress.GetBoundCopy();
|
operationInProgress = ongoingOperationTracker.InProgress.GetBoundCopy();
|
||||||
operationInProgress.BindValueChanged(_ => updateState());
|
operationInProgress.BindValueChanged(_ => updateState());
|
||||||
|
|
||||||
|
sampleReady = audio.Samples.Get(@"Multiplayer/player-ready");
|
||||||
|
sampleReadyAll = audio.Samples.Get(@"Multiplayer/player-ready-all");
|
||||||
|
sampleUnready = audio.Samples.Get(@"Multiplayer/player-unready");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnRoomUpdated()
|
protected override void OnRoomUpdated()
|
||||||
@ -107,21 +114,26 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
|||||||
|
|
||||||
button.Enabled.Value = enableButton;
|
button.Enabled.Value = enableButton;
|
||||||
|
|
||||||
if (newCountReady != countReady)
|
if (newCountReady == countReady)
|
||||||
{
|
|
||||||
countReady = newCountReady;
|
|
||||||
Scheduler.AddOnce(playSound);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void playSound()
|
|
||||||
{
|
|
||||||
if (sampleReadyCount == null)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var channel = sampleReadyCount.GetChannel();
|
readySampleDelegate?.Cancel();
|
||||||
channel.Frequency.Value = 0.77f + countReady * 0.06f;
|
readySampleDelegate = Schedule(() =>
|
||||||
channel.Play();
|
{
|
||||||
|
if (newCountReady > countReady)
|
||||||
|
{
|
||||||
|
if (newCountReady == newCountTotal)
|
||||||
|
sampleReadyAll?.Play();
|
||||||
|
else
|
||||||
|
sampleReady?.Play();
|
||||||
|
}
|
||||||
|
else if (newCountReady < countReady)
|
||||||
|
{
|
||||||
|
sampleUnready?.Play();
|
||||||
|
}
|
||||||
|
|
||||||
|
countReady = newCountReady;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateButtonColour(bool green)
|
private void updateButtonColour(bool green)
|
||||||
|
@ -3,10 +3,13 @@
|
|||||||
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Audio;
|
||||||
|
using osu.Framework.Audio.Sample;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.Cursor;
|
using osu.Game.Graphics.Cursor;
|
||||||
|
using osu.Game.Online.Multiplayer;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants
|
namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants
|
||||||
@ -15,8 +18,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants
|
|||||||
{
|
{
|
||||||
private FillFlowContainer<ParticipantPanel> panels;
|
private FillFlowContainer<ParticipantPanel> panels;
|
||||||
|
|
||||||
|
private Sample userJoinSample;
|
||||||
|
private Sample userLeftSample;
|
||||||
|
private Sample userKickedSample;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load(AudioManager audio)
|
||||||
{
|
{
|
||||||
InternalChild = new OsuContextMenuContainer
|
InternalChild = new OsuContextMenuContainer
|
||||||
{
|
{
|
||||||
@ -34,6 +41,31 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
userJoinSample = audio.Samples.Get(@"Multiplayer/player-joined");
|
||||||
|
userLeftSample = audio.Samples.Get(@"Multiplayer/player-left");
|
||||||
|
userKickedSample = audio.Samples.Get(@"Multiplayer/player-kicked");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UserJoined(MultiplayerRoomUser user)
|
||||||
|
{
|
||||||
|
base.UserJoined(user);
|
||||||
|
|
||||||
|
userJoinSample?.Play();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UserLeft(MultiplayerRoomUser user)
|
||||||
|
{
|
||||||
|
base.UserLeft(user);
|
||||||
|
|
||||||
|
userLeftSample?.Play();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UserKicked(MultiplayerRoomUser user)
|
||||||
|
{
|
||||||
|
base.UserKicked(user);
|
||||||
|
|
||||||
|
userKickedSample?.Play();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnRoomUpdated()
|
protected override void OnRoomUpdated()
|
||||||
|
@ -12,7 +12,6 @@ using osu.Framework.Graphics.Shapes;
|
|||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Framework.Threading;
|
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
@ -37,6 +36,8 @@ namespace osu.Game.Screens.Play
|
|||||||
private FadeContainer fadeContainer;
|
private FadeContainer fadeContainer;
|
||||||
private double displayTime;
|
private double displayTime;
|
||||||
|
|
||||||
|
private bool isClickable;
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private GameplayClock gameplayClock { get; set; }
|
private GameplayClock gameplayClock { get; set; }
|
||||||
|
|
||||||
@ -101,7 +102,7 @@ namespace osu.Game.Screens.Play
|
|||||||
public override void Show()
|
public override void Show()
|
||||||
{
|
{
|
||||||
base.Show();
|
base.Show();
|
||||||
fadeContainer.Show();
|
fadeContainer.TriggerShow();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
@ -118,24 +119,27 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
button.Action = () => RequestSkip?.Invoke();
|
button.Action = () => RequestSkip?.Invoke();
|
||||||
displayTime = gameplayClock.CurrentTime;
|
displayTime = gameplayClock.CurrentTime;
|
||||||
|
|
||||||
|
fadeContainer.TriggerShow();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
var progress = fadeOutBeginTime <= displayTime ? 1 : Math.Max(0, 1 - (gameplayClock.CurrentTime - displayTime) / (fadeOutBeginTime - displayTime));
|
double progress = fadeOutBeginTime <= displayTime ? 1 : Math.Max(0, 1 - (gameplayClock.CurrentTime - displayTime) / (fadeOutBeginTime - displayTime));
|
||||||
|
|
||||||
remainingTimeBox.Width = (float)Interpolation.Lerp(remainingTimeBox.Width, progress, Math.Clamp(Time.Elapsed / 40, 0, 1));
|
remainingTimeBox.Width = (float)Interpolation.Lerp(remainingTimeBox.Width, progress, Math.Clamp(Time.Elapsed / 40, 0, 1));
|
||||||
|
|
||||||
button.Enabled.Value = progress > 0;
|
isClickable = progress > 0;
|
||||||
buttonContainer.State.Value = progress > 0 ? Visibility.Visible : Visibility.Hidden;
|
button.Enabled.Value = isClickable;
|
||||||
|
buttonContainer.State.Value = isClickable ? Visibility.Visible : Visibility.Hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnMouseMove(MouseMoveEvent e)
|
protected override bool OnMouseMove(MouseMoveEvent e)
|
||||||
{
|
{
|
||||||
if (!e.HasAnyButtonPressed)
|
if (isClickable && !e.HasAnyButtonPressed)
|
||||||
fadeContainer.Show();
|
fadeContainer.TriggerShow();
|
||||||
|
|
||||||
return base.OnMouseMove(e);
|
return base.OnMouseMove(e);
|
||||||
}
|
}
|
||||||
@ -164,34 +168,45 @@ namespace osu.Game.Screens.Play
|
|||||||
public event Action<Visibility> StateChanged;
|
public event Action<Visibility> StateChanged;
|
||||||
|
|
||||||
private Visibility state;
|
private Visibility state;
|
||||||
private ScheduledDelegate scheduledHide;
|
private double? nextHideTime;
|
||||||
|
|
||||||
public override bool IsPresent => true;
|
public override bool IsPresent => true;
|
||||||
|
|
||||||
|
public void TriggerShow()
|
||||||
|
{
|
||||||
|
Show();
|
||||||
|
|
||||||
|
if (!IsHovered && !IsDragged)
|
||||||
|
nextHideTime = Time.Current + 1000;
|
||||||
|
else
|
||||||
|
nextHideTime = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
if (nextHideTime != null && nextHideTime <= Time.Current)
|
||||||
|
{
|
||||||
|
Hide();
|
||||||
|
nextHideTime = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Visibility State
|
public Visibility State
|
||||||
{
|
{
|
||||||
get => state;
|
get => state;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
bool stateChanged = value != state;
|
if (value == state)
|
||||||
|
return;
|
||||||
|
|
||||||
state = value;
|
state = value;
|
||||||
|
|
||||||
scheduledHide?.Cancel();
|
|
||||||
|
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case Visibility.Visible:
|
case Visibility.Visible:
|
||||||
// we may be triggered to become visible multiple times but we only want to transform once.
|
this.FadeIn(500, Easing.OutExpo);
|
||||||
if (stateChanged)
|
|
||||||
this.FadeIn(500, Easing.OutExpo);
|
|
||||||
|
|
||||||
if (!IsHovered && !IsDragged)
|
|
||||||
{
|
|
||||||
using (BeginDelayedSequence(1000))
|
|
||||||
scheduledHide = Schedule(Hide);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Visibility.Hidden:
|
case Visibility.Hidden:
|
||||||
@ -212,7 +227,7 @@ namespace osu.Game.Screens.Play
|
|||||||
protected override bool OnMouseDown(MouseDownEvent e)
|
protected override bool OnMouseDown(MouseDownEvent e)
|
||||||
{
|
{
|
||||||
Show();
|
Show();
|
||||||
scheduledHide?.Cancel();
|
nextHideTime = null;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Realm" Version="10.3.0" />
|
<PackageReference Include="Realm" Version="10.3.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2021.819.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2021.819.0" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.822.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.827.0" />
|
||||||
<PackageReference Include="Sentry" Version="3.8.3" />
|
<PackageReference Include="Sentry" Version="3.8.3" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.28.3" />
|
<PackageReference Include="SharpCompress" Version="0.28.3" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
|
@ -71,7 +71,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2021.819.0" />
|
<PackageReference Include="ppy.osu.Framework.iOS" Version="2021.819.0" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.822.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.827.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net5.0 / net6.0) -->
|
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net5.0 / net6.0) -->
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
Loading…
Reference in New Issue
Block a user