diff --git a/osu.Android.props b/osu.Android.props
index d59e41ba6d..cd57d7478e 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -52,7 +52,7 @@
-
+
diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/CatchEditorTestSceneContainer.cs b/osu.Game.Rulesets.Catch.Tests/Editor/CatchEditorTestSceneContainer.cs
new file mode 100644
index 0000000000..158c8edba5
--- /dev/null
+++ b/osu.Game.Rulesets.Catch.Tests/Editor/CatchEditorTestSceneContainer.cs
@@ -0,0 +1,66 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Timing;
+using osu.Game.Beatmaps;
+using osu.Game.Rulesets.Catch.Edit;
+using osu.Game.Rulesets.Catch.UI;
+using osu.Game.Rulesets.UI;
+using osu.Game.Rulesets.UI.Scrolling;
+using osu.Game.Tests.Visual;
+
+namespace osu.Game.Rulesets.Catch.Tests.Editor
+{
+ public class CatchEditorTestSceneContainer : Container
+ {
+ [Cached(typeof(Playfield))]
+ public readonly ScrollingPlayfield Playfield;
+
+ protected override Container Content { get; }
+
+ public CatchEditorTestSceneContainer()
+ {
+ Anchor = Anchor.Centre;
+ Origin = Anchor.Centre;
+ Width = CatchPlayfield.WIDTH;
+ Height = 1000;
+ Padding = new MarginPadding
+ {
+ Bottom = 100
+ };
+
+ InternalChildren = new Drawable[]
+ {
+ new ScrollingTestContainer(ScrollingDirection.Down)
+ {
+ TimeRange = 1000,
+ RelativeSizeAxes = Axes.Both,
+ Child = Playfield = new TestCatchPlayfield
+ {
+ RelativeSizeAxes = Axes.Both
+ }
+ },
+ new PlayfieldBorder
+ {
+ PlayfieldBorderStyle = { Value = PlayfieldBorderStyle.Full },
+ Clock = new FramedClock(new StopwatchClock(true))
+ },
+ Content = new Container
+ {
+ RelativeSizeAxes = Axes.Both
+ }
+ };
+ }
+
+ private class TestCatchPlayfield : CatchEditorPlayfield
+ {
+ public TestCatchPlayfield()
+ : base(new BeatmapDifficulty { CircleSize = 0 })
+ {
+ }
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/CatchPlacementBlueprintTestScene.cs b/osu.Game.Rulesets.Catch.Tests/Editor/CatchPlacementBlueprintTestScene.cs
new file mode 100644
index 0000000000..1d30ae34cd
--- /dev/null
+++ b/osu.Game.Rulesets.Catch.Tests/Editor/CatchPlacementBlueprintTestScene.cs
@@ -0,0 +1,79 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.Collections.Generic;
+using NUnit.Framework;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Testing;
+using osu.Framework.Timing;
+using osu.Game.Rulesets.Catch.Edit.Blueprints.Components;
+using osu.Game.Rulesets.Catch.Objects.Drawables;
+using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.UI.Scrolling;
+using osu.Game.Tests.Visual;
+using osuTK;
+using osuTK.Input;
+
+namespace osu.Game.Rulesets.Catch.Tests.Editor
+{
+ public abstract class CatchPlacementBlueprintTestScene : PlacementBlueprintTestScene
+ {
+ protected const double TIME_SNAP = 100;
+
+ protected DrawableCatchHitObject LastObject;
+
+ protected new ScrollingHitObjectContainer HitObjectContainer => contentContainer.Playfield.HitObjectContainer;
+
+ protected override Container Content => contentContainer;
+
+ private readonly CatchEditorTestSceneContainer contentContainer;
+
+ protected CatchPlacementBlueprintTestScene()
+ {
+ base.Content.Add(contentContainer = new CatchEditorTestSceneContainer());
+
+ contentContainer.Playfield.Clock = new FramedClock(new ManualClock());
+ }
+
+ [SetUp]
+ public void Setup() => Schedule(() =>
+ {
+ HitObjectContainer.Clear();
+ ResetPlacement();
+ LastObject = null;
+ });
+
+ protected void AddMoveStep(double time, float x) => AddStep($"move to time={time}, x={x}", () =>
+ {
+ float y = HitObjectContainer.PositionAtTime(time);
+ Vector2 pos = HitObjectContainer.ToScreenSpace(new Vector2(x, y + HitObjectContainer.DrawHeight));
+ InputManager.MoveMouseTo(pos);
+ });
+
+ protected void AddClickStep(MouseButton button) => AddStep($"click {button}", () =>
+ {
+ InputManager.Click(button);
+ });
+
+ protected IEnumerable FruitOutlines => Content.ChildrenOfType();
+
+ // Unused because AddHitObject is overriden
+ protected override Container CreateHitObjectContainer() => new Container();
+
+ protected override void AddHitObject(DrawableHitObject hitObject)
+ {
+ LastObject = (DrawableCatchHitObject)hitObject;
+ contentContainer.Playfield.HitObjectContainer.Add(hitObject);
+ }
+
+ protected override SnapResult SnapForBlueprint(PlacementBlueprint blueprint)
+ {
+ var result = base.SnapForBlueprint(blueprint);
+ result.Time = Math.Round(HitObjectContainer.TimeAtScreenSpacePosition(result.ScreenSpacePosition) / TIME_SNAP) * TIME_SNAP;
+ return result;
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/CatchSelectionBlueprintTestScene.cs b/osu.Game.Rulesets.Catch.Tests/Editor/CatchSelectionBlueprintTestScene.cs
new file mode 100644
index 0000000000..dcdc32145b
--- /dev/null
+++ b/osu.Game.Rulesets.Catch.Tests/Editor/CatchSelectionBlueprintTestScene.cs
@@ -0,0 +1,24 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Rulesets.UI.Scrolling;
+using osu.Game.Tests.Visual;
+
+namespace osu.Game.Rulesets.Catch.Tests.Editor
+{
+ public abstract class CatchSelectionBlueprintTestScene : SelectionBlueprintTestScene
+ {
+ protected ScrollingHitObjectContainer HitObjectContainer => contentContainer.Playfield.HitObjectContainer;
+
+ protected override Container Content => contentContainer;
+
+ private readonly CatchEditorTestSceneContainer contentContainer;
+
+ protected CatchSelectionBlueprintTestScene()
+ {
+ base.Content.Add(contentContainer = new CatchEditorTestSceneContainer());
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneBananaShowerPlacementBlueprint.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneBananaShowerPlacementBlueprint.cs
new file mode 100644
index 0000000000..e3811b7669
--- /dev/null
+++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneBananaShowerPlacementBlueprint.cs
@@ -0,0 +1,87 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Linq;
+using NUnit.Framework;
+using osu.Framework.Testing;
+using osu.Framework.Utils;
+using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Catch.Edit.Blueprints;
+using osu.Game.Rulesets.Catch.Edit.Blueprints.Components;
+using osu.Game.Rulesets.Catch.Objects;
+using osu.Game.Rulesets.Catch.Objects.Drawables;
+using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Objects.Drawables;
+using osuTK.Input;
+
+namespace osu.Game.Rulesets.Catch.Tests.Editor
+{
+ public class TestSceneBananaShowerPlacementBlueprint : CatchPlacementBlueprintTestScene
+ {
+ protected override DrawableHitObject CreateHitObject(HitObject hitObject) => new DrawableBananaShower((BananaShower)hitObject);
+
+ protected override PlacementBlueprint CreateBlueprint() => new BananaShowerPlacementBlueprint();
+
+ protected override void AddHitObject(DrawableHitObject hitObject)
+ {
+ // Create nested bananas (but positions are not randomized because beatmap processing is not done).
+ hitObject.HitObject.ApplyDefaults(new ControlPointInfo(), Beatmap.Value.BeatmapInfo.BaseDifficulty);
+
+ base.AddHitObject(hitObject);
+ }
+
+ [Test]
+ public void TestBasicPlacement()
+ {
+ const double start_time = 100;
+ const double end_time = 500;
+
+ AddMoveStep(start_time, 0);
+ AddClickStep(MouseButton.Left);
+ AddMoveStep(end_time, 0);
+ AddClickStep(MouseButton.Right);
+ AddAssert("banana shower is placed", () => LastObject is DrawableBananaShower);
+ AddAssert("start time is correct", () => Precision.AlmostEquals(LastObject.HitObject.StartTime, start_time));
+ AddAssert("end time is correct", () => Precision.AlmostEquals(LastObject.HitObject.GetEndTime(), end_time));
+ }
+
+ [Test]
+ public void TestReversePlacement()
+ {
+ const double start_time = 100;
+ const double end_time = 500;
+
+ AddMoveStep(end_time, 0);
+ AddClickStep(MouseButton.Left);
+ AddMoveStep(start_time, 0);
+ AddClickStep(MouseButton.Right);
+ AddAssert("start time is correct", () => Precision.AlmostEquals(LastObject.HitObject.StartTime, start_time));
+ AddAssert("end time is correct", () => Precision.AlmostEquals(LastObject.HitObject.GetEndTime(), end_time));
+ }
+
+ [Test]
+ public void TestFinishWithZeroDuration()
+ {
+ AddMoveStep(100, 0);
+ AddClickStep(MouseButton.Left);
+ AddClickStep(MouseButton.Right);
+ AddAssert("banana shower is not placed", () => LastObject == null);
+ AddAssert("state is waiting", () => CurrentBlueprint?.PlacementActive == PlacementBlueprint.PlacementState.Waiting);
+ }
+
+ [Test]
+ public void TestOpacity()
+ {
+ AddMoveStep(100, 0);
+ AddClickStep(MouseButton.Left);
+ AddUntilStep("outline is semitransparent", () => Precision.DefinitelyBigger(1, timeSpanOutline.Alpha));
+ AddMoveStep(200, 0);
+ AddUntilStep("outline is opaque", () => Precision.AlmostEquals(timeSpanOutline.Alpha, 1));
+ AddMoveStep(100, 0);
+ AddUntilStep("outline is semitransparent", () => Precision.DefinitelyBigger(1, timeSpanOutline.Alpha));
+ }
+
+ private TimeSpanOutline timeSpanOutline => Content.ChildrenOfType().Single();
+ }
+}
diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneFruitPlacementBlueprint.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneFruitPlacementBlueprint.cs
new file mode 100644
index 0000000000..4b1c45ae2f
--- /dev/null
+++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneFruitPlacementBlueprint.cs
@@ -0,0 +1,44 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Linq;
+using NUnit.Framework;
+using osu.Framework.Utils;
+using osu.Game.Rulesets.Catch.Edit.Blueprints;
+using osu.Game.Rulesets.Catch.Objects;
+using osu.Game.Rulesets.Catch.Objects.Drawables;
+using osu.Game.Rulesets.Catch.UI;
+using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Objects.Drawables;
+using osuTK.Input;
+
+namespace osu.Game.Rulesets.Catch.Tests.Editor
+{
+ public class TestSceneFruitPlacementBlueprint : CatchPlacementBlueprintTestScene
+ {
+ protected override DrawableHitObject CreateHitObject(HitObject hitObject) => new DrawableFruit((Fruit)hitObject);
+
+ protected override PlacementBlueprint CreateBlueprint() => new FruitPlacementBlueprint();
+
+ [Test]
+ public void TestFruitPlacementPosition()
+ {
+ const double time = 300;
+ const float x = CatchPlayfield.CENTER_X;
+
+ AddMoveStep(time, x);
+ AddClickStep(MouseButton.Left);
+
+ AddAssert("outline position is correct", () =>
+ {
+ var outline = FruitOutlines.Single();
+ return Precision.AlmostEquals(outline.X, x) &&
+ Precision.AlmostEquals(outline.Y, HitObjectContainer.PositionAtTime(time));
+ });
+
+ AddAssert("fruit time is correct", () => Precision.AlmostEquals(LastObject.StartTimeBindable.Value, time));
+ AddAssert("fruit position is correct", () => Precision.AlmostEquals(LastObject.X, x));
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs
new file mode 100644
index 0000000000..1b96175020
--- /dev/null
+++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs
@@ -0,0 +1,38 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Catch.Edit.Blueprints;
+using osu.Game.Rulesets.Catch.Objects;
+using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Objects.Types;
+using osuTK;
+
+namespace osu.Game.Rulesets.Catch.Tests.Editor
+{
+ public class TestSceneJuiceStreamSelectionBlueprint : CatchSelectionBlueprintTestScene
+ {
+ public TestSceneJuiceStreamSelectionBlueprint()
+ {
+ var hitObject = new JuiceStream
+ {
+ OriginalX = 100,
+ StartTime = 100,
+ Path = new SliderPath(PathType.PerfectCurve, new[]
+ {
+ Vector2.Zero,
+ new Vector2(200, 100),
+ new Vector2(0, 200),
+ }),
+ };
+ var controlPoint = new ControlPointInfo();
+ controlPoint.Add(0, new TimingControlPoint
+ {
+ BeatLength = 100
+ });
+ hitObject.ApplyDefaults(controlPoint, new BeatmapDifficulty { CircleSize = 0 });
+ AddBlueprint(new JuiceStreamSelectionBlueprint(hitObject));
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs
index 271fbde5c3..449401c0bf 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs
@@ -31,10 +31,11 @@ namespace osu.Game.Tests.Visual.SongSelect
private readonly TestBeatmapDifficultyCache testDifficultyCache = new TestBeatmapDifficultyCache();
[Test]
- public void TestLocal([Values("Beatmap", "Some long title and stuff")]
- string title,
- [Values("Trial", "Some1's very hardest difficulty")]
- string version)
+ public void TestLocal(
+ [Values("Beatmap", "Some long title and stuff")]
+ string title,
+ [Values("Trial", "Some1's very hardest difficulty")]
+ string version)
{
showMetadataForBeatmap(() => CreateWorkingBeatmap(new Beatmap
{
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModDifficultyAdjustSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModDifficultyAdjustSettings.cs
index bf494d4362..e0d76b3e4a 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneModDifficultyAdjustSettings.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModDifficultyAdjustSettings.cs
@@ -157,6 +157,23 @@ namespace osu.Game.Tests.Visual.UserInterface
checkBindableAtValue("Circle Size", 3);
}
+ [Test]
+ public void TestResetToDefaults()
+ {
+ setBeatmapWithDifficultyParameters(5);
+
+ setSliderValue("Circle Size", 3);
+ setExtendedLimits(true);
+
+ checkSliderAtValue("Circle Size", 3);
+ checkBindableAtValue("Circle Size", 3);
+
+ AddStep("reset mod settings", () => modDifficultyAdjust.ResetSettingsToDefaults());
+
+ checkSliderAtValue("Circle Size", 5);
+ checkBindableAtValue("Circle Size", null);
+ }
+
private void resetToDefault(string name)
{
AddStep($"Reset {name} to default", () =>
diff --git a/osu.Game/Rulesets/Edit/HitObjectSelectionBlueprint.cs b/osu.Game/Rulesets/Edit/HitObjectSelectionBlueprint.cs
index 56434b1d82..77dc55c6ef 100644
--- a/osu.Game/Rulesets/Edit/HitObjectSelectionBlueprint.cs
+++ b/osu.Game/Rulesets/Edit/HitObjectSelectionBlueprint.cs
@@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Edit
///
protected virtual bool AlwaysShowWhenSelected => false;
- protected override bool ShouldBeAlive => (DrawableObject.IsAlive && DrawableObject.IsPresent) || (AlwaysShowWhenSelected && State == SelectionState.Selected);
+ protected override bool ShouldBeAlive => (DrawableObject?.IsAlive == true && DrawableObject.IsPresent) || (AlwaysShowWhenSelected && State == SelectionState.Selected);
protected HitObjectSelectionBlueprint(HitObject hitObject)
: base(hitObject)
diff --git a/osu.Game/Rulesets/Mods/DifficultyBindable.cs b/osu.Game/Rulesets/Mods/DifficultyBindable.cs
index 1f76a3e366..664b88eef4 100644
--- a/osu.Game/Rulesets/Mods/DifficultyBindable.cs
+++ b/osu.Game/Rulesets/Mods/DifficultyBindable.cs
@@ -71,6 +71,12 @@ namespace osu.Game.Rulesets.Mods
}
public DifficultyBindable()
+ : this(null)
+ {
+ }
+
+ public DifficultyBindable(float? defaultValue = null)
+ : base(defaultValue)
{
ExtendedLimits.BindValueChanged(_ => updateMaxValue());
}
@@ -93,15 +99,35 @@ namespace osu.Game.Rulesets.Mods
CurrentNumber.MaxValue = ExtendedLimits.Value && extendedMaxValue != null ? extendedMaxValue.Value : maxValue;
}
- public new DifficultyBindable GetBoundCopy() => new DifficultyBindable
+ public override void BindTo(Bindable them)
{
- BindTarget = this,
- CurrentNumber = { BindTarget = CurrentNumber },
- ExtendedLimits = { BindTarget = ExtendedLimits },
- ReadCurrentFromDifficulty = ReadCurrentFromDifficulty,
- // the following is only safe as long as these values are effectively constants.
- MaxValue = maxValue,
- ExtendedMaxValue = extendedMaxValue
- };
+ if (!(them is DifficultyBindable otherDifficultyBindable))
+ throw new InvalidOperationException($"Cannot bind to a non-{nameof(DifficultyBindable)}.");
+
+ ReadCurrentFromDifficulty = otherDifficultyBindable.ReadCurrentFromDifficulty;
+
+ // the following max value copies are only safe as long as these values are effectively constants.
+ MaxValue = otherDifficultyBindable.maxValue;
+ ExtendedMaxValue = otherDifficultyBindable.extendedMaxValue;
+
+ ExtendedLimits.BindTarget = otherDifficultyBindable.ExtendedLimits;
+
+ // the actual values need to be copied after the max value constraints.
+ CurrentNumber.BindTarget = otherDifficultyBindable.CurrentNumber;
+ base.BindTo(them);
+ }
+
+ public override void UnbindFrom(IUnbindable them)
+ {
+ if (!(them is DifficultyBindable otherDifficultyBindable))
+ throw new InvalidOperationException($"Cannot unbind from a non-{nameof(DifficultyBindable)}.");
+
+ base.UnbindFrom(them);
+
+ CurrentNumber.UnbindFrom(otherDifficultyBindable.CurrentNumber);
+ ExtendedLimits.UnbindFrom(otherDifficultyBindable.ExtendedLimits);
+ }
+
+ public new DifficultyBindable GetBoundCopy() => new DifficultyBindable { BindTarget = this };
}
}
diff --git a/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs b/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs
index 2dc77fa72a..42cf826bd4 100644
--- a/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs
+++ b/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs
@@ -17,11 +17,11 @@ namespace osu.Game.Tests.Visual
public abstract class PlacementBlueprintTestScene : OsuManualInputManagerTestScene, IPlacementHandler
{
protected readonly Container HitObjectContainer;
- private PlacementBlueprint currentBlueprint;
+ protected PlacementBlueprint CurrentBlueprint { get; private set; }
protected PlacementBlueprintTestScene()
{
- Add(HitObjectContainer = CreateHitObjectContainer().With(c => c.Clock = new FramedClock(new StopwatchClock())));
+ base.Content.Add(HitObjectContainer = CreateHitObjectContainer().With(c => c.Clock = new FramedClock(new StopwatchClock())));
}
[BackgroundDependencyLoader]
@@ -63,9 +63,9 @@ namespace osu.Game.Tests.Visual
protected void ResetPlacement()
{
- if (currentBlueprint != null)
- Remove(currentBlueprint);
- Add(currentBlueprint = CreateBlueprint());
+ if (CurrentBlueprint != null)
+ Remove(CurrentBlueprint);
+ Add(CurrentBlueprint = CreateBlueprint());
}
public void Delete(HitObject hitObject)
@@ -76,7 +76,7 @@ namespace osu.Game.Tests.Visual
{
base.Update();
- currentBlueprint.UpdateTimeAndPosition(SnapForBlueprint(currentBlueprint));
+ CurrentBlueprint.UpdateTimeAndPosition(SnapForBlueprint(CurrentBlueprint));
}
protected virtual SnapResult SnapForBlueprint(PlacementBlueprint blueprint) =>
diff --git a/osu.Game/Tests/Visual/SelectionBlueprintTestScene.cs b/osu.Game/Tests/Visual/SelectionBlueprintTestScene.cs
index dc12a4999d..c3fb3bfc17 100644
--- a/osu.Game/Tests/Visual/SelectionBlueprintTestScene.cs
+++ b/osu.Game/Tests/Visual/SelectionBlueprintTestScene.cs
@@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using JetBrains.Annotations;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Timing;
@@ -23,7 +24,7 @@ namespace osu.Game.Tests.Visual
});
}
- protected void AddBlueprint(HitObjectSelectionBlueprint blueprint, DrawableHitObject drawableObject)
+ protected void AddBlueprint(HitObjectSelectionBlueprint blueprint, [CanBeNull] DrawableHitObject drawableObject = null)
{
Add(blueprint.With(d =>
{
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index e79901c4bb..eb7a0141c7 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -36,7 +36,7 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/osu.iOS.props b/osu.iOS.props
index 60b358b274..2e5fab758d 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -70,7 +70,7 @@
-
+
@@ -93,7 +93,7 @@
-
+