mirror of
https://github.com/ppy/osu.git
synced 2025-01-28 06:42:54 +08:00
Merge branch 'master' into multiplayer-spectator-leaderboard
This commit is contained in:
commit
27660265b5
@ -51,7 +51,7 @@
|
||||
<Reference Include="Java.Interop" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.211.1" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.407.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.410.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.410.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
@ -69,7 +69,6 @@ namespace osu.Desktop
|
||||
/// Allow a maximum of one unhandled exception, per second of execution.
|
||||
/// </summary>
|
||||
/// <param name="arg"></param>
|
||||
/// <returns></returns>
|
||||
private static bool handleException(Exception arg)
|
||||
{
|
||||
bool continueExecution = Interlocked.Decrement(ref allowableExceptions) >= 0;
|
||||
|
53
osu.Game.Rulesets.Catch.Tests/TestSceneCatchReplay.cs
Normal file
53
osu.Game.Rulesets.Catch.Tests/TestSceneCatchReplay.cs
Normal file
@ -0,0 +1,53 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
public class TestSceneCatchReplay : TestSceneCatchPlayer
|
||||
{
|
||||
protected override bool Autoplay => true;
|
||||
|
||||
private const int object_count = 10;
|
||||
|
||||
[Test]
|
||||
public void TestReplayCatcherPositionIsFramePerfect()
|
||||
{
|
||||
AddUntilStep("caught all fruits", () => Player.ScoreProcessor.Combo.Value == object_count);
|
||||
}
|
||||
|
||||
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
|
||||
{
|
||||
var beatmap = new Beatmap
|
||||
{
|
||||
BeatmapInfo =
|
||||
{
|
||||
Ruleset = ruleset,
|
||||
}
|
||||
};
|
||||
|
||||
beatmap.ControlPointInfo.Add(0, new TimingControlPoint());
|
||||
|
||||
for (int i = 0; i < object_count / 2; i++)
|
||||
{
|
||||
beatmap.HitObjects.Add(new Fruit
|
||||
{
|
||||
StartTime = (i + 1) * 1000,
|
||||
X = 0
|
||||
});
|
||||
beatmap.HitObjects.Add(new Fruit
|
||||
{
|
||||
StartTime = (i + 1) * 1000 + 1,
|
||||
X = CatchPlayfield.WIDTH
|
||||
});
|
||||
}
|
||||
|
||||
return beatmap;
|
||||
}
|
||||
}
|
||||
}
|
@ -51,8 +51,11 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
{
|
||||
droppedObjectContainer,
|
||||
CatcherArea.MovableCatcher.CreateProxiedContent(),
|
||||
HitObjectContainer,
|
||||
HitObjectContainer.CreateProxy(),
|
||||
// This ordering (`CatcherArea` before `HitObjectContainer`) is important to
|
||||
// make sure the up-to-date catcher position is used for the catcher catching logic of hit objects.
|
||||
CatcherArea,
|
||||
HitObjectContainer,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -482,7 +482,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
/// Retrieves the sample info list at a point in time.
|
||||
/// </summary>
|
||||
/// <param name="time">The time to retrieve the sample info list from.</param>
|
||||
/// <returns></returns>
|
||||
private IList<HitSampleInfo> sampleInfoListAt(int time) => nodeSamplesAt(time)?.First() ?? HitObject.Samples;
|
||||
|
||||
/// <summary>
|
||||
|
@ -4,9 +4,11 @@
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Tests.Visual;
|
||||
@ -14,7 +16,7 @@ using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
{
|
||||
public class TestScenePathControlPointVisualiser : OsuTestScene
|
||||
public class TestScenePathControlPointVisualiser : OsuManualInputManagerTestScene
|
||||
{
|
||||
private Slider slider;
|
||||
private PathControlPointVisualiser visualiser;
|
||||
@ -43,12 +45,145 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPerfectCurveTooManyPoints()
|
||||
{
|
||||
createVisualiser(true);
|
||||
|
||||
addControlPointStep(new Vector2(200), PathType.Bezier);
|
||||
addControlPointStep(new Vector2(300));
|
||||
addControlPointStep(new Vector2(500, 300));
|
||||
addControlPointStep(new Vector2(700, 200));
|
||||
addControlPointStep(new Vector2(500, 100));
|
||||
|
||||
// Must be both hovering and selecting the control point for the context menu to work.
|
||||
moveMouseToControlPoint(1);
|
||||
AddStep("select control point", () => visualiser.Pieces[1].IsSelected.Value = true);
|
||||
addContextMenuItemStep("Perfect curve");
|
||||
|
||||
assertControlPointPathType(0, PathType.Bezier);
|
||||
assertControlPointPathType(1, PathType.PerfectCurve);
|
||||
assertControlPointPathType(3, PathType.Bezier);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPerfectCurveLastThreePoints()
|
||||
{
|
||||
createVisualiser(true);
|
||||
|
||||
addControlPointStep(new Vector2(200), PathType.Bezier);
|
||||
addControlPointStep(new Vector2(300));
|
||||
addControlPointStep(new Vector2(500, 300));
|
||||
addControlPointStep(new Vector2(700, 200));
|
||||
addControlPointStep(new Vector2(500, 100));
|
||||
|
||||
moveMouseToControlPoint(2);
|
||||
AddStep("select control point", () => visualiser.Pieces[2].IsSelected.Value = true);
|
||||
addContextMenuItemStep("Perfect curve");
|
||||
|
||||
assertControlPointPathType(0, PathType.Bezier);
|
||||
assertControlPointPathType(2, PathType.PerfectCurve);
|
||||
assertControlPointPathType(4, null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPerfectCurveLastTwoPoints()
|
||||
{
|
||||
createVisualiser(true);
|
||||
|
||||
addControlPointStep(new Vector2(200), PathType.Bezier);
|
||||
addControlPointStep(new Vector2(300));
|
||||
addControlPointStep(new Vector2(500, 300));
|
||||
addControlPointStep(new Vector2(700, 200));
|
||||
addControlPointStep(new Vector2(500, 100));
|
||||
|
||||
moveMouseToControlPoint(3);
|
||||
AddStep("select control point", () => visualiser.Pieces[3].IsSelected.Value = true);
|
||||
addContextMenuItemStep("Perfect curve");
|
||||
|
||||
assertControlPointPathType(0, PathType.Bezier);
|
||||
AddAssert("point 3 is not inherited", () => slider.Path.ControlPoints[3].Type != null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPerfectCurveTooManyPointsLinear()
|
||||
{
|
||||
createVisualiser(true);
|
||||
|
||||
addControlPointStep(new Vector2(200), PathType.Linear);
|
||||
addControlPointStep(new Vector2(300));
|
||||
addControlPointStep(new Vector2(500, 300));
|
||||
addControlPointStep(new Vector2(700, 200));
|
||||
addControlPointStep(new Vector2(500, 100));
|
||||
|
||||
// Must be both hovering and selecting the control point for the context menu to work.
|
||||
moveMouseToControlPoint(1);
|
||||
AddStep("select control point", () => visualiser.Pieces[1].IsSelected.Value = true);
|
||||
addContextMenuItemStep("Perfect curve");
|
||||
|
||||
assertControlPointPathType(0, PathType.Linear);
|
||||
assertControlPointPathType(1, PathType.PerfectCurve);
|
||||
assertControlPointPathType(3, PathType.Linear);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPerfectCurveChangeToBezier()
|
||||
{
|
||||
createVisualiser(true);
|
||||
|
||||
addControlPointStep(new Vector2(200), PathType.Bezier);
|
||||
addControlPointStep(new Vector2(300), PathType.PerfectCurve);
|
||||
addControlPointStep(new Vector2(500, 300));
|
||||
addControlPointStep(new Vector2(700, 200), PathType.Bezier);
|
||||
addControlPointStep(new Vector2(500, 100));
|
||||
|
||||
moveMouseToControlPoint(3);
|
||||
AddStep("select control point", () => visualiser.Pieces[3].IsSelected.Value = true);
|
||||
addContextMenuItemStep("Inherit");
|
||||
|
||||
assertControlPointPathType(0, PathType.Bezier);
|
||||
assertControlPointPathType(1, PathType.Bezier);
|
||||
assertControlPointPathType(3, null);
|
||||
}
|
||||
|
||||
private void createVisualiser(bool allowSelection) => AddStep("create visualiser", () => Child = visualiser = new PathControlPointVisualiser(slider, allowSelection)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre
|
||||
});
|
||||
|
||||
private void addControlPointStep(Vector2 position) => AddStep($"add control point {position}", () => slider.Path.ControlPoints.Add(new PathControlPoint(position)));
|
||||
private void addControlPointStep(Vector2 position) => addControlPointStep(position, null);
|
||||
|
||||
private void addControlPointStep(Vector2 position, PathType? type)
|
||||
{
|
||||
AddStep($"add {type} control point at {position}", () =>
|
||||
{
|
||||
slider.Path.ControlPoints.Add(new PathControlPoint(position, type));
|
||||
});
|
||||
}
|
||||
|
||||
private void moveMouseToControlPoint(int index)
|
||||
{
|
||||
AddStep($"move mouse to control point {index}", () =>
|
||||
{
|
||||
Vector2 position = slider.Path.ControlPoints[index].Position.Value;
|
||||
InputManager.MoveMouseTo(visualiser.Pieces[0].Parent.ToScreenSpace(position));
|
||||
});
|
||||
}
|
||||
|
||||
private void assertControlPointPathType(int controlPointIndex, PathType? type)
|
||||
{
|
||||
AddAssert($"point {controlPointIndex} is {type}", () => slider.Path.ControlPoints[controlPointIndex].Type.Value == type);
|
||||
}
|
||||
|
||||
private void addContextMenuItemStep(string contextMenuText)
|
||||
{
|
||||
AddStep($"click context menu item \"{contextMenuText}\"", () =>
|
||||
{
|
||||
MenuItem item = visualiser.ContextMenuItems[1].Items.FirstOrDefault(menuItem => menuItem.Text.Value == contextMenuText);
|
||||
|
||||
item?.Action?.Value();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -213,10 +213,13 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
if (ControlPoint.Type.Value != PathType.PerfectCurve)
|
||||
return;
|
||||
|
||||
ReadOnlySpan<Vector2> points = PointsInSegment.Select(p => p.Position.Value).ToArray();
|
||||
if (points.Length != 3)
|
||||
if (PointsInSegment.Count > 3)
|
||||
ControlPoint.Type.Value = PathType.Bezier;
|
||||
|
||||
if (PointsInSegment.Count != 3)
|
||||
return;
|
||||
|
||||
ReadOnlySpan<Vector2> points = PointsInSegment.Select(p => p.Position.Value).ToArray();
|
||||
RectangleF boundingBox = PathApproximator.CircularArcBoundingBox(points);
|
||||
if (boundingBox.Width >= 640 || boundingBox.Height >= 480)
|
||||
ControlPoint.Type.Value = PathType.Bezier;
|
||||
|
@ -153,6 +153,34 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to set the given control point piece to the given path type.
|
||||
/// If that would fail, try to change the path such that it instead succeeds
|
||||
/// in a UX-friendly way.
|
||||
/// </summary>
|
||||
/// <param name="piece">The control point piece that we want to change the path type of.</param>
|
||||
/// <param name="type">The path type we want to assign to the given control point piece.</param>
|
||||
private void updatePathType(PathControlPointPiece piece, PathType? type)
|
||||
{
|
||||
int indexInSegment = piece.PointsInSegment.IndexOf(piece.ControlPoint);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case PathType.PerfectCurve:
|
||||
// Can't always create a circular arc out of 4 or more points,
|
||||
// so we split the segment into one 3-point circular arc segment
|
||||
// and one segment of the previous type.
|
||||
int thirdPointIndex = indexInSegment + 2;
|
||||
|
||||
if (piece.PointsInSegment.Count > thirdPointIndex + 1)
|
||||
piece.PointsInSegment[thirdPointIndex].Type.Value = piece.PointsInSegment[0].Type.Value;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
piece.ControlPoint.Type.Value = type;
|
||||
}
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
private IEditorChangeHandler changeHandler { get; set; }
|
||||
|
||||
@ -218,7 +246,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
var item = new PathTypeMenuItem(type, () =>
|
||||
{
|
||||
foreach (var p in Pieces.Where(p => p.IsSelected.Value))
|
||||
p.ControlPoint.Type.Value = type;
|
||||
updatePathType(p, type);
|
||||
});
|
||||
|
||||
if (countOfState == totalCount)
|
||||
|
@ -9,10 +9,12 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Mods
|
||||
@ -23,6 +25,8 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
|
||||
private const float default_flashlight_size = 180;
|
||||
|
||||
private const double default_follow_delay = 120;
|
||||
|
||||
private OsuFlashlight flashlight;
|
||||
|
||||
public override Flashlight CreateFlashlight() => flashlight = new OsuFlashlight();
|
||||
@ -35,8 +39,25 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
}
|
||||
}
|
||||
|
||||
public override void ApplyToDrawableRuleset(DrawableRuleset<OsuHitObject> drawableRuleset)
|
||||
{
|
||||
base.ApplyToDrawableRuleset(drawableRuleset);
|
||||
|
||||
flashlight.FollowDelay = FollowDelay.Value;
|
||||
}
|
||||
|
||||
[SettingSource("Follow delay", "Milliseconds until the flashlight reaches the cursor")]
|
||||
public BindableNumber<double> FollowDelay { get; } = new BindableDouble(default_follow_delay)
|
||||
{
|
||||
MinValue = default_follow_delay,
|
||||
MaxValue = default_follow_delay * 10,
|
||||
Precision = default_follow_delay,
|
||||
};
|
||||
|
||||
private class OsuFlashlight : Flashlight, IRequireHighFrequencyMousePosition
|
||||
{
|
||||
public double FollowDelay { private get; set; }
|
||||
|
||||
public OsuFlashlight()
|
||||
{
|
||||
FlashlightSize = new Vector2(0, getSizeFor(0));
|
||||
@ -50,13 +71,11 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
|
||||
protected override bool OnMouseMove(MouseMoveEvent e)
|
||||
{
|
||||
const double follow_delay = 120;
|
||||
|
||||
var position = FlashlightPosition;
|
||||
var destination = e.MousePosition;
|
||||
|
||||
FlashlightPosition = Interpolation.ValueAt(
|
||||
Math.Min(Math.Abs(Clock.ElapsedFrameTime), follow_delay), position, destination, 0, follow_delay, Easing.Out);
|
||||
Math.Min(Math.Abs(Clock.ElapsedFrameTime), FollowDelay), position, destination, 0, FollowDelay, Easing.Out);
|
||||
|
||||
return base.OnMouseMove(e);
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using osuTK;
|
||||
@ -108,16 +109,27 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
protected override void LoadSamples()
|
||||
{
|
||||
base.LoadSamples();
|
||||
// Note: base.LoadSamples() isn't called since the slider plays the tail's hitsounds for the time being.
|
||||
|
||||
var firstSample = HitObject.Samples.FirstOrDefault();
|
||||
|
||||
if (firstSample != null)
|
||||
if (HitObject.SampleControlPoint == null)
|
||||
{
|
||||
var clone = HitObject.SampleControlPoint.ApplyTo(firstSample).With("sliderslide");
|
||||
|
||||
slidingSample.Samples = new ISampleInfo[] { clone };
|
||||
throw new InvalidOperationException($"{nameof(HitObject)}s must always have an attached {nameof(HitObject.SampleControlPoint)}."
|
||||
+ $" This is an indication that {nameof(HitObject.ApplyDefaults)} has not been invoked on {this}.");
|
||||
}
|
||||
|
||||
Samples.Samples = HitObject.TailSamples.Select(s => HitObject.SampleControlPoint.ApplyTo(s)).Cast<ISampleInfo>().ToArray();
|
||||
|
||||
var slidingSamples = new List<ISampleInfo>();
|
||||
|
||||
var normalSample = HitObject.Samples.FirstOrDefault(s => s.Name == HitSampleInfo.HIT_NORMAL);
|
||||
if (normalSample != null)
|
||||
slidingSamples.Add(HitObject.SampleControlPoint.ApplyTo(normalSample).With("sliderslide"));
|
||||
|
||||
var whistleSample = HitObject.Samples.FirstOrDefault(s => s.Name == HitSampleInfo.HIT_WHISTLE);
|
||||
if (whistleSample != null)
|
||||
slidingSamples.Add(HitObject.SampleControlPoint.ApplyTo(whistleSample).With("sliderwhistle"));
|
||||
|
||||
slidingSample.Samples = slidingSamples.ToArray();
|
||||
}
|
||||
|
||||
public override void StopAllSamples()
|
||||
|
@ -81,6 +81,9 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
|
||||
public List<IList<HitSampleInfo>> NodeSamples { get; set; } = new List<IList<HitSampleInfo>>();
|
||||
|
||||
[JsonIgnore]
|
||||
public IList<HitSampleInfo> TailSamples { get; private set; }
|
||||
|
||||
private int repeatCount;
|
||||
|
||||
public int RepeatCount
|
||||
@ -143,11 +146,6 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
|
||||
Velocity = scoringDistance / timingPoint.BeatLength;
|
||||
TickDistance = scoringDistance / difficulty.SliderTickRate * TickDistanceMultiplier;
|
||||
|
||||
// The samples should be attached to the slider tail, however this can only be done after LegacyLastTick is removed otherwise they would play earlier than they're intended to.
|
||||
// For now, the samples are attached to and played by the slider itself at the correct end time.
|
||||
// ToArray call is required as GetNodeSamples may fallback to Samples itself (without it it will get cleared due to the list reference being live).
|
||||
Samples = this.GetNodeSamples(repeatCount + 1).ToArray();
|
||||
}
|
||||
|
||||
protected override void CreateNestedHitObjects(CancellationToken cancellationToken)
|
||||
@ -238,6 +236,10 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
|
||||
if (HeadCircle != null)
|
||||
HeadCircle.Samples = this.GetNodeSamples(0);
|
||||
|
||||
// The samples should be attached to the slider tail, however this can only be done after LegacyLastTick is removed otherwise they would play earlier than they're intended to.
|
||||
// For now, the samples are played by the slider itself at the correct end time.
|
||||
TailSamples = this.GetNodeSamples(repeatCount + 1);
|
||||
}
|
||||
|
||||
public override Judgement CreateJudgement() => OnlyJudgeNestedObjects ? new OsuIgnoreJudgement() : new OsuJudgement();
|
||||
|
@ -1,115 +0,0 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Game.Rulesets.Difficulty.Utils;
|
||||
|
||||
namespace osu.Game.Tests.NonVisual
|
||||
{
|
||||
[TestFixture]
|
||||
public class LimitedCapacityStackTest
|
||||
{
|
||||
private const int capacity = 3;
|
||||
|
||||
private LimitedCapacityStack<int> stack;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
stack = new LimitedCapacityStack<int>(capacity);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestEmptyStack()
|
||||
{
|
||||
Assert.AreEqual(0, stack.Count);
|
||||
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() =>
|
||||
{
|
||||
int unused = stack[0];
|
||||
});
|
||||
|
||||
int count = 0;
|
||||
foreach (var unused in stack)
|
||||
count++;
|
||||
|
||||
Assert.AreEqual(0, count);
|
||||
}
|
||||
|
||||
[TestCase(1)]
|
||||
[TestCase(2)]
|
||||
[TestCase(3)]
|
||||
public void TestInRangeElements(int count)
|
||||
{
|
||||
// e.g. 0 -> 1 -> 2
|
||||
for (int i = 0; i < count; i++)
|
||||
stack.Push(i);
|
||||
|
||||
Assert.AreEqual(count, stack.Count);
|
||||
|
||||
// e.g. 2 -> 1 -> 0 (reverse order)
|
||||
for (int i = 0; i < stack.Count; i++)
|
||||
Assert.AreEqual(count - 1 - i, stack[i]);
|
||||
|
||||
// e.g. indices 3, 4, 5, 6 (out of range)
|
||||
for (int i = stack.Count; i < stack.Count + capacity; i++)
|
||||
{
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() =>
|
||||
{
|
||||
int unused = stack[i];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase(4)]
|
||||
[TestCase(5)]
|
||||
[TestCase(6)]
|
||||
public void TestOverflowElements(int count)
|
||||
{
|
||||
// e.g. 0 -> 1 -> 2 -> 3
|
||||
for (int i = 0; i < count; i++)
|
||||
stack.Push(i);
|
||||
|
||||
Assert.AreEqual(capacity, stack.Count);
|
||||
|
||||
// e.g. 3 -> 2 -> 1 (reverse order)
|
||||
for (int i = 0; i < stack.Count; i++)
|
||||
Assert.AreEqual(count - 1 - i, stack[i]);
|
||||
|
||||
// e.g. indices 3, 4, 5, 6 (out of range)
|
||||
for (int i = stack.Count; i < stack.Count + capacity; i++)
|
||||
{
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() =>
|
||||
{
|
||||
int unused = stack[i];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase(1)]
|
||||
[TestCase(2)]
|
||||
[TestCase(3)]
|
||||
[TestCase(4)]
|
||||
[TestCase(5)]
|
||||
[TestCase(6)]
|
||||
public void TestEnumerator(int count)
|
||||
{
|
||||
// e.g. 0 -> 1 -> 2 -> 3
|
||||
for (int i = 0; i < count; i++)
|
||||
stack.Push(i);
|
||||
|
||||
int enumeratorCount = 0;
|
||||
int expectedValue = count - 1;
|
||||
|
||||
foreach (var item in stack)
|
||||
{
|
||||
Assert.AreEqual(expectedValue, item);
|
||||
enumeratorCount++;
|
||||
expectedValue--;
|
||||
}
|
||||
|
||||
Assert.AreEqual(stack.Count, enumeratorCount);
|
||||
}
|
||||
}
|
||||
}
|
143
osu.Game.Tests/NonVisual/ReverseQueueTest.cs
Normal file
143
osu.Game.Tests/NonVisual/ReverseQueueTest.cs
Normal file
@ -0,0 +1,143 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Game.Rulesets.Difficulty.Utils;
|
||||
|
||||
namespace osu.Game.Tests.NonVisual
|
||||
{
|
||||
[TestFixture]
|
||||
public class ReverseQueueTest
|
||||
{
|
||||
private ReverseQueue<char> queue;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
queue = new ReverseQueue<char>(4);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestEmptyQueue()
|
||||
{
|
||||
Assert.AreEqual(0, queue.Count);
|
||||
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() =>
|
||||
{
|
||||
char unused = queue[0];
|
||||
});
|
||||
|
||||
int count = 0;
|
||||
foreach (var unused in queue)
|
||||
count++;
|
||||
|
||||
Assert.AreEqual(0, count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestEnqueue()
|
||||
{
|
||||
// Assert correct values and reverse index after enqueueing
|
||||
queue.Enqueue('a');
|
||||
queue.Enqueue('b');
|
||||
queue.Enqueue('c');
|
||||
|
||||
Assert.AreEqual('c', queue[0]);
|
||||
Assert.AreEqual('b', queue[1]);
|
||||
Assert.AreEqual('a', queue[2]);
|
||||
|
||||
// Assert correct values and reverse index after enqueueing beyond initial capacity of 4
|
||||
queue.Enqueue('d');
|
||||
queue.Enqueue('e');
|
||||
queue.Enqueue('f');
|
||||
|
||||
Assert.AreEqual('f', queue[0]);
|
||||
Assert.AreEqual('e', queue[1]);
|
||||
Assert.AreEqual('d', queue[2]);
|
||||
Assert.AreEqual('c', queue[3]);
|
||||
Assert.AreEqual('b', queue[4]);
|
||||
Assert.AreEqual('a', queue[5]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDequeue()
|
||||
{
|
||||
queue.Enqueue('a');
|
||||
queue.Enqueue('b');
|
||||
queue.Enqueue('c');
|
||||
queue.Enqueue('d');
|
||||
queue.Enqueue('e');
|
||||
queue.Enqueue('f');
|
||||
|
||||
// Assert correct item return and no longer in queue after dequeueing
|
||||
Assert.AreEqual('a', queue[5]);
|
||||
var dequeuedItem = queue.Dequeue();
|
||||
|
||||
Assert.AreEqual('a', dequeuedItem);
|
||||
Assert.AreEqual(5, queue.Count);
|
||||
Assert.AreEqual('f', queue[0]);
|
||||
Assert.AreEqual('b', queue[4]);
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() =>
|
||||
{
|
||||
char unused = queue[5];
|
||||
});
|
||||
|
||||
// Assert correct state after enough enqueues and dequeues to wrap around array (queue.start = 0 again)
|
||||
queue.Enqueue('g');
|
||||
queue.Enqueue('h');
|
||||
queue.Enqueue('i');
|
||||
queue.Dequeue();
|
||||
queue.Dequeue();
|
||||
queue.Dequeue();
|
||||
queue.Dequeue();
|
||||
queue.Dequeue();
|
||||
queue.Dequeue();
|
||||
queue.Dequeue();
|
||||
|
||||
Assert.AreEqual(1, queue.Count);
|
||||
Assert.AreEqual('i', queue[0]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestClear()
|
||||
{
|
||||
queue.Enqueue('a');
|
||||
queue.Enqueue('b');
|
||||
queue.Enqueue('c');
|
||||
queue.Enqueue('d');
|
||||
queue.Enqueue('e');
|
||||
queue.Enqueue('f');
|
||||
|
||||
// Assert queue is empty after clearing
|
||||
queue.Clear();
|
||||
|
||||
Assert.AreEqual(0, queue.Count);
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() =>
|
||||
{
|
||||
char unused = queue[0];
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestEnumerator()
|
||||
{
|
||||
queue.Enqueue('a');
|
||||
queue.Enqueue('b');
|
||||
queue.Enqueue('c');
|
||||
queue.Enqueue('d');
|
||||
queue.Enqueue('e');
|
||||
queue.Enqueue('f');
|
||||
|
||||
char[] expectedValues = { 'f', 'e', 'd', 'c', 'b', 'a' };
|
||||
int expectedValueIndex = 0;
|
||||
|
||||
// Assert items are enumerated in correct order
|
||||
foreach (var item in queue)
|
||||
{
|
||||
Assert.AreEqual(expectedValues[expectedValueIndex], item);
|
||||
expectedValueIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -134,7 +134,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
|
||||
AddAssert("match started", () => Client.Room?.State == MultiplayerRoomState.WaitingForLoad);
|
||||
AddUntilStep("match started", () => Client.Room?.State == MultiplayerRoomState.WaitingForLoad);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -141,7 +141,6 @@ namespace osu.Game.Tournament
|
||||
/// <summary>
|
||||
/// Add missing player info based on user IDs.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private bool addPlayers()
|
||||
{
|
||||
bool addedInfo = false;
|
||||
|
@ -273,7 +273,7 @@ namespace osu.Game.Beatmaps.Formats
|
||||
if (hitObject is IHasPath path)
|
||||
{
|
||||
addPathData(writer, path, position);
|
||||
writer.Write(getSampleBank(hitObject.Samples, zeroBanks: true));
|
||||
writer.Write(getSampleBank(hitObject.Samples));
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -420,15 +420,15 @@ namespace osu.Game.Beatmaps.Formats
|
||||
writer.Write(FormattableString.Invariant($"{endTimeData.EndTime}{suffix}"));
|
||||
}
|
||||
|
||||
private string getSampleBank(IList<HitSampleInfo> samples, bool banksOnly = false, bool zeroBanks = false)
|
||||
private string getSampleBank(IList<HitSampleInfo> samples, bool banksOnly = false)
|
||||
{
|
||||
LegacySampleBank normalBank = toLegacySampleBank(samples.SingleOrDefault(s => s.Name == HitSampleInfo.HIT_NORMAL)?.Bank);
|
||||
LegacySampleBank addBank = toLegacySampleBank(samples.FirstOrDefault(s => !string.IsNullOrEmpty(s.Name) && s.Name != HitSampleInfo.HIT_NORMAL)?.Bank);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.Append(FormattableString.Invariant($"{(zeroBanks ? 0 : (int)normalBank)}:"));
|
||||
sb.Append(FormattableString.Invariant($"{(zeroBanks ? 0 : (int)addBank)}"));
|
||||
sb.Append(FormattableString.Invariant($"{(int)normalBank}:"));
|
||||
sb.Append(FormattableString.Invariant($"{(int)addBank}"));
|
||||
|
||||
if (!banksOnly)
|
||||
{
|
||||
|
@ -44,7 +44,6 @@ namespace osu.Game.Beatmaps
|
||||
/// <summary>
|
||||
/// Returns statistics for the <see cref="HitObjects"/> contained in this beatmap.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
IEnumerable<BeatmapStatistic> GetStatistics();
|
||||
|
||||
/// <summary>
|
||||
|
@ -22,7 +22,6 @@ namespace osu.Game.Configuration
|
||||
/// </summary>
|
||||
/// <param name="rulesetId">The ruleset's internal ID.</param>
|
||||
/// <param name="variant">An optional variant.</param>
|
||||
/// <returns></returns>
|
||||
public List<DatabasedSetting> Query(int? rulesetId = null, int? variant = null) =>
|
||||
ContextFactory.Get().DatabasedSetting.Where(b => b.RulesetID == rulesetId && b.Variant == variant).ToList();
|
||||
|
||||
|
@ -346,7 +346,6 @@ namespace osu.Game.Graphics.Backgrounds
|
||||
/// such that the smaller triangles appear on top.
|
||||
/// </summary>
|
||||
/// <param name="other"></param>
|
||||
/// <returns></returns>
|
||||
public int CompareTo(TriangleParticle other) => other.Scale.CompareTo(Scale);
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
|
||||
public override void PlayHoverSample()
|
||||
{
|
||||
sampleHover.Frequency.Value = 0.96 + RNG.NextDouble(0.08);
|
||||
sampleHover.Frequency.Value = 0.98 + RNG.NextDouble(0.04);
|
||||
sampleHover.Play();
|
||||
}
|
||||
}
|
||||
@ -53,6 +53,9 @@ namespace osu.Game.Graphics.UserInterface
|
||||
Soft,
|
||||
|
||||
[Description("-toolbar")]
|
||||
Toolbar
|
||||
Toolbar,
|
||||
|
||||
[Description("-songselect")]
|
||||
SongSelect
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,6 @@ namespace osu.Game.IO.Serialization
|
||||
/// <summary>
|
||||
/// Creates the default <see cref="JsonSerializerSettings"/> that should be used for all <see cref="IJsonSerializable"/>s.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static JsonSerializerSettings CreateGlobalSettings() => new JsonSerializerSettings
|
||||
{
|
||||
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
|
||||
|
@ -85,7 +85,6 @@ namespace osu.Game.Input
|
||||
/// </summary>
|
||||
/// <param name="rulesetId">The ruleset's internal ID.</param>
|
||||
/// <param name="variant">An optional variant.</param>
|
||||
/// <returns></returns>
|
||||
public List<DatabasedKeyBinding> Query(int? rulesetId = null, int? variant = null) =>
|
||||
ContextFactory.Get().DatabasedKeyBinding.Where(b => b.RulesetID == rulesetId && b.Variant == variant).ToList();
|
||||
|
||||
|
@ -16,7 +16,12 @@ namespace osu.Game.Rulesets.Difficulty.Skills
|
||||
/// <summary>
|
||||
/// <see cref="DifficultyHitObject"/>s that were processed previously. They can affect the strain values of the following objects.
|
||||
/// </summary>
|
||||
protected readonly LimitedCapacityStack<DifficultyHitObject> Previous = new LimitedCapacityStack<DifficultyHitObject>(2); // Contained objects not used yet
|
||||
protected readonly ReverseQueue<DifficultyHitObject> Previous;
|
||||
|
||||
/// <summary>
|
||||
/// Number of previous <see cref="DifficultyHitObject"/>s to keep inside the <see cref="Previous"/> queue.
|
||||
/// </summary>
|
||||
protected virtual int HistoryLength => 1;
|
||||
|
||||
/// <summary>
|
||||
/// Mods for use in skill calculations.
|
||||
@ -28,12 +33,17 @@ namespace osu.Game.Rulesets.Difficulty.Skills
|
||||
protected Skill(Mod[] mods)
|
||||
{
|
||||
this.mods = mods;
|
||||
Previous = new ReverseQueue<DifficultyHitObject>(HistoryLength + 1);
|
||||
}
|
||||
|
||||
internal void ProcessInternal(DifficultyHitObject current)
|
||||
{
|
||||
while (Previous.Count > HistoryLength)
|
||||
Previous.Dequeue();
|
||||
|
||||
Process(current);
|
||||
Previous.Push(current);
|
||||
|
||||
Previous.Enqueue(current);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -1,92 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace osu.Game.Rulesets.Difficulty.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// An indexed stack with limited depth. Indexing starts at the top of the stack.
|
||||
/// </summary>
|
||||
public class LimitedCapacityStack<T> : IEnumerable<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// The number of elements in the stack.
|
||||
/// </summary>
|
||||
public int Count { get; private set; }
|
||||
|
||||
private readonly T[] array;
|
||||
private readonly int capacity;
|
||||
private int marker; // Marks the position of the most recently added item.
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <see cref="LimitedCapacityStack{T}"/>.
|
||||
/// </summary>
|
||||
/// <param name="capacity">The number of items the stack can hold.</param>
|
||||
public LimitedCapacityStack(int capacity)
|
||||
{
|
||||
if (capacity < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(capacity));
|
||||
|
||||
this.capacity = capacity;
|
||||
array = new T[capacity];
|
||||
marker = capacity; // Set marker to the end of the array, outside of the indexed range by one.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the item at an index in the stack.
|
||||
/// </summary>
|
||||
/// <param name="i">The index of the item to retrieve. The top of the stack is returned at index 0.</param>
|
||||
public T this[int i]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (i < 0 || i > Count - 1)
|
||||
throw new ArgumentOutOfRangeException(nameof(i));
|
||||
|
||||
i += marker;
|
||||
if (i > capacity - 1)
|
||||
i -= capacity;
|
||||
|
||||
return array[i];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pushes an item to this <see cref="LimitedCapacityStack{T}"/>.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to push.</param>
|
||||
public void Push(T item)
|
||||
{
|
||||
// Overwrite the oldest item instead of shifting every item by one with every addition.
|
||||
if (marker == 0)
|
||||
marker = capacity - 1;
|
||||
else
|
||||
--marker;
|
||||
|
||||
array[marker] = item;
|
||||
|
||||
if (Count < capacity)
|
||||
++Count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an enumerator which enumerates items in the history starting from the most recently added one.
|
||||
/// </summary>
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
for (int i = marker; i < capacity; ++i)
|
||||
yield return array[i];
|
||||
|
||||
if (Count == capacity)
|
||||
{
|
||||
for (int i = 0; i < marker; ++i)
|
||||
yield return array[i];
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
}
|
||||
}
|
133
osu.Game/Rulesets/Difficulty/Utils/ReverseQueue.cs
Normal file
133
osu.Game/Rulesets/Difficulty/Utils/ReverseQueue.cs
Normal file
@ -0,0 +1,133 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace osu.Game.Rulesets.Difficulty.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// An indexed queue where items are indexed beginning from the most recently enqueued item.
|
||||
/// Enqueuing an item pushes all existing indexes up by one and inserts the item at index 0.
|
||||
/// Dequeuing an item removes the item from the highest index and returns it.
|
||||
/// </summary>
|
||||
public class ReverseQueue<T> : IEnumerable<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// The number of elements in the <see cref="ReverseQueue{T}"/>.
|
||||
/// </summary>
|
||||
public int Count { get; private set; }
|
||||
|
||||
private T[] items;
|
||||
private int capacity;
|
||||
private int start;
|
||||
|
||||
public ReverseQueue(int initialCapacity)
|
||||
{
|
||||
if (initialCapacity <= 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(initialCapacity));
|
||||
|
||||
items = new T[initialCapacity];
|
||||
capacity = initialCapacity;
|
||||
start = 0;
|
||||
Count = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the item at an index in the <see cref="ReverseQueue{T}"/>.
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the item to retrieve. The most recently enqueued item is at index 0.</param>
|
||||
public T this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (index < 0 || index > Count - 1)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
|
||||
int reverseIndex = Count - 1 - index;
|
||||
return items[(start + reverseIndex) % capacity];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enqueues an item to this <see cref="ReverseQueue{T}"/>.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to enqueue.</param>
|
||||
public void Enqueue(T item)
|
||||
{
|
||||
if (Count == capacity)
|
||||
{
|
||||
// Double the buffer size
|
||||
var buffer = new T[capacity * 2];
|
||||
|
||||
// Copy items to new queue
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
buffer[i] = items[(start + i) % capacity];
|
||||
}
|
||||
|
||||
// Replace array with new buffer
|
||||
items = buffer;
|
||||
capacity *= 2;
|
||||
start = 0;
|
||||
}
|
||||
|
||||
items[(start + Count) % capacity] = item;
|
||||
Count++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dequeues the least recently enqueued item from the <see cref="ReverseQueue{T}"/> and returns it.
|
||||
/// </summary>
|
||||
/// <returns>The item dequeued from the <see cref="ReverseQueue{T}"/>.</returns>
|
||||
public T Dequeue()
|
||||
{
|
||||
var item = items[start];
|
||||
start = (start + 1) % capacity;
|
||||
Count--;
|
||||
return item;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the <see cref="ReverseQueue{T}"/> of all items.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
start = 0;
|
||||
Count = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an enumerator which enumerates items in the <see cref="ReverseQueue{T}"/> starting from the most recently enqueued item.
|
||||
/// </summary>
|
||||
public IEnumerator<T> GetEnumerator() => new Enumerator(this);
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
public struct Enumerator : IEnumerator<T>
|
||||
{
|
||||
private ReverseQueue<T> reverseQueue;
|
||||
private int currentIndex;
|
||||
|
||||
internal Enumerator(ReverseQueue<T> reverseQueue)
|
||||
{
|
||||
this.reverseQueue = reverseQueue;
|
||||
currentIndex = -1; // The first MoveNext() should bring the iterator to 0
|
||||
}
|
||||
|
||||
public bool MoveNext() => ++currentIndex < reverseQueue.Count;
|
||||
|
||||
public void Reset() => currentIndex = -1;
|
||||
|
||||
public readonly T Current => reverseQueue[currentIndex];
|
||||
|
||||
readonly object IEnumerator.Current => Current;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
reverseQueue = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -574,7 +574,6 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
/// Calculate the position to be used for sample playback at a specified X position (0..1).
|
||||
/// </summary>
|
||||
/// <param name="position">The lookup X position. Generally should be <see cref="SamplePlaybackPosition"/>.</param>
|
||||
/// <returns></returns>
|
||||
protected double CalculateSamplePlaybackBalance(double position)
|
||||
{
|
||||
const float balance_adjust_amount = 0.4f;
|
||||
|
@ -147,7 +147,6 @@ namespace osu.Game.Rulesets.Objects
|
||||
/// to 1 (end of the path).
|
||||
/// </summary>
|
||||
/// <param name="progress">Ranges from 0 (beginning of the path) to 1 (end of the path).</param>
|
||||
/// <returns></returns>
|
||||
public Vector2 PositionAt(double progress)
|
||||
{
|
||||
ensureValid();
|
||||
@ -161,7 +160,6 @@ namespace osu.Game.Rulesets.Objects
|
||||
/// The first point has a PathType which all other points inherit.
|
||||
/// </summary>
|
||||
/// <param name="controlPoint">One of the control points in the segment.</param>
|
||||
/// <returns></returns>
|
||||
public List<PathControlPoint> PointsInSegment(PathControlPoint controlPoint)
|
||||
{
|
||||
bool found = false;
|
||||
|
@ -146,7 +146,6 @@ namespace osu.Game.Rulesets
|
||||
/// <param name="beatmap">The beatmap to create the hit renderer for.</param>
|
||||
/// <param name="mods">The <see cref="Mod"/>s to apply.</param>
|
||||
/// <exception cref="BeatmapInvalidForRulesetException">Unable to successfully load the beatmap to be usable with this ruleset.</exception>
|
||||
/// <returns></returns>
|
||||
public abstract DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod> mods = null);
|
||||
|
||||
/// <summary>
|
||||
|
@ -62,7 +62,6 @@ namespace osu.Game.Rulesets.Scoring
|
||||
/// <summary>
|
||||
/// Retrieves a mapping of <see cref="HitResult"/>s to their timing windows for all allowed <see cref="HitResult"/>s.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<(HitResult result, double length)> GetAllAvailableWindows()
|
||||
{
|
||||
for (var result = HitResult.Meh; result <= HitResult.Perfect; ++result)
|
||||
|
@ -12,7 +12,6 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons
|
||||
/// <summary>
|
||||
/// Whether this <see cref="RadioButton"/> is selected.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public readonly BindableBool Selected;
|
||||
|
||||
/// <summary>
|
||||
|
@ -226,7 +226,6 @@ namespace osu.Game.Screens.Ranking
|
||||
/// <summary>
|
||||
/// Enumerates all <see cref="ScorePanel"/>s contained in this <see cref="ScorePanelList"/>.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<ScorePanel> GetScorePanels() => flow.Select(t => t.Panel);
|
||||
|
||||
/// <summary>
|
||||
|
@ -152,7 +152,7 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
{
|
||||
if (sampleHover == null) return;
|
||||
|
||||
sampleHover.Frequency.Value = 0.90 + RNG.NextDouble(0.2);
|
||||
sampleHover.Frequency.Value = 0.99 + RNG.NextDouble(0.02);
|
||||
sampleHover.Play();
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
|
||||
namespace osu.Game.Screens.Select
|
||||
{
|
||||
@ -65,6 +66,7 @@ namespace osu.Game.Screens.Select
|
||||
private readonly Box light;
|
||||
|
||||
public FooterButton()
|
||||
: base(HoverSampleSet.SongSelect)
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
Shear = SHEAR;
|
||||
|
@ -189,7 +189,6 @@ namespace osu.Game.Tests.Beatmaps
|
||||
/// <summary>
|
||||
/// Creates the <see cref="Ruleset"/> applicable to this <see cref="BeatmapConversionTest{TConvertMapping,TConvertValue}"/>.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected abstract Ruleset CreateRuleset();
|
||||
|
||||
private class ConvertResult
|
||||
|
@ -17,7 +17,6 @@ namespace osu.Game.Tests.Beatmaps
|
||||
/// <summary>
|
||||
/// Creates the <see cref="Ruleset"/> whose legacy mod conversion is to be tested.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected abstract Ruleset CreateRuleset();
|
||||
|
||||
protected void TestFromLegacy(LegacyMods legacyMods, Type[] expectedMods)
|
||||
|
@ -37,7 +37,6 @@ namespace osu.Game.Utils
|
||||
/// Shortcase for: <c>optional.HasValue ? optional.Value : fallback</c>.
|
||||
/// </remarks>
|
||||
/// <param name="fallback">The fallback value to return if <see cref="HasValue"/> is <c>false</c>.</param>
|
||||
/// <returns></returns>
|
||||
public T GetOr(T fallback) => HasValue ? Value : fallback;
|
||||
|
||||
public static implicit operator Optional<T>(T value) => new Optional<T>(value);
|
||||
|
@ -29,8 +29,8 @@
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.NETCore.Targets" Version="3.1.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2021.407.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.211.1" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2021.410.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.410.0" />
|
||||
<PackageReference Include="Sentry" Version="3.2.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.28.1" />
|
||||
<PackageReference Include="NUnit" Version="3.13.1" />
|
||||
|
@ -70,8 +70,8 @@
|
||||
<Reference Include="System.Net.Http" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2021.407.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.211.1" />
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2021.410.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.410.0" />
|
||||
</ItemGroup>
|
||||
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net5.0 / net6.0) -->
|
||||
<PropertyGroup>
|
||||
@ -93,7 +93,7 @@
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2021.407.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2021.410.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.28.1" />
|
||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
||||
|
Loading…
Reference in New Issue
Block a user