mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 06:12:56 +08:00
Merge branch 'master' into cursor-trail
This commit is contained in:
commit
8d433cb4bf
@ -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.
|
||||
/// </summary>
|
||||
/// <param name="original">The original hit object.</param>
|
||||
/// <param name="originalBeatmap">The original beatmap. This is used to look-up any values dependent on a fully-loaded beatmap.</param>
|
||||
/// <returns>The hit objects generated.</returns>
|
||||
private IEnumerable<ManiaHitObject> generateSpecific(HitObject original)
|
||||
private IEnumerable<ManiaHitObject> 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.
|
||||
/// </summary>
|
||||
/// <param name="original">The original hit object.</param>
|
||||
/// <param name="originalBeatmap">The original beatmap. This is used to look-up any values dependent on a fully-loaded beatmap.</param>
|
||||
/// <returns>The hit objects generated.</returns>
|
||||
private IEnumerable<ManiaHitObject> generateConverted(HitObject original)
|
||||
private IEnumerable<ManiaHitObject> 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
|
||||
/// </summary>
|
||||
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)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -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++)
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -25,14 +25,20 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
/// </summary>
|
||||
protected readonly FastRandom Random;
|
||||
|
||||
protected PatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern)
|
||||
/// <summary>
|
||||
/// The beatmap which <see cref="HitObject"/> is being converted from.
|
||||
/// </summary>
|
||||
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;
|
||||
|
@ -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;
|
||||
|
@ -1,26 +0,0 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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]
|
@ -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;
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -83,6 +83,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
components.Add(drawableRepeatPoint);
|
||||
AddNested(drawableRepeatPoint);
|
||||
}
|
||||
|
||||
HitObject.PositionChanged += _ => Position = HitObject.StackedPosition;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
|
@ -1,23 +1,41 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// 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<Vector2> 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;
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
19
osu.Game.Rulesets.Osu/Objects/SliderCircle.cs
Normal file
19
osu.Game.Rulesets.Osu/Objects/SliderCircle.cs
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// 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);
|
||||
}
|
||||
}
|
@ -64,10 +64,9 @@
|
||||
<ItemGroup>
|
||||
<Compile Include="Beatmaps\OsuBeatmapConverter.cs" />
|
||||
<Compile Include="Beatmaps\OsuBeatmapProcessor.cs" />
|
||||
<Compile Include="Edit\Layers\Selection\OsuHitObjectOverlayLayer.cs" />
|
||||
<Compile Include="Edit\Layers\Selection\Overlays\HitCircleOverlay.cs" />
|
||||
<Compile Include="Edit\Layers\Selection\Overlays\SliderCircleOverlay.cs" />
|
||||
<Compile Include="Edit\Layers\Selection\Overlays\SliderOverlay.cs" />
|
||||
<Compile Include="Edit\Layers\Selection\Overlays\HitCircleMask.cs" />
|
||||
<Compile Include="Edit\Layers\Selection\Overlays\SliderCircleMask.cs" />
|
||||
<Compile Include="Edit\Layers\Selection\Overlays\SliderMask.cs" />
|
||||
<Compile Include="Edit\OsuEditPlayfield.cs" />
|
||||
<Compile Include="Edit\OsuEditRulesetContainer.cs" />
|
||||
<Compile Include="Edit\OsuHitObjectComposer.cs" />
|
||||
@ -118,6 +117,7 @@
|
||||
<Compile Include="Objects\Drawables\Pieces\SliderBody.cs" />
|
||||
<Compile Include="Objects\ISliderProgress.cs" />
|
||||
<Compile Include="Objects\RepeatPoint.cs" />
|
||||
<Compile Include="Objects\SliderCircle.cs" />
|
||||
<Compile Include="Objects\SliderTick.cs" />
|
||||
<Compile Include="OsuDifficulty\OsuDifficultyCalculator.cs" />
|
||||
<Compile Include="OsuDifficulty\Preprocessing\OsuDifficultyBeatmap.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<Type> 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]
|
||||
|
@ -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<ICompositionTool> CompositionTools { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="HitObjectMask"/> for a specific <see cref="DrawableHitObject"/>.
|
||||
/// </summary>
|
||||
/// <param name="hitObject">The <see cref="DrawableHitObject"/> to create the overlay for.</param>
|
||||
public virtual HitObjectMask CreateMaskFor(DrawableHitObject hitObject) => null;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="SelectionBox"/> which outlines <see cref="DrawableHitObject"/>s
|
||||
/// and handles all hitobject movement/pattern adjustments.
|
||||
/// </summary>
|
||||
/// <param name="overlays">The <see cref="DrawableHitObject"/> overlays.</param>
|
||||
public virtual SelectionBox CreateSelectionOverlay(IReadOnlyList<HitObjectMask> overlays) => new SelectionBox(overlays);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="ScalableContainer"/> which provides a layer above or below the <see cref="Playfield"/>.
|
||||
/// </summary>
|
||||
protected virtual ScalableContainer CreateLayerContainer() => new ScalableContainer { RelativeSizeAxes = Axes.Both };
|
||||
|
||||
/// <summary>
|
||||
/// Creates the <see cref="HitObjectOverlayLayer"/> which overlays selected <see cref="DrawableHitObject"/>s.
|
||||
/// </summary>
|
||||
protected virtual HitObjectOverlayLayer CreateHitObjectOverlayLayer() => new HitObjectOverlayLayer();
|
||||
}
|
||||
}
|
||||
|
21
osu.Game/Rulesets/Edit/HitObjectMask.cs
Normal file
21
osu.Game/Rulesets/Edit/HitObjectMask.cs
Normal file
@ -0,0 +1,21 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// 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
|
||||
{
|
||||
/// <summary>
|
||||
/// A mask placed above a <see cref="DrawableHitObject"/> adding editing functionality.
|
||||
/// </summary>
|
||||
public class HitObjectMask : Container
|
||||
{
|
||||
public readonly DrawableHitObject HitObject;
|
||||
|
||||
public HitObjectMask(DrawableHitObject hitObject)
|
||||
{
|
||||
HitObject = hitObject;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// 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
|
||||
{
|
||||
/// <summary>
|
||||
/// A box which encloses <see cref="DrawableHitObject"/>s.
|
||||
/// </summary>
|
||||
public class CaptureBox : VisibilityContainer
|
||||
{
|
||||
private readonly IDrawable captureArea;
|
||||
private readonly IReadOnlyList<DrawableHitObject> capturedObjects;
|
||||
|
||||
public CaptureBox(IDrawable captureArea, IReadOnlyList<DrawableHitObject> 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();
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// 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;
|
||||
}
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// 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
|
||||
{
|
||||
/// <summary>
|
||||
/// A box that represents a drag selection.
|
||||
/// </summary>
|
||||
public class SelectionBox : VisibilityContainer
|
||||
{
|
||||
public const float BORDER_RADIUS = 2;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="SelectionBox"/>.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
13
osu.Game/Rulesets/Edit/Types/IHasEditablePosition.cs
Normal file
13
osu.Game/Rulesets/Edit/Types/IHasEditablePosition.cs
Normal file
@ -0,0 +1,13 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// 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);
|
||||
}
|
||||
}
|
@ -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
|
||||
{
|
@ -1,20 +1,25 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// 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<DrawableHitObject, HitObjectOverlay> existingOverlays = new Dictionary<DrawableHitObject, HitObjectOverlay>();
|
||||
private readonly HitObjectComposer composer;
|
||||
private readonly Container<HitObjectMask> overlayContainer;
|
||||
|
||||
public HitObjectOverlayLayer()
|
||||
public HitObjectMaskLayer(HitObjectComposer composer)
|
||||
{
|
||||
this.composer = composer;
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
InternalChild = overlayContainer = new Container<HitObjectMask> { RelativeSizeAxes = Axes.Both };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -23,12 +28,11 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection
|
||||
/// <param name="hitObject">The <see cref="DrawableHitObject"/> to create an overlay for.</param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -37,17 +41,22 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection
|
||||
/// <param name="hitObject">The <see cref="DrawableHitObject"/> to remove the overlay for.</param>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="HitObjectOverlay"/> for a specific <see cref="DrawableHitObject"/>.
|
||||
/// </summary>
|
||||
/// <param name="hitObject">The <see cref="DrawableHitObject"/> to create the overlay for.</param>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
102
osu.Game/Screens/Edit/Screens/Compose/Layers/SelectionBox.cs
Normal file
102
osu.Game/Screens/Edit/Screens/Compose/Layers/SelectionBox.cs
Normal file
@ -0,0 +1,102 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// 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
|
||||
{
|
||||
/// <summary>
|
||||
/// A box which surrounds <see cref="DrawableHitObject"/>s and provides interactive handles, context menus etc.
|
||||
/// </summary>
|
||||
public class SelectionBox : VisibilityContainer
|
||||
{
|
||||
private readonly IReadOnlyList<HitObjectMask> overlays;
|
||||
|
||||
public const float BORDER_RADIUS = 2;
|
||||
|
||||
public SelectionBox(IReadOnlyList<HitObjectMask> 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();
|
||||
}
|
||||
}
|
@ -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
|
||||
/// </summary>
|
||||
public event Action<DrawableHitObject> ObjectDeselected;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the selection has been cleared.
|
||||
/// </summary>
|
||||
public event Action SelectionCleared;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the user has finished selecting all <see cref="DrawableHitObject"/>s.
|
||||
/// </summary>
|
||||
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<DrawableHitObject> selectedHitObjects = new HashSet<DrawableHitObject>();
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -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()));
|
||||
/// <summary>
|
||||
/// A box that represents a drag selection.
|
||||
/// </summary>
|
||||
private class DragBox : VisibilityContainer
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="DragBox"/>.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
@ -8,6 +8,9 @@ using osu.Game.Beatmaps;
|
||||
|
||||
namespace osu.Game.Screens.Edit.Screens
|
||||
{
|
||||
/// <summary>
|
||||
/// TODO: eventually make this inherit Screen and add a local scren stack inside the Editor.
|
||||
/// </summary>
|
||||
public class EditorScreen : Container
|
||||
{
|
||||
public readonly Bindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();
|
||||
|
@ -362,10 +362,8 @@
|
||||
<Compile Include="Overlays\Volume\VolumeMeter.cs" />
|
||||
<Compile Include="Rulesets\Configuration\IRulesetConfigManager.cs" />
|
||||
<Compile Include="Rulesets\Configuration\RulesetConfigManager.cs" />
|
||||
<Compile Include="Rulesets\Edit\Layers\BorderLayer.cs" />
|
||||
<Compile Include="Rulesets\Edit\Layers\Selection\CaptureBox.cs" />
|
||||
<Compile Include="Rulesets\Edit\Layers\Selection\HitObjectOverlay.cs" />
|
||||
<Compile Include="Rulesets\Edit\Layers\Selection\HitObjectOverlayLayer.cs" />
|
||||
<Compile Include="Rulesets\Edit\HitObjectMask.cs" />
|
||||
<Compile Include="Rulesets\Edit\Types\IHasEditablePosition.cs" />
|
||||
<Compile Include="Rulesets\Mods\IApplicableFailOverride.cs" />
|
||||
<Compile Include="Rulesets\Mods\IApplicableMod.cs" />
|
||||
<Compile Include="Rulesets\Mods\IApplicableToBeatmapConverter.cs" />
|
||||
@ -380,6 +378,10 @@
|
||||
<Compile Include="Rulesets\Replays\Types\IConvertibleReplayFrame.cs" />
|
||||
<Compile Include="Rulesets\Scoring\Legacy\LegacyScoreParser.cs" />
|
||||
<Compile Include="Rulesets\UI\JudgementContainer.cs" />
|
||||
<Compile Include="Screens\Edit\Screens\Compose\Layers\BorderLayer.cs" />
|
||||
<Compile Include="Screens\Edit\Screens\Compose\Layers\HitObjectMaskLayer.cs" />
|
||||
<Compile Include="Screens\Edit\Screens\Compose\Layers\SelectionBox.cs" />
|
||||
<Compile Include="Screens\Edit\Screens\Compose\Layers\SelectionLayer.cs" />
|
||||
<Compile Include="Screens\Play\ScreenWithBeatmapBackground.cs" />
|
||||
<Compile Include="Screens\Play\PlayerSettings\VisualSettings.cs" />
|
||||
<Compile Include="Rulesets\Objects\CatmullApproximator.cs" />
|
||||
@ -394,8 +396,6 @@
|
||||
<Compile Include="Rulesets\UI\Scrolling\ScrollingRulesetContainer.cs" />
|
||||
<Compile Include="Screens\Select\ImportFromStablePopup.cs" />
|
||||
<Compile Include="Overlays\Settings\SettingsButton.cs" />
|
||||
<Compile Include="Rulesets\Edit\Layers\Selection\SelectionBox.cs" />
|
||||
<Compile Include="Rulesets\Edit\Layers\Selection\SelectionLayer.cs" />
|
||||
<Compile Include="Screens\Edit\Components\BottomBarContainer.cs" />
|
||||
<Compile Include="Screens\Edit\Components\PlaybackControl.cs" />
|
||||
<Compile Include="Screens\Edit\Components\TimeInfoContainer.cs" />
|
||||
|
Loading…
Reference in New Issue
Block a user