diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
index 9922d4c8ad..4734e40803 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
@@ -77,7 +77,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
yield break;
}
- var objects = IsForCurrentRuleset ? generateSpecific(original) : generateConverted(original);
+ var objects = IsForCurrentRuleset ? generateSpecific(original, beatmap) : generateConverted(original, beatmap);
if (objects == null)
yield break;
@@ -110,10 +110,11 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
/// Method that generates hit objects for osu!mania specific beatmaps.
///
/// The original hit object.
+ /// The original beatmap. This is used to look-up any values dependent on a fully-loaded beatmap.
/// The hit objects generated.
- private IEnumerable generateSpecific(HitObject original)
+ private IEnumerable generateSpecific(HitObject original, Beatmap originalBeatmap)
{
- var generator = new SpecificBeatmapPatternGenerator(random, original, beatmap, lastPattern);
+ var generator = new SpecificBeatmapPatternGenerator(random, original, beatmap, lastPattern, originalBeatmap);
Pattern newPattern = generator.Generate();
lastPattern = newPattern;
@@ -125,26 +126,25 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
/// Method that generates hit objects for non-osu!mania beatmaps.
///
/// The original hit object.
+ /// The original beatmap. This is used to look-up any values dependent on a fully-loaded beatmap.
/// The hit objects generated.
- private IEnumerable generateConverted(HitObject original)
+ private IEnumerable generateConverted(HitObject original, Beatmap originalBeatmap)
{
var endTimeData = original as IHasEndTime;
var distanceData = original as IHasDistance;
var positionData = original as IHasPosition;
- // Following lines currently commented out to appease resharper
-
Patterns.PatternGenerator conversion = null;
if (distanceData != null)
- conversion = new DistanceObjectPatternGenerator(random, original, beatmap, lastPattern);
+ conversion = new DistanceObjectPatternGenerator(random, original, beatmap, lastPattern, originalBeatmap);
else if (endTimeData != null)
- conversion = new EndTimeObjectPatternGenerator(random, original, beatmap);
+ conversion = new EndTimeObjectPatternGenerator(random, original, beatmap, originalBeatmap);
else if (positionData != null)
{
computeDensity(original.StartTime);
- conversion = new HitObjectPatternGenerator(random, original, beatmap, lastPattern, lastTime, lastPosition, density, lastStair);
+ conversion = new HitObjectPatternGenerator(random, original, beatmap, lastPattern, lastTime, lastPosition, density, lastStair, originalBeatmap);
recordNote(original.StartTime, positionData.Position);
}
@@ -153,10 +153,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
return null;
Pattern newPattern = conversion.Generate();
- lastPattern = newPattern;
- var stairPatternGenerator = conversion as HitObjectPatternGenerator;
- lastStair = stairPatternGenerator?.StairType ?? lastStair;
+ lastPattern = conversion is EndTimeObjectPatternGenerator ? lastPattern : newPattern;
+ lastStair = (conversion as HitObjectPatternGenerator)?.StairType ?? lastStair;
return newPattern.HitObjects;
}
@@ -166,8 +165,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
///
private class SpecificBeatmapPatternGenerator : Patterns.Legacy.PatternGenerator
{
- public SpecificBeatmapPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern)
- : base(random, hitObject, beatmap, previousPattern)
+ public SpecificBeatmapPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, Beatmap originalBeatmap)
+ : base(random, hitObject, beatmap, previousPattern, originalBeatmap)
{
}
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
index a102781e70..28cf119833 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using osu.Game.Audio;
+using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.MathUtils;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
@@ -29,11 +30,11 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
private PatternType convertType;
- public DistanceObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern)
- : base(random, hitObject, beatmap, previousPattern)
+ public DistanceObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, Beatmap originalBeatmap)
+ : base(random, hitObject, beatmap, previousPattern, originalBeatmap)
{
convertType = PatternType.None;
- if (Beatmap.ControlPointInfo.EffectPointAt(hitObject.StartTime).KiaiMode)
+ if (!Beatmap.ControlPointInfo.EffectPointAt(hitObject.StartTime).KiaiMode)
convertType = PatternType.LowProbability;
var distanceData = hitObject as IHasDistance;
@@ -305,19 +306,19 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
p4 = 0;
break;
case 3:
- p2 = Math.Max(p2, 0.1);
+ p2 = Math.Min(p2, 0.1);
p3 = 0;
p4 = 0;
break;
case 4:
- p2 = Math.Max(p2, 0.3);
- p3 = Math.Max(p3, 0.04);
+ p2 = Math.Min(p2, 0.3);
+ p3 = Math.Min(p3, 0.04);
p4 = 0;
break;
case 5:
- p2 = Math.Max(p2, 0.34);
- p3 = Math.Max(p3, 0.1);
- p4 = Math.Max(p4, 0.03);
+ p2 = Math.Min(p2, 0.34);
+ p3 = Math.Min(p3, 0.1);
+ p4 = Math.Min(p4, 0.03);
break;
}
@@ -396,17 +397,19 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
// Create the hold note
addToPattern(pattern, holdColumn, startTime, endTime);
- int noteCount = 1;
+ int nextColumn = Random.Next(RandomStart, TotalColumns);
+ int noteCount;
if (ConversionDifficulty > 6.5)
noteCount = GetRandomNoteCount(0.63, 0);
else if (ConversionDifficulty > 4)
noteCount = GetRandomNoteCount(TotalColumns < 6 ? 0.12 : 0.45, 0);
else if (ConversionDifficulty > 2.5)
noteCount = GetRandomNoteCount(TotalColumns < 6 ? 0 : 0.24, 0);
+ else
+ noteCount = 0;
noteCount = Math.Min(TotalColumns - 1, noteCount);
bool ignoreHead = !sampleInfoListAt(startTime).Any(s => s.Name == SampleInfo.HIT_WHISTLE || s.Name == SampleInfo.HIT_FINISH || s.Name == SampleInfo.HIT_CLAP);
- int nextColumn = Random.Next(RandomStart, TotalColumns);
var rowPattern = new Pattern();
for (int i = 0; i <= spanCount; i++)
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs
index 278a4c4aab..ffbabba75a 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs
@@ -7,6 +7,7 @@ using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using System.Linq;
using osu.Game.Audio;
+using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
@@ -15,8 +16,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
{
private readonly double endTime;
- public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap)
- : base(random, hitObject, beatmap, new Pattern())
+ public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Beatmap originalBeatmap)
+ : base(random, hitObject, beatmap, new Pattern(), originalBeatmap)
{
var endtimeData = HitObject as IHasEndTime;
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs
index c4ef23a982..e126534c54 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs
@@ -5,6 +5,7 @@ using System;
using System.Linq;
using OpenTK;
using osu.Game.Audio;
+using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Mania.MathUtils;
using osu.Game.Rulesets.Mania.Objects;
@@ -19,8 +20,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
private readonly PatternType convertType;
- public HitObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, PatternType lastStair)
- : base(random, hitObject, beatmap, previousPattern)
+ public HitObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, PatternType lastStair, Beatmap originalBeatmap)
+ : base(random, hitObject, beatmap, previousPattern, originalBeatmap)
{
if (previousTime > hitObject.StartTime) throw new ArgumentOutOfRangeException(nameof(previousTime));
if (density < 0) throw new ArgumentOutOfRangeException(nameof(density));
@@ -308,20 +309,20 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
p5 = 0;
break;
case 3:
- p2 = Math.Max(p2, 0.1);
+ p2 = Math.Min(p2, 0.1);
p3 = 0;
p4 = 0;
p5 = 0;
break;
case 4:
- p2 = Math.Max(p2, 0.23);
- p3 = Math.Max(p3, 0.04);
+ p2 = Math.Min(p2, 0.23);
+ p3 = Math.Min(p3, 0.04);
p4 = 0;
p5 = 0;
break;
case 5:
- p3 = Math.Max(p3, 0.15);
- p4 = Math.Max(p4, 0.03);
+ p3 = Math.Min(p3, 0.15);
+ p4 = Math.Min(p4, 0.03);
p5 = 0;
break;
}
@@ -355,23 +356,23 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
p3 = 0;
break;
case 3:
- centreProbability = Math.Max(centreProbability, 0.03);
- p2 = Math.Max(p2, 0.1);
+ centreProbability = Math.Min(centreProbability, 0.03);
+ p2 = 0;
p3 = 0;
break;
case 4:
centreProbability = 0;
- p2 = Math.Max(p2 * 2, 0.2);
+ p2 = Math.Min(p2 * 2, 0.2);
p3 = 0;
break;
case 5:
- centreProbability = Math.Max(centreProbability, 0.03);
+ centreProbability = Math.Min(centreProbability, 0.03);
p3 = 0;
break;
case 6:
centreProbability = 0;
- p2 = Math.Max(p2 * 2, 0.5);
- p3 = Math.Max(p3 * 2, 0.15);
+ p2 = Math.Min(p2 * 2, 0.5);
+ p3 = Math.Min(p3 * 2, 0.15);
break;
}
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs
index 5f98749f0c..501950cdcd 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs
@@ -25,14 +25,20 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
///
protected readonly FastRandom Random;
- protected PatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern)
+ ///
+ /// The beatmap which is being converted from.
+ ///
+ protected readonly Beatmap OriginalBeatmap;
+
+ protected PatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, Beatmap originalBeatmap)
: base(hitObject, beatmap, previousPattern)
{
if (random == null) throw new ArgumentNullException(nameof(random));
- if (beatmap == null) throw new ArgumentNullException(nameof(beatmap));
- if (previousPattern == null) throw new ArgumentNullException(nameof(previousPattern));
+ if (originalBeatmap == null) throw new ArgumentNullException(nameof(originalBeatmap));
Random = random;
+ OriginalBeatmap = originalBeatmap;
+
RandomStart = TotalColumns == 8 ? 1 : 0;
}
@@ -94,17 +100,20 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
if (conversionDifficulty != null)
return conversionDifficulty.Value;
- HitObject lastObject = Beatmap.HitObjects.LastOrDefault();
- HitObject firstObject = Beatmap.HitObjects.FirstOrDefault();
+ HitObject lastObject = OriginalBeatmap.HitObjects.LastOrDefault();
+ HitObject firstObject = OriginalBeatmap.HitObjects.FirstOrDefault();
double drainTime = (lastObject?.StartTime ?? 0) - (firstObject?.StartTime ?? 0);
- drainTime -= Beatmap.TotalBreakTime;
+ drainTime -= OriginalBeatmap.TotalBreakTime;
if (drainTime == 0)
- drainTime = 10000;
+ drainTime = 10000000;
- BeatmapDifficulty difficulty = Beatmap.BeatmapInfo.BaseDifficulty;
- conversionDifficulty = ((difficulty.DrainRate + MathHelper.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + Beatmap.HitObjects.Count / drainTime * 9f) / 38f * 5f / 1.15;
+ // We need this in seconds
+ drainTime /= 1000;
+
+ BeatmapDifficulty difficulty = OriginalBeatmap.BeatmapInfo.BaseDifficulty;
+ conversionDifficulty = ((difficulty.DrainRate + MathHelper.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + OriginalBeatmap.HitObjects.Count / drainTime * 9f) / 38f * 5f / 1.15;
conversionDifficulty = Math.Min(conversionDifficulty.Value, 12);
return conversionDifficulty.Value;
diff --git a/osu.Game.Rulesets.Mania/Tests/ManiaBeatmapConversionTest.cs b/osu.Game.Rulesets.Mania/Tests/ManiaBeatmapConversionTest.cs
index 2095addc72..9d55ab643d 100644
--- a/osu.Game.Rulesets.Mania/Tests/ManiaBeatmapConversionTest.cs
+++ b/osu.Game.Rulesets.Mania/Tests/ManiaBeatmapConversionTest.cs
@@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Mania.Tests
private bool isForCurrentRuleset;
[NonParallelizable]
- [TestCase("basic", false), Ignore("See: https://github.com/ppy/osu/issues/2150")]
+ [TestCase("basic", false)]
public void Test(string name, bool isForCurrentRuleset)
{
this.isForCurrentRuleset = isForCurrentRuleset;
diff --git a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/OsuHitObjectOverlayLayer.cs b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/OsuHitObjectOverlayLayer.cs
deleted file mode 100644
index e0d1b34ca5..0000000000
--- a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/OsuHitObjectOverlayLayer.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (c) 2007-2018 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using osu.Game.Rulesets.Edit.Layers.Selection;
-using osu.Game.Rulesets.Objects.Drawables;
-using osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays;
-using osu.Game.Rulesets.Osu.Objects.Drawables;
-
-namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection
-{
- public class OsuHitObjectOverlayLayer : HitObjectOverlayLayer
- {
- protected override HitObjectOverlay CreateOverlayFor(DrawableHitObject hitObject)
- {
- switch (hitObject)
- {
- case DrawableHitCircle circle:
- return new HitCircleOverlay(circle);
- case DrawableSlider slider:
- return new SliderOverlay(slider);
- }
-
- return base.CreateOverlayFor(hitObject);
- }
- }
-}
diff --git a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/HitCircleOverlay.cs b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/HitCircleMask.cs
similarity index 75%
rename from osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/HitCircleOverlay.cs
rename to osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/HitCircleMask.cs
index 4e64783840..b48dd73bb5 100644
--- a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/HitCircleOverlay.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/HitCircleMask.cs
@@ -4,15 +4,15 @@
using osu.Framework.Graphics;
using osu.Framework.Allocation;
using osu.Game.Graphics;
-using osu.Game.Rulesets.Edit.Layers.Selection;
+using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
{
- public class HitCircleOverlay : HitObjectOverlay
+ public class HitCircleMask : HitObjectMask
{
- public HitCircleOverlay(DrawableHitCircle hitCircle)
+ public HitCircleMask(DrawableHitCircle hitCircle)
: base(hitCircle)
{
Origin = Anchor.Centre;
@@ -22,6 +22,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
Scale = hitCircle.Scale;
AddInternal(new RingPiece());
+
+ hitCircle.HitObject.PositionChanged += _ => Position = hitCircle.Position;
}
[BackgroundDependencyLoader]
diff --git a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleOverlay.cs b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleMask.cs
similarity index 74%
rename from osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleOverlay.cs
rename to osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleMask.cs
index 50a325d6cc..586b516a11 100644
--- a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleOverlay.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleMask.cs
@@ -4,7 +4,7 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Graphics;
-using osu.Game.Rulesets.Edit.Layers.Selection;
+using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
@@ -12,21 +12,21 @@ using OpenTK;
namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
{
- public class SliderCircleOverlay : HitObjectOverlay
+ public class SliderCircleMask : HitObjectMask
{
- public SliderCircleOverlay(DrawableHitCircle sliderHead, DrawableSlider slider)
+ public SliderCircleMask(DrawableHitCircle sliderHead, DrawableSlider slider)
: this(sliderHead, Vector2.Zero, slider)
{
}
- public SliderCircleOverlay(DrawableSliderTail sliderTail, DrawableSlider slider)
+ public SliderCircleMask(DrawableSliderTail sliderTail, DrawableSlider slider)
: this(sliderTail, ((Slider)slider.HitObject).Curve.PositionAt(1), slider)
{
}
private readonly DrawableOsuHitObject hitObject;
- private SliderCircleOverlay(DrawableOsuHitObject hitObject, Vector2 position, DrawableSlider slider)
+ private SliderCircleMask(DrawableOsuHitObject hitObject, Vector2 position, DrawableSlider slider)
: base(hitObject)
{
this.hitObject = hitObject;
diff --git a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderOverlay.cs b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderMask.cs
similarity index 64%
rename from osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderOverlay.cs
rename to osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderMask.cs
index a035a683e9..53f02617cd 100644
--- a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderOverlay.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderMask.cs
@@ -4,36 +4,41 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Graphics;
-using osu.Game.Rulesets.Edit.Layers.Selection;
+using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
+using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
{
- public class SliderOverlay : HitObjectOverlay
+ public class SliderMask : HitObjectMask
{
private readonly SliderBody body;
private readonly DrawableSlider slider;
- public SliderOverlay(DrawableSlider slider)
+ public SliderMask(DrawableSlider slider)
: base(slider)
{
this.slider = slider;
- var obj = (Slider)slider.HitObject;
+ Position = slider.Position;
+
+ var sliderObject = (Slider)slider.HitObject;
InternalChildren = new Drawable[]
{
- body = new SliderBody(obj)
+ body = new SliderBody(sliderObject)
{
AccentColour = Color4.Transparent,
- PathWidth = obj.Scale * 64
+ PathWidth = sliderObject.Scale * 64
},
- new SliderCircleOverlay(slider.HeadCircle, slider),
- new SliderCircleOverlay(slider.TailCircle, slider),
+ new SliderCircleMask(slider.HeadCircle, slider),
+ new SliderCircleMask(slider.TailCircle, slider),
};
+
+ sliderObject.PositionChanged += _ => Position = slider.Position;
}
[BackgroundDependencyLoader]
@@ -46,12 +51,13 @@ namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
{
base.Update();
- Position = slider.Position;
Size = slider.Size;
OriginPosition = slider.OriginPosition;
// Need to cause one update
body.UpdateProgress(0);
}
+
+ public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => body.ReceiveMouseInputAt(screenSpacePos);
}
}
diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs
index 70d49a6b4f..026c85d909 100644
--- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs
+++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs
@@ -5,10 +5,11 @@ using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit;
-using osu.Game.Rulesets.Edit.Layers.Selection;
using osu.Game.Rulesets.Edit.Tools;
-using osu.Game.Rulesets.Osu.Edit.Layers.Selection;
+using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays;
using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Osu.UI;
using osu.Game.Rulesets.UI;
@@ -32,6 +33,17 @@ namespace osu.Game.Rulesets.Osu.Edit
protected override ScalableContainer CreateLayerContainer() => new ScalableContainer(OsuPlayfield.BASE_SIZE.X) { RelativeSizeAxes = Axes.Both };
- protected override HitObjectOverlayLayer CreateHitObjectOverlayLayer() => new OsuHitObjectOverlayLayer();
+ public override HitObjectMask CreateMaskFor(DrawableHitObject hitObject)
+ {
+ switch (hitObject)
+ {
+ case DrawableHitCircle circle:
+ return new HitCircleMask(circle);
+ case DrawableSlider slider:
+ return new SliderMask(slider);
+ }
+
+ return base.CreateMaskFor(hitObject);
+ }
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
index 959c87bbba..d70b26e181 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
@@ -66,6 +66,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
//may not be so correct
Size = circle.DrawSize;
+
+ HitObject.PositionChanged += _ => Position = HitObject.StackedPosition;
}
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
index 5b9ed4d259..fb3294d319 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
@@ -83,6 +83,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
components.Add(drawableRepeatPoint);
AddNested(drawableRepeatPoint);
}
+
+ HitObject.PositionChanged += _ => Position = HitObject.StackedPosition;
}
[BackgroundDependencyLoader]
diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
index 9b9d88f0f6..d9aed23414 100644
--- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
@@ -1,23 +1,41 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects;
using OpenTK;
using osu.Game.Rulesets.Objects.Types;
using OpenTK.Graphics;
using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Edit.Types;
namespace osu.Game.Rulesets.Osu.Objects
{
- public abstract class OsuHitObject : HitObject, IHasCombo, IHasPosition
+ public abstract class OsuHitObject : HitObject, IHasCombo, IHasEditablePosition
{
public const double OBJECT_RADIUS = 64;
+ public event Action PositionChanged;
+
public double TimePreempt = 600;
public double TimeFadein = 400;
- public Vector2 Position { get; set; }
+ private Vector2 position;
+
+ public Vector2 Position
+ {
+ get => position;
+ set
+ {
+ if (position == value)
+ return;
+ position = value;
+
+ PositionChanged?.Invoke(value);
+ }
+ }
+
public float X => Position.X;
public float Y => Position.Y;
@@ -48,5 +66,7 @@ namespace osu.Game.Rulesets.Osu.Objects
Scale = (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5) / 2;
}
+
+ public virtual void OffsetPosition(Vector2 offset) => Position += offset;
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs
index 76439ca530..a633e3957e 100644
--- a/osu.Game.Rulesets.Osu/Objects/Slider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs
@@ -95,7 +95,7 @@ namespace osu.Game.Rulesets.Osu.Objects
private void createSliderEnds()
{
- HeadCircle = new HitCircle
+ HeadCircle = new SliderCircle(this)
{
StartTime = StartTime,
Position = Position,
@@ -105,7 +105,7 @@ namespace osu.Game.Rulesets.Osu.Objects
SampleControlPoint = SampleControlPoint
};
- TailCircle = new HitCircle
+ TailCircle = new SliderCircle(this)
{
StartTime = EndTime,
Position = EndPosition,
diff --git a/osu.Game.Rulesets.Osu/Objects/SliderCircle.cs b/osu.Game.Rulesets.Osu/Objects/SliderCircle.cs
new file mode 100644
index 0000000000..1e83d02735
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Objects/SliderCircle.cs
@@ -0,0 +1,19 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using OpenTK;
+
+namespace osu.Game.Rulesets.Osu.Objects
+{
+ public class SliderCircle : HitCircle
+ {
+ private readonly Slider slider;
+
+ public SliderCircle(Slider slider)
+ {
+ this.slider = slider;
+ }
+
+ public override void OffsetPosition(Vector2 offset) => slider.OffsetPosition(offset);
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj
index b4c5654de4..4a404e9526 100644
--- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj
+++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj
@@ -64,10 +64,9 @@
-
-
-
-
+
+
+
@@ -118,6 +117,7 @@
+
diff --git a/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs b/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs
index 8d12dfc517..a7e104dd81 100644
--- a/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs
+++ b/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs
@@ -8,13 +8,12 @@ using osu.Framework.Allocation;
using OpenTK;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit;
-using osu.Game.Rulesets.Edit.Layers.Selection;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Edit;
-using osu.Game.Rulesets.Osu.Edit.Layers.Selection;
using osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays;
using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Screens.Edit.Screens.Compose.Layers;
using osu.Game.Tests.Beatmaps;
namespace osu.Game.Tests.Visual
@@ -24,17 +23,15 @@ namespace osu.Game.Tests.Visual
{
public override IReadOnlyList RequiredTypes => new[]
{
- typeof(SelectionBox),
typeof(SelectionLayer),
- typeof(CaptureBox),
+ typeof(SelectionBox),
typeof(HitObjectComposer),
typeof(OsuHitObjectComposer),
- typeof(HitObjectOverlayLayer),
- typeof(OsuHitObjectOverlayLayer),
- typeof(HitObjectOverlay),
- typeof(HitCircleOverlay),
- typeof(SliderOverlay),
- typeof(SliderCircleOverlay)
+ typeof(HitObjectMaskLayer),
+ typeof(HitObjectMask),
+ typeof(HitCircleMask),
+ typeof(SliderMask),
+ typeof(SliderCircleMask)
};
[BackgroundDependencyLoader]
diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
index e6a51cc39b..3dd8d503ed 100644
--- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs
+++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
@@ -10,10 +10,10 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Logging;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
-using osu.Game.Rulesets.Edit.Layers;
-using osu.Game.Rulesets.Edit.Layers.Selection;
using osu.Game.Rulesets.Edit.Tools;
+using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI;
+using osu.Game.Screens.Edit.Screens.Compose.Layers;
using osu.Game.Screens.Edit.Screens.Compose.RadioButtons;
namespace osu.Game.Rulesets.Edit
@@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Edit
return;
}
- HitObjectOverlayLayer hitObjectOverlayLayer = CreateHitObjectOverlayLayer();
+ HitObjectMaskLayer hitObjectMaskLayer = new HitObjectMaskLayer(this);
SelectionLayer selectionLayer = new SelectionLayer(rulesetContainer.Playfield);
var layerBelowRuleset = new BorderLayer
@@ -62,7 +62,7 @@ namespace osu.Game.Rulesets.Edit
layerAboveRuleset.Children = new Drawable[]
{
selectionLayer, // Below object overlays for input
- hitObjectOverlayLayer,
+ hitObjectMaskLayer,
selectionLayer.CreateProxy() // Proxy above object overlays for selections
};
@@ -106,8 +106,10 @@ namespace osu.Game.Rulesets.Edit
}
};
- selectionLayer.ObjectSelected += hitObjectOverlayLayer.AddOverlay;
- selectionLayer.ObjectDeselected += hitObjectOverlayLayer.RemoveOverlay;
+ selectionLayer.ObjectSelected += hitObjectMaskLayer.AddOverlay;
+ selectionLayer.ObjectDeselected += hitObjectMaskLayer.RemoveOverlay;
+ selectionLayer.SelectionCleared += hitObjectMaskLayer.RemoveSelectionOverlay;
+ selectionLayer.SelectionFinished += hitObjectMaskLayer.AddSelectionOverlay;
toolboxCollection.Items =
new[] { new RadioButton("Select", () => setCompositionTool(null)) }
@@ -138,14 +140,22 @@ namespace osu.Game.Rulesets.Edit
protected abstract IReadOnlyList CompositionTools { get; }
+ ///
+ /// Creates a for a specific .
+ ///
+ /// The to create the overlay for.
+ public virtual HitObjectMask CreateMaskFor(DrawableHitObject hitObject) => null;
+
+ ///
+ /// Creates a which outlines s
+ /// and handles all hitobject movement/pattern adjustments.
+ ///
+ /// The overlays.
+ public virtual SelectionBox CreateSelectionOverlay(IReadOnlyList overlays) => new SelectionBox(overlays);
+
///
/// Creates a which provides a layer above or below the .
///
protected virtual ScalableContainer CreateLayerContainer() => new ScalableContainer { RelativeSizeAxes = Axes.Both };
-
- ///
- /// Creates the which overlays selected s.
- ///
- protected virtual HitObjectOverlayLayer CreateHitObjectOverlayLayer() => new HitObjectOverlayLayer();
}
}
diff --git a/osu.Game/Rulesets/Edit/HitObjectMask.cs b/osu.Game/Rulesets/Edit/HitObjectMask.cs
new file mode 100644
index 0000000000..051b42fec6
--- /dev/null
+++ b/osu.Game/Rulesets/Edit/HitObjectMask.cs
@@ -0,0 +1,21 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Graphics.Containers;
+using osu.Game.Rulesets.Objects.Drawables;
+
+namespace osu.Game.Rulesets.Edit
+{
+ ///
+ /// A mask placed above a adding editing functionality.
+ ///
+ public class HitObjectMask : Container
+ {
+ public readonly DrawableHitObject HitObject;
+
+ public HitObjectMask(DrawableHitObject hitObject)
+ {
+ HitObject = hitObject;
+ }
+ }
+}
diff --git a/osu.Game/Rulesets/Edit/Layers/Selection/CaptureBox.cs b/osu.Game/Rulesets/Edit/Layers/Selection/CaptureBox.cs
deleted file mode 100644
index 269dd79bf7..0000000000
--- a/osu.Game/Rulesets/Edit/Layers/Selection/CaptureBox.cs
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright (c) 2007-2018 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using System.Collections.Generic;
-using osu.Framework.Allocation;
-using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Shapes;
-using osu.Game.Graphics;
-using osu.Game.Rulesets.Objects.Drawables;
-using OpenTK;
-
-namespace osu.Game.Rulesets.Edit.Layers.Selection
-{
- ///
- /// A box which encloses s.
- ///
- public class CaptureBox : VisibilityContainer
- {
- private readonly IDrawable captureArea;
- private readonly IReadOnlyList capturedObjects;
-
- public CaptureBox(IDrawable captureArea, IReadOnlyList capturedObjects)
- {
- this.captureArea = captureArea;
- this.capturedObjects = capturedObjects;
-
- Masking = true;
- BorderThickness = SelectionBox.BORDER_RADIUS;
-
- InternalChild = new Box
- {
- RelativeSizeAxes = Axes.Both,
- AlwaysPresent = true,
- Alpha = 0
- };
-
- State = Visibility.Visible;
- }
-
- [BackgroundDependencyLoader]
- private void load(OsuColour colours)
- {
- BorderColour = colours.Yellow;
-
- // Move the rectangle to cover the hitobjects
- var topLeft = new Vector2(float.MaxValue, float.MaxValue);
- var bottomRight = new Vector2(float.MinValue, float.MinValue);
-
- foreach (var obj in capturedObjects)
- {
- topLeft = Vector2.ComponentMin(topLeft, captureArea.ToLocalSpace(obj.SelectionQuad.TopLeft));
- bottomRight = Vector2.ComponentMax(bottomRight, captureArea.ToLocalSpace(obj.SelectionQuad.BottomRight));
- }
-
- topLeft -= new Vector2(5);
- bottomRight += new Vector2(5);
-
- Size = bottomRight - topLeft;
- Position = topLeft;
- }
-
- public override bool DisposeOnDeathRemoval => true;
-
- protected override void PopIn() => this.FadeIn();
- protected override void PopOut() => this.FadeOut();
- }
-}
diff --git a/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlay.cs b/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlay.cs
deleted file mode 100644
index 543dd2cc54..0000000000
--- a/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlay.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (c) 2007-2018 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using osu.Framework.Graphics.Containers;
-using osu.Game.Rulesets.Objects.Drawables;
-
-namespace osu.Game.Rulesets.Edit.Layers.Selection
-{
- public class HitObjectOverlay : OverlayContainer
- {
- // ReSharper disable once NotAccessedField.Local
- // This will be used later to handle drag movement, etc
- private readonly DrawableHitObject hitObject;
-
- public HitObjectOverlay(DrawableHitObject hitObject)
- {
- this.hitObject = hitObject;
-
- State = Visibility.Visible;
- }
-
- protected override void PopIn() => Alpha = 1;
- protected override void PopOut() => Alpha = 0;
- }
-}
diff --git a/osu.Game/Rulesets/Edit/Layers/Selection/SelectionBox.cs b/osu.Game/Rulesets/Edit/Layers/Selection/SelectionBox.cs
deleted file mode 100644
index 1c25846ee3..0000000000
--- a/osu.Game/Rulesets/Edit/Layers/Selection/SelectionBox.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright (c) 2007-2018 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Primitives;
-using osu.Framework.Graphics.Shapes;
-using OpenTK.Graphics;
-
-namespace osu.Game.Rulesets.Edit.Layers.Selection
-{
- ///
- /// A box that represents a drag selection.
- ///
- public class SelectionBox : VisibilityContainer
- {
- public const float BORDER_RADIUS = 2;
-
- ///
- /// Creates a new .
- ///
- public SelectionBox()
- {
- Masking = true;
- BorderColour = Color4.White;
- BorderThickness = BORDER_RADIUS;
-
- Child = new Box
- {
- RelativeSizeAxes = Axes.Both,
- Alpha = 0.1f
- };
- }
-
- public void SetDragRectangle(RectangleF rectangle)
- {
- var topLeft = Parent.ToLocalSpace(rectangle.TopLeft);
- var bottomRight = Parent.ToLocalSpace(rectangle.BottomRight);
-
- Position = topLeft;
- Size = bottomRight - topLeft;
- }
-
- public override bool DisposeOnDeathRemoval => true;
-
- protected override void PopIn() => this.FadeIn(250, Easing.OutQuint);
- protected override void PopOut() => this.FadeOut(250, Easing.OutQuint);
- }
-}
diff --git a/osu.Game/Rulesets/Edit/Types/IHasEditablePosition.cs b/osu.Game/Rulesets/Edit/Types/IHasEditablePosition.cs
new file mode 100644
index 0000000000..fa101ed835
--- /dev/null
+++ b/osu.Game/Rulesets/Edit/Types/IHasEditablePosition.cs
@@ -0,0 +1,13 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Rulesets.Objects.Types;
+using OpenTK;
+
+namespace osu.Game.Rulesets.Edit.Types
+{
+ public interface IHasEditablePosition : IHasPosition
+ {
+ void OffsetPosition(Vector2 offset);
+ }
+}
diff --git a/osu.Game/Rulesets/Edit/Layers/BorderLayer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/BorderLayer.cs
similarity index 92%
rename from osu.Game/Rulesets/Edit/Layers/BorderLayer.cs
rename to osu.Game/Screens/Edit/Screens/Compose/Layers/BorderLayer.cs
index 54c30b8d89..49cf078d36 100644
--- a/osu.Game/Rulesets/Edit/Layers/BorderLayer.cs
+++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/BorderLayer.cs
@@ -6,7 +6,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using OpenTK.Graphics;
-namespace osu.Game.Rulesets.Edit.Layers
+namespace osu.Game.Screens.Edit.Screens.Compose.Layers
{
public class BorderLayer : Container
{
diff --git a/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlayLayer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs
similarity index 50%
rename from osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlayLayer.cs
rename to osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs
index 0b6e63d1fe..63b5538ad7 100644
--- a/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlayLayer.cs
+++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs
@@ -1,20 +1,25 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using System.Collections.Generic;
+using System.Linq;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects.Drawables;
-namespace osu.Game.Rulesets.Edit.Layers.Selection
+namespace osu.Game.Screens.Edit.Screens.Compose.Layers
{
- public class HitObjectOverlayLayer : CompositeDrawable
+ public class HitObjectMaskLayer : CompositeDrawable
{
- private readonly Dictionary existingOverlays = new Dictionary();
+ private readonly HitObjectComposer composer;
+ private readonly Container overlayContainer;
- public HitObjectOverlayLayer()
+ public HitObjectMaskLayer(HitObjectComposer composer)
{
+ this.composer = composer;
RelativeSizeAxes = Axes.Both;
+
+ InternalChild = overlayContainer = new Container { RelativeSizeAxes = Axes.Both };
}
///
@@ -23,12 +28,11 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection
/// The to create an overlay for.
public void AddOverlay(DrawableHitObject hitObject)
{
- var overlay = CreateOverlayFor(hitObject);
+ var overlay = composer.CreateMaskFor(hitObject);
if (overlay == null)
return;
- existingOverlays[hitObject] = overlay;
- AddInternal(overlay);
+ overlayContainer.Add(overlay);
}
///
@@ -37,17 +41,22 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection
/// The to remove the overlay for.
public void RemoveOverlay(DrawableHitObject hitObject)
{
- if (!existingOverlays.TryGetValue(hitObject, out var existing))
+ var existing = overlayContainer.FirstOrDefault(h => h.HitObject == hitObject);
+ if (existing == null)
return;
existing.Hide();
existing.Expire();
}
- ///
- /// Creates a for a specific .
- ///
- /// The to create the overlay for.
- protected virtual HitObjectOverlay CreateOverlayFor(DrawableHitObject hitObject) => null;
+ private SelectionBox currentSelectionBox;
+
+ public void AddSelectionOverlay() => AddInternal(currentSelectionBox = composer.CreateSelectionOverlay(overlayContainer));
+
+ public void RemoveSelectionOverlay()
+ {
+ currentSelectionBox?.Hide();
+ currentSelectionBox?.Expire();
+ }
}
}
diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/SelectionBox.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/SelectionBox.cs
new file mode 100644
index 0000000000..0e5d824559
--- /dev/null
+++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/SelectionBox.cs
@@ -0,0 +1,102 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System.Collections.Generic;
+using System.Linq;
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Input;
+using osu.Game.Graphics;
+using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Edit.Types;
+using osu.Game.Rulesets.Objects.Drawables;
+using OpenTK;
+
+namespace osu.Game.Screens.Edit.Screens.Compose.Layers
+{
+ ///
+ /// A box which surrounds s and provides interactive handles, context menus etc.
+ ///
+ public class SelectionBox : VisibilityContainer
+ {
+ private readonly IReadOnlyList overlays;
+
+ public const float BORDER_RADIUS = 2;
+
+ public SelectionBox(IReadOnlyList overlays)
+ {
+ this.overlays = overlays;
+
+ Masking = true;
+ BorderThickness = BORDER_RADIUS;
+
+ InternalChild = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ AlwaysPresent = true,
+ Alpha = 0
+ };
+
+ State = Visibility.Visible;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ BorderColour = colours.Yellow;
+ }
+
+ protected override void Update()
+ {
+ base.Update();
+
+ // Todo: We might need to optimise this
+
+ // Move the rectangle to cover the hitobjects
+ var topLeft = new Vector2(float.MaxValue, float.MaxValue);
+ var bottomRight = new Vector2(float.MinValue, float.MinValue);
+
+ foreach (var obj in overlays)
+ {
+ topLeft = Vector2.ComponentMin(topLeft, Parent.ToLocalSpace(obj.HitObject.SelectionQuad.TopLeft));
+ bottomRight = Vector2.ComponentMax(bottomRight, Parent.ToLocalSpace(obj.HitObject.SelectionQuad.BottomRight));
+ }
+
+ topLeft -= new Vector2(5);
+ bottomRight += new Vector2(5);
+
+ Size = bottomRight - topLeft;
+ Position = topLeft;
+ }
+
+ public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => overlays.Any(o => o.ReceiveMouseInputAt(screenSpacePos));
+
+ protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => true;
+
+ protected override bool OnDragStart(InputState state) => true;
+
+ protected override bool OnDrag(InputState state)
+ {
+ // Todo: Various forms of snapping
+ foreach (var hitObject in overlays.Select(o => o.HitObject.HitObject))
+ {
+ switch (hitObject)
+ {
+ case IHasEditablePosition editablePosition:
+ editablePosition.OffsetPosition(state.Mouse.Delta);
+ break;
+ }
+ }
+ return true;
+ }
+
+ protected override bool OnDragEnd(InputState state) => true;
+
+ public override bool DisposeOnDeathRemoval => true;
+
+ protected override void PopIn() => this.FadeIn();
+ protected override void PopOut() => this.FadeOut();
+ }
+}
diff --git a/osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/SelectionLayer.cs
similarity index 71%
rename from osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs
rename to osu.Game/Screens/Edit/Screens/Compose/Layers/SelectionLayer.cs
index 3895d34d7f..8c66007bb7 100644
--- a/osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs
+++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/SelectionLayer.cs
@@ -8,12 +8,14 @@ using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
+using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI;
using OpenTK;
+using OpenTK.Graphics;
-namespace osu.Game.Rulesets.Edit.Layers.Selection
+namespace osu.Game.Screens.Edit.Screens.Compose.Layers
{
public class SelectionLayer : CompositeDrawable
{
@@ -27,6 +29,16 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection
///
public event Action ObjectDeselected;
+ ///
+ /// Invoked when the selection has been cleared.
+ ///
+ public event Action SelectionCleared;
+
+ ///
+ /// Invoked when the user has finished selecting all s.
+ ///
+ public event Action SelectionFinished;
+
private readonly Playfield playfield;
public SelectionLayer(Playfield playfield)
@@ -36,8 +48,7 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection
RelativeSizeAxes = Axes.Both;
}
- private SelectionBox selectionBox;
- private CaptureBox captureBox;
+ private DragBox dragBox;
private readonly HashSet selectedHitObjects = new HashSet();
@@ -49,20 +60,20 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection
protected override bool OnDragStart(InputState state)
{
- AddInternal(selectionBox = new SelectionBox());
+ AddInternal(dragBox = new DragBox());
return true;
}
protected override bool OnDrag(InputState state)
{
- selectionBox.Show();
+ dragBox.Show();
var dragPosition = state.Mouse.NativeState.Position;
var dragStartPosition = state.Mouse.NativeState.PositionMouseDown ?? dragPosition;
var screenSpaceDragQuad = new Quad(dragStartPosition.X, dragStartPosition.Y, dragPosition.X - dragStartPosition.X, dragPosition.Y - dragStartPosition.Y);
- selectionBox.SetDragRectangle(screenSpaceDragQuad.AABBFloat);
+ dragBox.SetDragRectangle(screenSpaceDragQuad.AABBFloat);
selectQuad(screenSpaceDragQuad);
return true;
@@ -70,8 +81,8 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection
protected override bool OnDragEnd(InputState state)
{
- selectionBox.Hide();
- selectionBox.Expire();
+ dragBox.Hide();
+ dragBox.Expire();
finishSelection();
@@ -95,7 +106,7 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection
if (!select(hitObject))
return;
- clearCapture();
+ clearSelection();
finishSelection();
}
@@ -122,7 +133,7 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection
if (!deselect(hitObject))
return;
- clearCapture();
+ clearSelection();
finishSelection();
}
@@ -148,7 +159,7 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection
selectedHitObjects.ForEach(h => ObjectDeselected?.Invoke(h));
selectedHitObjects.Clear();
- clearCapture();
+ clearSelection();
}
///
@@ -180,18 +191,49 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection
select(target);
}
- private void clearCapture()
- {
- captureBox?.Hide();
- captureBox?.Expire();
- }
+ private void clearSelection() => SelectionCleared?.Invoke();
private void finishSelection()
{
if (selectedHitObjects.Count == 0)
return;
+ SelectionFinished?.Invoke();
+ }
- AddInternal(captureBox = new CaptureBox(this, selectedHitObjects.ToList()));
+ ///
+ /// A box that represents a drag selection.
+ ///
+ private class DragBox : VisibilityContainer
+ {
+ ///
+ /// Creates a new .
+ ///
+ public DragBox()
+ {
+ Masking = true;
+ BorderColour = Color4.White;
+ BorderThickness = SelectionBox.BORDER_RADIUS;
+
+ Child = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Alpha = 0.1f
+ };
+ }
+
+ public void SetDragRectangle(RectangleF rectangle)
+ {
+ var topLeft = Parent.ToLocalSpace(rectangle.TopLeft);
+ var bottomRight = Parent.ToLocalSpace(rectangle.BottomRight);
+
+ Position = topLeft;
+ Size = bottomRight - topLeft;
+ }
+
+ public override bool DisposeOnDeathRemoval => true;
+
+ protected override void PopIn() => this.FadeIn(250, Easing.OutQuint);
+ protected override void PopOut() => this.FadeOut(250, Easing.OutQuint);
}
}
}
diff --git a/osu.Game/Screens/Edit/Screens/EditorScreen.cs b/osu.Game/Screens/Edit/Screens/EditorScreen.cs
index 2e654b4373..009830502e 100644
--- a/osu.Game/Screens/Edit/Screens/EditorScreen.cs
+++ b/osu.Game/Screens/Edit/Screens/EditorScreen.cs
@@ -8,6 +8,9 @@ using osu.Game.Beatmaps;
namespace osu.Game.Screens.Edit.Screens
{
+ ///
+ /// TODO: eventually make this inherit Screen and add a local scren stack inside the Editor.
+ ///
public class EditorScreen : Container
{
public readonly Bindable Beatmap = new Bindable();
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 21a7ba76f6..264cdb57b9 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -362,10 +362,8 @@
-
-
-
-
+
+
@@ -380,6 +378,10 @@
+
+
+
+
@@ -394,8 +396,6 @@
-
-