diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs
index 90a6e609f0..0de2060e2d 100644
--- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs
+++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs
@@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
switch (obj)
{
- case IHasCurve curveData:
+ case IHasPathWithRepeats curveData:
return new JuiceStream
{
StartTime = obj.StartTime,
@@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
LegacyLastTickOffset = (obj as IHasLegacyLastTickOffset)?.LegacyLastTickOffset ?? 0
}.Yield();
- case IHasEndTime endTime:
+ case IHasDuration endTime:
return new BananaShower
{
StartTime = obj.StartTime,
diff --git a/osu.Game.Rulesets.Catch/Objects/BananaShower.cs b/osu.Game.Rulesets.Catch/Objects/BananaShower.cs
index 3a0b5ace53..04a995c77e 100644
--- a/osu.Game.Rulesets.Catch/Objects/BananaShower.cs
+++ b/osu.Game.Rulesets.Catch/Objects/BananaShower.cs
@@ -7,7 +7,7 @@ using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Rulesets.Catch.Objects
{
- public class BananaShower : CatchHitObject, IHasEndTime
+ public class BananaShower : CatchHitObject, IHasDuration
{
public override FruitVisualRepresentation VisualRepresentation => FruitVisualRepresentation.Banana;
diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
index d32595c2e1..2c96ee2b19 100644
--- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
+++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
@@ -14,7 +14,7 @@ using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Rulesets.Catch.Objects
{
- public class JuiceStream : CatchHitObject, IHasCurve
+ public class JuiceStream : CatchHitObject, IHasPathWithRepeats
{
///
/// Positional distance that results in a duration of one second, before any speed adjustments.
@@ -115,15 +115,15 @@ namespace osu.Game.Rulesets.Catch.Objects
}
}
- public double EndTime
+ public float EndX => X + this.CurvePositionAt(1).X / CatchPlayfield.BASE_WIDTH;
+
+ public double Duration
{
- get => StartTime + this.SpanCount() * Path.Distance / Velocity;
+ get => this.SpanCount() * Path.Distance / Velocity;
set => throw new System.NotSupportedException($"Adjust via {nameof(RepeatCount)} instead"); // can be implemented if/when needed.
}
- public float EndX => X + this.CurvePositionAt(1).X / CatchPlayfield.BASE_WIDTH;
-
- public double Duration => EndTime - StartTime;
+ public double EndTime => StartTime + Duration;
private readonly SliderPath path = new SliderPath();
diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneNotes.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneNotes.cs
index ea6a1e2e6a..dd5fd93710 100644
--- a/osu.Game.Rulesets.Mania.Tests/TestSceneNotes.cs
+++ b/osu.Game.Rulesets.Mania.Tests/TestSceneNotes.cs
@@ -156,7 +156,7 @@ namespace osu.Game.Rulesets.Mania.Tests
foreach (var obj in content.OfType())
{
- if (!(obj.HitObject is IHasEndTime endTime))
+ if (!(obj.HitObject is IHasDuration endTime))
continue;
foreach (var nested in obj.NestedHitObjects)
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
index 1c8116754f..32abf5e7f9 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
@@ -54,7 +54,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
}
else
{
- float percentSliderOrSpinner = (float)beatmap.HitObjects.Count(h => h is IHasEndTime) / beatmap.HitObjects.Count;
+ float percentSliderOrSpinner = (float)beatmap.HitObjects.Count(h => h is IHasDuration) / beatmap.HitObjects.Count;
if (percentSliderOrSpinner < 0.2)
TargetColumns = 7;
else if (percentSliderOrSpinner < 0.3 || roundedCircleSize >= 5)
@@ -175,7 +175,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
break;
}
- case IHasEndTime endTimeData:
+ case IHasDuration endTimeData:
{
conversion = new EndTimeObjectPatternGenerator(Random, original, beatmap, originalBeatmap);
@@ -231,7 +231,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
var pattern = new Pattern();
- if (HitObject is IHasEndTime endTimeData)
+ if (HitObject is IHasDuration endTimeData)
{
pattern.Add(new HoldNote
{
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
index d8d5b67c0e..1bd796511b 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
@@ -474,7 +474,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
///
private IList sampleInfoListAt(double time)
{
- if (!(HitObject is IHasCurve curveData))
+ if (!(HitObject is IHasPathWithRepeats curveData))
return HitObject.Samples;
double segmentTime = (EndTime - HitObject.StartTime) / spanCount;
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs
index 907bed0d65..d5286a3779 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs
@@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, IBeatmap originalBeatmap)
: base(random, hitObject, beatmap, new Pattern(), originalBeatmap)
{
- endTime = (HitObject as IHasEndTime)?.EndTime ?? 0;
+ endTime = (HitObject as IHasDuration)?.EndTime ?? 0;
}
public override IEnumerable Generate()
diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs
index 10d344242c..7e2469a794 100644
--- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs
+++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs
@@ -13,6 +13,7 @@ using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Screens.Edit.Compose.Components;
@@ -88,7 +89,8 @@ namespace osu.Game.Rulesets.Mania.Edit
return drawableRuleset;
}
- protected override ComposeBlueprintContainer CreateBlueprintContainer() => new ManiaBlueprintContainer(drawableRuleset.Playfield.AllHitObjects);
+ protected override ComposeBlueprintContainer CreateBlueprintContainer(IEnumerable hitObjects)
+ => new ManiaBlueprintContainer(hitObjects);
protected override IReadOnlyList CompositionTools => new HitObjectCompositionTool[]
{
diff --git a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs
index e6f722a8a9..a100c9a58e 100644
--- a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs
+++ b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs
@@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mania.Objects
///
/// Represents a hit object which requires pressing, holding, and releasing a key.
///
- public class HoldNote : ManiaHitObject, IHasEndTime
+ public class HoldNote : ManiaHitObject, IHasDuration
{
public double EndTime
{
diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs
index 147d74c929..fcad356a1c 100644
--- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs
+++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs
@@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
switch (original)
{
- case IHasCurve curveData:
+ case IHasPathWithRepeats curveData:
return new Slider
{
StartTime = original.StartTime,
@@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
TickDistanceMultiplier = beatmap.BeatmapInfo.BeatmapVersion < 8 ? 1f / beatmap.ControlPointInfo.DifficultyPointAt(original.StartTime).SpeedMultiplier : 1
}.Yield();
- case IHasEndTime endTimeData:
+ case IHasDuration endTimeData:
return new Spinner
{
StartTime = original.StartTime,
diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs
index de5c1e54d7..37019a7a05 100644
--- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs
+++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs
@@ -13,6 +13,7 @@ using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.Edit.Compose.Components;
@@ -46,7 +47,8 @@ namespace osu.Game.Rulesets.Osu.Edit
EditorBeatmap.PlacementObject.ValueChanged += _ => updateDistanceSnapGrid();
}
- protected override ComposeBlueprintContainer CreateBlueprintContainer() => new OsuBlueprintContainer(HitObjects);
+ protected override ComposeBlueprintContainer CreateBlueprintContainer(IEnumerable hitObjects)
+ => new OsuBlueprintContainer(hitObjects);
private DistanceSnapGrid distanceSnapGrid;
private Container distanceSnapGridContainer;
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs
index 7b1941b7f9..5d191119b9 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs
@@ -70,7 +70,7 @@ namespace osu.Game.Rulesets.Osu.Mods
break;
// already hit or beyond the hittable end time.
- if (h.IsHit || (h.HitObject is IHasEndTime hasEnd && time > hasEnd.EndTime))
+ if (h.IsHit || (h.HitObject is IHasDuration hasEnd && time > hasEnd.EndTime))
continue;
switch (h)
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs b/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs
index 297a0fea79..3cad52faeb 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs
@@ -61,7 +61,7 @@ namespace osu.Game.Rulesets.Osu.Mods
}
// Keep wiggling sliders and spinners for their duration
- if (!(osuObject is IHasEndTime endTime))
+ if (!(osuObject is IHasDuration endTime))
return;
amountWiggles = (int)(endTime.Duration / wiggle_duration);
diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs
index 6ba0e1c6aa..705e88040f 100644
--- a/osu.Game.Rulesets.Osu/Objects/Slider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs
@@ -17,16 +17,16 @@ using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Objects
{
- public class Slider : OsuHitObject, IHasCurve
+ public class Slider : OsuHitObject, IHasPathWithRepeats
{
- public double EndTime
+ public double EndTime => StartTime + this.SpanCount() * Path.Distance / Velocity;
+
+ public double Duration
{
- get => StartTime + this.SpanCount() * Path.Distance / Velocity;
+ get => EndTime - StartTime;
set => throw new System.NotSupportedException($"Adjust via {nameof(RepeatCount)} instead"); // can be implemented if/when needed.
}
- public double Duration => EndTime - StartTime;
-
private readonly Cached endPositionCache = new Cached();
public override Vector2 EndPosition => endPositionCache.IsValid ? endPositionCache.Value : endPositionCache.Value = Position + this.CurvePositionAt(1);
diff --git a/osu.Game.Rulesets.Osu/Objects/Spinner.cs b/osu.Game.Rulesets.Osu/Objects/Spinner.cs
index 0b8d03d118..418375c090 100644
--- a/osu.Game.Rulesets.Osu/Objects/Spinner.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Spinner.cs
@@ -11,7 +11,7 @@ using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Objects
{
- public class Spinner : OsuHitObject, IHasEndTime
+ public class Spinner : OsuHitObject, IHasDuration
{
public double EndTime
{
diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs
index d324441285..78550ed270 100644
--- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs
+++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs
@@ -114,7 +114,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
if (!isForCurrentRuleset && tickSpacing > 0 && osuDuration < 2 * speedAdjustedBeatLength)
{
- List> allSamples = obj is IHasCurve curveData ? curveData.NodeSamples : new List>(new[] { samples });
+ List> allSamples = obj is IHasPathWithRepeats curveData ? curveData.NodeSamples : new List>(new[] { samples });
int i = 0;
@@ -150,7 +150,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
break;
}
- case IHasEndTime endTimeData:
+ case IHasDuration endTimeData:
{
double hitMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, 3, 5, 7.5) * swell_hit_multiplier;
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs
index 32f7acadc8..7294587b10 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs
@@ -237,7 +237,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
case ArmedState.Miss:
case ArmedState.Hit:
- using (BeginAbsoluteSequence(Time.Current, true))
+ using (BeginDelayedSequence(HitObject.Duration, true))
{
this.FadeOut(transition_duration, Easing.Out);
bodyContainer.ScaleTo(1.4f, transition_duration);
diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs
index 7b11bce520..5f52160be1 100644
--- a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs
@@ -3,9 +3,7 @@
using osu.Game.Rulesets.Objects.Types;
using System;
-using System.Collections.Generic;
using System.Threading;
-using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Judgements;
@@ -17,7 +15,7 @@ using osuTK;
namespace osu.Game.Rulesets.Taiko.Objects
{
- public class DrumRoll : TaikoHitObject, IHasCurve
+ public class DrumRoll : TaikoHitObject, IHasPath
{
///
/// Drum roll distance that results in a duration of 1 speed-adjusted beat length.
@@ -115,11 +113,7 @@ namespace osu.Game.Rulesets.Taiko.Objects
double IHasDistance.Distance => Duration * Velocity;
- int IHasRepeats.RepeatCount { get => 0; set { } }
-
- List> IHasRepeats.NodeSamples => new List>();
-
- SliderPath IHasCurve.Path
+ SliderPath IHasPath.Path
=> new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(1) }, ((IHasDistance)this).Distance / TaikoBeatmapConverter.LEGACY_VELOCITY_MULTIPLIER);
#endregion
diff --git a/osu.Game.Rulesets.Taiko/Objects/Swell.cs b/osu.Game.Rulesets.Taiko/Objects/Swell.cs
index 390f8d1f3b..8a63a89951 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Swell.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Swell.cs
@@ -10,7 +10,7 @@ using osu.Game.Rulesets.Taiko.Judgements;
namespace osu.Game.Rulesets.Taiko.Objects
{
- public class Swell : TaikoHitObject, IHasEndTime
+ public class Swell : TaikoHitObject, IHasDuration
{
public double EndTime
{
diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
index acb30a6277..dab923d75b 100644
--- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
@@ -365,7 +365,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
{
var hitObjects = decoder.Decode(stream).HitObjects;
- var curveData = hitObjects[0] as IHasCurve;
+ var curveData = hitObjects[0] as IHasPathWithRepeats;
var positionData = hitObjects[0] as IHasPosition;
Assert.IsNotNull(positionData);
diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
index b034e66616..b4c78ce273 100644
--- a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
@@ -95,7 +95,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
{
var beatmap = decodeAsJson(normal);
- var curveData = beatmap.HitObjects[0] as IHasCurve;
+ var curveData = beatmap.HitObjects[0] as IHasPathWithRepeats;
var positionData = beatmap.HitObjects[0] as IHasPosition;
Assert.IsNotNull(positionData);
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs
index b25b81c9af..bd7e894cf8 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs
@@ -281,7 +281,7 @@ namespace osu.Game.Tests.Visual.Gameplay
yield return new TestHitObject
{
StartTime = original.StartTime,
- EndTime = (original as IHasEndTime)?.EndTime ?? (original.StartTime + 100)
+ Duration = (original as IHasDuration)?.Duration ?? 100
};
}
}
@@ -290,11 +290,11 @@ namespace osu.Game.Tests.Visual.Gameplay
#region HitObject
- private class TestHitObject : ConvertHitObject, IHasEndTime
+ private class TestHitObject : ConvertHitObject, IHasDuration
{
- public double EndTime { get; set; }
+ public double EndTime => StartTime + Duration;
- public double Duration => EndTime - StartTime;
+ public double Duration { get; set; }
}
private class DrawableTestHitObject : DrawableHitObject
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
index 7727f25967..cefb47893c 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
@@ -233,14 +233,14 @@ namespace osu.Game.Beatmaps.Formats
writer.Write(FormattableString.Invariant($"{(int)getObjectType(hitObject)},"));
writer.Write(FormattableString.Invariant($"{(int)toLegacyHitSoundType(hitObject.Samples)},"));
- if (hitObject is IHasCurve curveData)
+ if (hitObject is IHasPath path)
{
- addCurveData(writer, curveData, position);
+ addPathData(writer, path, position);
writer.Write(getSampleBank(hitObject.Samples, zeroBanks: true));
}
else
{
- if (hitObject is IHasEndTime)
+ if (hitObject is IHasDuration)
addEndTimeData(writer, hitObject);
writer.Write(getSampleBank(hitObject.Samples));
@@ -263,11 +263,11 @@ namespace osu.Game.Beatmaps.Formats
switch (hitObject)
{
- case IHasCurve _:
+ case IHasPath _:
type |= LegacyHitObjectType.Slider;
break;
- case IHasEndTime _:
+ case IHasDuration _:
if (beatmap.BeatmapInfo.RulesetID == 3)
type |= LegacyHitObjectType.Hold;
else
@@ -282,13 +282,13 @@ namespace osu.Game.Beatmaps.Formats
return type;
}
- private void addCurveData(TextWriter writer, IHasCurve curveData, Vector2 position)
+ private void addPathData(TextWriter writer, IHasPath pathData, Vector2 position)
{
PathType? lastType = null;
- for (int i = 0; i < curveData.Path.ControlPoints.Count; i++)
+ for (int i = 0; i < pathData.Path.ControlPoints.Count; i++)
{
- PathControlPoint point = curveData.Path.ControlPoints[i];
+ PathControlPoint point = pathData.Path.ControlPoints[i];
if (point.Type.Value != null)
{
@@ -325,29 +325,34 @@ namespace osu.Game.Beatmaps.Formats
if (i != 0)
{
writer.Write(FormattableString.Invariant($"{position.X + point.Position.Value.X}:{position.Y + point.Position.Value.Y}"));
- writer.Write(i != curveData.Path.ControlPoints.Count - 1 ? "|" : ",");
+ writer.Write(i != pathData.Path.ControlPoints.Count - 1 ? "|" : ",");
}
}
- writer.Write(FormattableString.Invariant($"{curveData.RepeatCount + 1},"));
- writer.Write(FormattableString.Invariant($"{curveData.Path.Distance},"));
+ var curveData = pathData as IHasPathWithRepeats;
- for (int i = 0; i < curveData.NodeSamples.Count; i++)
- {
- writer.Write(FormattableString.Invariant($"{(int)toLegacyHitSoundType(curveData.NodeSamples[i])}"));
- writer.Write(i != curveData.NodeSamples.Count - 1 ? "|" : ",");
- }
+ writer.Write(FormattableString.Invariant($"{(curveData?.RepeatCount ?? 0) + 1},"));
+ writer.Write(FormattableString.Invariant($"{pathData.Path.Distance},"));
- for (int i = 0; i < curveData.NodeSamples.Count; i++)
+ if (curveData != null)
{
- writer.Write(getSampleBank(curveData.NodeSamples[i], true));
- writer.Write(i != curveData.NodeSamples.Count - 1 ? "|" : ",");
+ for (int i = 0; i < curveData.NodeSamples.Count; i++)
+ {
+ writer.Write(FormattableString.Invariant($"{(int)toLegacyHitSoundType(curveData.NodeSamples[i])}"));
+ writer.Write(i != curveData.NodeSamples.Count - 1 ? "|" : ",");
+ }
+
+ for (int i = 0; i < curveData.NodeSamples.Count; i++)
+ {
+ writer.Write(getSampleBank(curveData.NodeSamples[i], true));
+ writer.Write(i != curveData.NodeSamples.Count - 1 ? "|" : ",");
+ }
}
}
private void addEndTimeData(TextWriter writer, HitObject hitObject)
{
- var endTimeData = (IHasEndTime)hitObject;
+ var endTimeData = (IHasDuration)hitObject;
var type = getObjectType(hitObject);
char suffix = ',';
diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs
index 8126311cbd..ac399e37c4 100644
--- a/osu.Game/Beatmaps/WorkingBeatmap.cs
+++ b/osu.Game/Beatmaps/WorkingBeatmap.cs
@@ -63,7 +63,7 @@ namespace osu.Game.Beatmaps
length = emptyLength;
break;
- case IHasEndTime endTime:
+ case IHasDuration endTime:
length = endTime.EndTime + excess_length;
break;
diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
index 38576e02a0..c25fb03fd0 100644
--- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs
+++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
@@ -48,8 +48,6 @@ namespace osu.Game.Rulesets.Edit
protected ComposeBlueprintContainer BlueprintContainer { get; private set; }
- public override Playfield Playfield => drawableRulesetWrapper.Playfield;
-
private DrawableEditRulesetWrapper drawableRulesetWrapper;
protected readonly Container LayerBelowRuleset = new Container { RelativeSizeAxes = Axes.Both };
@@ -61,7 +59,6 @@ namespace osu.Game.Rulesets.Edit
protected HitObjectComposer(Ruleset ruleset)
{
Ruleset = ruleset;
- RelativeSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
@@ -116,7 +113,7 @@ namespace osu.Game.Rulesets.Edit
drawableRulesetWrapper,
// layers above playfield
drawableRulesetWrapper.CreatePlayfieldAdjustmentContainer()
- .WithChild(BlueprintContainer = CreateBlueprintContainer())
+ .WithChild(BlueprintContainer = CreateBlueprintContainer(HitObjects))
}
}
},
@@ -137,6 +134,51 @@ namespace osu.Game.Rulesets.Edit
EditorBeatmap.SelectedHitObjects.CollectionChanged += selectionChanged;
}
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ inputManager = GetContainingInputManager();
+ }
+
+ public override Playfield Playfield => drawableRulesetWrapper.Playfield;
+
+ public override IEnumerable HitObjects => drawableRulesetWrapper.Playfield.AllHitObjects;
+
+ public override bool CursorInPlacementArea => drawableRulesetWrapper.Playfield.ReceivePositionalInputAt(inputManager.CurrentState.Mouse.Position);
+
+ ///
+ /// Defines all available composition tools, listed on the left side of the editor screen as button controls.
+ /// This should usually define one tool for each type used in the target ruleset.
+ ///
+ ///
+ /// A "select" tool is automatically added as the first tool.
+ ///
+ protected abstract IReadOnlyList CompositionTools { get; }
+
+ ///
+ /// Construct a relevant blueprint container. This will manage hitobject selection/placement input handling and display logic.
+ ///
+ /// A live collection of all s in the editor beatmap.
+ protected virtual ComposeBlueprintContainer CreateBlueprintContainer(IEnumerable hitObjects)
+ => new ComposeBlueprintContainer(hitObjects);
+
+ ///
+ /// Construct a drawable ruleset for the provided ruleset.
+ ///
+ ///
+ /// Can be overridden to add editor-specific logical changes to a 's standard .
+ /// For example, hit animations or judgement logic may be changed to give a better editor user experience.
+ ///
+ /// The ruleset used to construct its drawable counterpart.
+ /// The loaded beatmap.
+ /// The mods to be applied.
+ /// An editor-relevant .
+ protected virtual DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null)
+ => (DrawableRuleset)ruleset.CreateDrawableRulesetWith(beatmap, mods);
+
+ #region Tool selection logic
+
protected override bool OnKeyDown(KeyDownEvent e)
{
if (e.Key >= Key.Number1 && e.Key <= Key.Number9)
@@ -153,13 +195,6 @@ namespace osu.Game.Rulesets.Edit
return base.OnKeyDown(e);
}
- protected override void LoadComplete()
- {
- base.LoadComplete();
-
- inputManager = GetContainingInputManager();
- }
-
private void selectionChanged(object sender, NotifyCollectionChangedEventArgs changedArgs)
{
if (EditorBeatmap.SelectedHitObjects.Any())
@@ -179,15 +214,9 @@ namespace osu.Game.Rulesets.Edit
EditorBeatmap.SelectedHitObjects.Clear();
}
- public override IEnumerable HitObjects => drawableRulesetWrapper.Playfield.AllHitObjects;
+ #endregion
- public override bool CursorInPlacementArea => drawableRulesetWrapper.Playfield.ReceivePositionalInputAt(inputManager.CurrentState.Mouse.Position);
-
- protected abstract IReadOnlyList CompositionTools { get; }
-
- protected abstract ComposeBlueprintContainer CreateBlueprintContainer();
-
- protected abstract DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null);
+ #region IPlacementHandler
public void BeginPlacement(HitObject hitObject)
{
@@ -209,6 +238,17 @@ namespace osu.Game.Rulesets.Edit
public void Delete(HitObject hitObject) => EditorBeatmap.Remove(hitObject);
+ #endregion
+
+ #region IPositionSnapProvider
+
+ ///
+ /// Retrieve the relevant at a specified screen-space position.
+ /// In cases where a ruleset doesn't require custom logic (due to nested playfields, for example)
+ /// this will return the ruleset's main playfield.
+ ///
+ /// The screen-space position to query.
+ /// The most relevant .
protected virtual Playfield PlayfieldAtScreenSpacePosition(Vector2 screenSpacePosition) => drawableRulesetWrapper.Playfield;
public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition)
@@ -257,8 +297,14 @@ namespace osu.Game.Rulesets.Edit
return DurationToDistance(referenceTime, snappedEndTime - referenceTime);
}
+
+ #endregion
}
+ ///
+ /// A non-generic definition of a HitObject composer class.
+ /// Generally used to access certain methods without requiring a generic type for .
+ ///
[Cached(typeof(HitObjectComposer))]
[Cached(typeof(IPositionSnapProvider))]
public abstract class HitObjectComposer : CompositeDrawable, IPositionSnapProvider
@@ -268,10 +314,13 @@ namespace osu.Game.Rulesets.Edit
RelativeSizeAxes = Axes.Both;
}
+ ///
+ /// The target ruleset's playfield.
+ ///
public abstract Playfield Playfield { get; }
///
- /// All the s.
+ /// All s in currently loaded beatmap.
///
public abstract IEnumerable HitObjects { get; }
@@ -280,6 +329,8 @@ namespace osu.Game.Rulesets.Edit
///
public abstract bool CursorInPlacementArea { get; }
+ #region IPositionSnapProvider
+
public abstract SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition);
public abstract float GetBeatSnapDistanceAt(double referenceTime);
@@ -291,5 +342,7 @@ namespace osu.Game.Rulesets.Edit
public abstract double GetSnappedDurationFromDistance(double referenceTime, float distance);
public abstract float GetSnappedDistanceFromDistance(double referenceTime, float distance);
+
+ #endregion
}
}
diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs
index 6f9053d7cb..e2cc98813a 100644
--- a/osu.Game/Rulesets/Objects/HitObject.cs
+++ b/osu.Game/Rulesets/Objects/HitObject.cs
@@ -175,10 +175,10 @@ namespace osu.Game.Rulesets.Objects
/// Returns the end time of this object.
///
///
- /// This returns the where available, falling back to otherwise.
+ /// This returns the where available, falling back to otherwise.
///
/// The object.
/// The end time of this object.
- public static double GetEndTime(this HitObject hitObject) => (hitObject as IHasEndTime)?.EndTime ?? hitObject.StartTime;
+ public static double GetEndTime(this HitObject hitObject) => (hitObject as IHasDuration)?.EndTime ?? hitObject.StartTime;
}
}
diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs
index 43e8d01297..c10c8dc30f 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs
@@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
};
}
- protected override HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double endTime)
+ protected override HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double duration)
{
// Convert spinners don't create the new combo themselves, but force the next non-spinner hitobject to create a new combo
// Their combo offset is still added to that next hitobject's combo index
@@ -65,11 +65,11 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
return new ConvertSpinner
{
- EndTime = endTime
+ Duration = duration
};
}
- protected override HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double endTime)
+ protected override HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double duration)
{
return null;
}
diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs
index 9de311c9d7..014494ec54 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs
@@ -8,11 +8,11 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
///
/// Legacy osu!catch Spinner-type, used for parsing Beatmaps.
///
- internal sealed class ConvertSpinner : ConvertHitObject, IHasEndTime, IHasXPosition, IHasCombo
+ internal sealed class ConvertSpinner : ConvertHitObject, IHasDuration, IHasXPosition, IHasCombo
{
- public double EndTime { get; set; }
+ public double EndTime => StartTime + Duration;
- public double Duration => EndTime - StartTime;
+ public double Duration { get; set; }
public float X => 256; // Required for CatchBeatmapConverter
diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs
index 9a60a0a75c..9e936c7717 100644
--- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs
@@ -189,9 +189,9 @@ namespace osu.Game.Rulesets.Objects.Legacy
}
else if (type.HasFlag(LegacyHitObjectType.Spinner))
{
- double endTime = Math.Max(startTime, Parsing.ParseDouble(split[5]) + Offset);
+ double duration = Math.Max(0, Parsing.ParseDouble(split[5]) + Offset - startTime);
- result = CreateSpinner(new Vector2(512, 384) / 2, combo, comboOffset, endTime);
+ result = CreateSpinner(new Vector2(512, 384) / 2, combo, comboOffset, duration);
if (split.Length > 6)
readCustomSampleBanks(split[6], bankInfo);
@@ -209,7 +209,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
readCustomSampleBanks(string.Join(":", ss.Skip(1)), bankInfo);
}
- result = CreateHold(pos, combo, comboOffset, endTime + Offset);
+ result = CreateHold(pos, combo, comboOffset, endTime + Offset - startTime);
}
if (result == null)
@@ -321,9 +321,9 @@ namespace osu.Game.Rulesets.Objects.Legacy
/// The position of the hit object.
/// Whether the hit object creates a new combo.
/// When starting a new combo, the offset of the new combo relative to the current one.
- /// The spinner end time.
+ /// The spinner duration.
/// The hit object.
- protected abstract HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double endTime);
+ protected abstract HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double duration);
///
/// Creates a legacy Hold-type hit object.
@@ -331,8 +331,8 @@ namespace osu.Game.Rulesets.Objects.Legacy
/// The position of the hit object.
/// Whether the hit object creates a new combo.
/// When starting a new combo, the offset of the new combo relative to the current one.
- /// The hold end time.
- protected abstract HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double endTime);
+ /// The hold duration.
+ protected abstract HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double duration);
private List convertSoundType(LegacyHitSoundType type, SampleBankInfo bankInfo)
{
diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs
index 924182b265..c522dc623c 100644
--- a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs
@@ -9,7 +9,10 @@ using osu.Game.Beatmaps.ControlPoints;
namespace osu.Game.Rulesets.Objects.Legacy
{
- internal abstract class ConvertSlider : ConvertHitObject, IHasCurve, IHasLegacyLastTickOffset
+ internal abstract class ConvertSlider : ConvertHitObject, IHasPathWithRepeats, IHasLegacyLastTickOffset,
+#pragma warning disable 618
+ IHasCurve
+#pragma warning restore 618
{
///
/// Scoring distance with a speed-adjusted beat length of 1 second.
@@ -26,13 +29,13 @@ namespace osu.Game.Rulesets.Objects.Legacy
public List> NodeSamples { get; set; }
public int RepeatCount { get; set; }
- public double EndTime
+ public double Duration
{
- get => StartTime + this.SpanCount() * Distance / Velocity;
+ get => this.SpanCount() * Distance / Velocity;
set => throw new System.NotSupportedException($"Adjust via {nameof(RepeatCount)} instead"); // can be implemented if/when needed.
}
- public double Duration => EndTime - StartTime;
+ public double EndTime => StartTime + Duration;
public double Velocity = 1;
diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs
index f94c4aaa75..bc64518f40 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs
@@ -37,21 +37,21 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania
};
}
- protected override HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double endTime)
+ protected override HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double duration)
{
return new ConvertSpinner
{
X = position.X,
- EndTime = endTime
+ Duration = duration
};
}
- protected override HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double endTime)
+ protected override HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double duration)
{
return new ConvertHold
{
X = position.X,
- EndTime = endTime
+ Duration = duration
};
}
}
diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHold.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHold.cs
index 1d92d638dd..2fa4766c1d 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHold.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHold.cs
@@ -5,12 +5,12 @@ using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Rulesets.Objects.Legacy.Mania
{
- internal sealed class ConvertHold : ConvertHitObject, IHasXPosition, IHasEndTime
+ internal sealed class ConvertHold : ConvertHitObject, IHasXPosition, IHasDuration
{
public float X { get; set; }
- public double EndTime { get; set; }
+ public double Duration { get; set; }
- public double Duration => EndTime - StartTime;
+ public double EndTime => StartTime + Duration;
}
}
diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSpinner.cs
index 7dc13e27cd..c05aaceb9c 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSpinner.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSpinner.cs
@@ -8,11 +8,11 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania
///
/// Legacy osu!mania Spinner-type, used for parsing Beatmaps.
///
- internal sealed class ConvertSpinner : ConvertHitObject, IHasEndTime, IHasXPosition
+ internal sealed class ConvertSpinner : ConvertHitObject, IHasDuration, IHasXPosition
{
- public double EndTime { get; set; }
+ public double Duration { get; set; }
- public double Duration => EndTime - StartTime;
+ public double EndTime => StartTime + Duration;
public float X { get; set; }
}
diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs
index b95ec703b6..75ecab0b8f 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs
@@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
};
}
- protected override HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double endTime)
+ protected override HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double duration)
{
// Convert spinners don't create the new combo themselves, but force the next non-spinner hitobject to create a new combo
// Their combo offset is still added to that next hitobject's combo index
@@ -66,11 +66,11 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
return new ConvertSpinner
{
Position = position,
- EndTime = endTime
+ Duration = duration
};
}
- protected override HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double endTime)
+ protected override HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double duration)
{
return null;
}
diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs
index 8b21aab411..e9e5ca8c94 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs
@@ -9,11 +9,11 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
///
/// Legacy osu! Spinner-type, used for parsing Beatmaps.
///
- internal sealed class ConvertSpinner : ConvertHitObject, IHasEndTime, IHasPosition, IHasCombo
+ internal sealed class ConvertSpinner : ConvertHitObject, IHasDuration, IHasPosition, IHasCombo
{
- public double EndTime { get; set; }
+ public double Duration { get; set; }
- public double Duration => EndTime - StartTime;
+ public double EndTime => StartTime + Duration;
public Vector2 Position { get; set; }
diff --git a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs
index db65a61c90..13e3e84c6a 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs
@@ -33,15 +33,15 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko
};
}
- protected override HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double endTime)
+ protected override HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double duration)
{
return new ConvertSpinner
{
- EndTime = endTime
+ Duration = duration
};
}
- protected override HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double endTime)
+ protected override HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double duration)
{
return null;
}
diff --git a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSpinner.cs
index 8e28487f2f..1d5ecb1ef3 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSpinner.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSpinner.cs
@@ -8,10 +8,10 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko
///
/// Legacy osu!taiko Spinner-type, used for parsing Beatmaps.
///
- internal sealed class ConvertSpinner : ConvertHitObject, IHasEndTime
+ internal sealed class ConvertSpinner : ConvertHitObject, IHasDuration
{
- public double EndTime { get; set; }
+ public double Duration { get; set; }
- public double Duration => EndTime - StartTime;
+ public double EndTime => StartTime + Duration;
}
}
diff --git a/osu.Game/Rulesets/Objects/Types/IHasCurve.cs b/osu.Game/Rulesets/Objects/Types/IHasCurve.cs
index e98a888bd7..26f50ffa31 100644
--- a/osu.Game/Rulesets/Objects/Types/IHasCurve.cs
+++ b/osu.Game/Rulesets/Objects/Types/IHasCurve.cs
@@ -1,13 +1,12 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System;
using osuTK;
namespace osu.Game.Rulesets.Objects.Types
{
- ///
- /// A HitObject that has a curve.
- ///
+ [Obsolete("Use IHasPathWithRepeats instead.")] // can be removed 20201126
public interface IHasCurve : IHasDistance, IHasRepeats
{
///
@@ -16,6 +15,8 @@ namespace osu.Game.Rulesets.Objects.Types
SliderPath Path { get; }
}
+#pragma warning disable 618
+ [Obsolete("Use IHasPathWithRepeats instead.")] // can be removed 20201126
public static class HasCurveExtensions
{
///
@@ -50,4 +51,5 @@ namespace osu.Game.Rulesets.Objects.Types
public static int SpanAt(this IHasCurve obj, double progress)
=> (int)(progress * obj.SpanCount());
}
+#pragma warning restore 618
}
diff --git a/osu.Game/Rulesets/Objects/Types/IHasDistance.cs b/osu.Game/Rulesets/Objects/Types/IHasDistance.cs
index e7f552115e..b497ca5da3 100644
--- a/osu.Game/Rulesets/Objects/Types/IHasDistance.cs
+++ b/osu.Game/Rulesets/Objects/Types/IHasDistance.cs
@@ -6,7 +6,7 @@ namespace osu.Game.Rulesets.Objects.Types
///
/// A HitObject that has a positional length.
///
- public interface IHasDistance : IHasEndTime
+ public interface IHasDistance : IHasDuration
{
///
/// The positional length of the HitObject.
diff --git a/osu.Game/Rulesets/Objects/Types/IHasDuration.cs b/osu.Game/Rulesets/Objects/Types/IHasDuration.cs
new file mode 100644
index 0000000000..185fd5977b
--- /dev/null
+++ b/osu.Game/Rulesets/Objects/Types/IHasDuration.cs
@@ -0,0 +1,34 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using Newtonsoft.Json;
+
+namespace osu.Game.Rulesets.Objects.Types
+{
+ ///
+ /// A HitObject that ends at a different time than its start time.
+ ///
+#pragma warning disable 618
+ public interface IHasDuration : IHasEndTime
+#pragma warning restore 618
+ {
+ double IHasEndTime.EndTime
+ {
+ get => EndTime;
+ set => Duration = (Duration - EndTime) + value;
+ }
+
+ double IHasEndTime.Duration => Duration;
+
+ ///
+ /// The time at which the HitObject ends.
+ ///
+ new double EndTime { get; }
+
+ ///
+ /// The duration of the HitObject.
+ ///
+ [JsonIgnore]
+ new double Duration { get; set; }
+ }
+}
diff --git a/osu.Game/Rulesets/Objects/Types/IHasEndTime.cs b/osu.Game/Rulesets/Objects/Types/IHasEndTime.cs
index bc7103c60d..c3769c5909 100644
--- a/osu.Game/Rulesets/Objects/Types/IHasEndTime.cs
+++ b/osu.Game/Rulesets/Objects/Types/IHasEndTime.cs
@@ -1,6 +1,7 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System;
using Newtonsoft.Json;
namespace osu.Game.Rulesets.Objects.Types
@@ -8,6 +9,7 @@ namespace osu.Game.Rulesets.Objects.Types
///
/// A HitObject that ends at a different time than its start time.
///
+ [Obsolete("Use IHasDuration instead.")] // can be removed 20201126
public interface IHasEndTime
{
///
diff --git a/osu.Game/Rulesets/Objects/Types/IHasPath.cs b/osu.Game/Rulesets/Objects/Types/IHasPath.cs
new file mode 100644
index 0000000000..567c24a4a2
--- /dev/null
+++ b/osu.Game/Rulesets/Objects/Types/IHasPath.cs
@@ -0,0 +1,13 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+namespace osu.Game.Rulesets.Objects.Types
+{
+ public interface IHasPath : IHasDistance
+ {
+ ///
+ /// The curve.
+ ///
+ SliderPath Path { get; }
+ }
+}
diff --git a/osu.Game/Rulesets/Objects/Types/IHasPathWithRepeats.cs b/osu.Game/Rulesets/Objects/Types/IHasPathWithRepeats.cs
new file mode 100644
index 0000000000..279946b44e
--- /dev/null
+++ b/osu.Game/Rulesets/Objects/Types/IHasPathWithRepeats.cs
@@ -0,0 +1,50 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osuTK;
+
+namespace osu.Game.Rulesets.Objects.Types
+{
+ ///
+ /// A HitObject that has a curve.
+ ///
+ // ReSharper disable once RedundantExtendsListEntry
+ public interface IHasPathWithRepeats : IHasPath, IHasRepeats
+ {
+ }
+
+ public static class HasPathWithRepeatsExtensions
+ {
+ ///
+ /// Computes the position on the curve relative to how much of the has been completed.
+ ///
+ /// The curve.
+ /// [0, 1] where 0 is the start time of the and 1 is the end time of the .
+ /// The position on the curve.
+ public static Vector2 CurvePositionAt(this IHasPathWithRepeats obj, double progress)
+ => obj.Path.PositionAt(obj.ProgressAt(progress));
+
+ ///
+ /// Computes the progress along the curve relative to how much of the has been completed.
+ ///
+ /// The curve.
+ /// [0, 1] where 0 is the start time of the and 1 is the end time of the .
+ /// [0, 1] where 0 is the beginning of the curve and 1 is the end of the curve.
+ public static double ProgressAt(this IHasPathWithRepeats obj, double progress)
+ {
+ double p = progress * obj.SpanCount() % 1;
+ if (obj.SpanAt(progress) % 2 == 1)
+ p = 1 - p;
+ return p;
+ }
+
+ ///
+ /// Determines which span of the curve the progress point is on.
+ ///
+ /// The curve.
+ /// [0, 1] where 0 is the beginning of the curve and 1 is the end of the curve.
+ /// [0, SpanCount) where 0 is the first run.
+ public static int SpanAt(this IHasPathWithRepeats obj, double progress)
+ => (int)(progress * obj.SpanCount());
+ }
+}
diff --git a/osu.Game/Rulesets/Objects/Types/IHasRepeats.cs b/osu.Game/Rulesets/Objects/Types/IHasRepeats.cs
index 256b1f3963..7a3fb16196 100644
--- a/osu.Game/Rulesets/Objects/Types/IHasRepeats.cs
+++ b/osu.Game/Rulesets/Objects/Types/IHasRepeats.cs
@@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Objects.Types
///
/// A HitObject that spans some length.
///
- public interface IHasRepeats : IHasEndTime
+ public interface IHasRepeats : IHasDuration
{
///
/// The amount of times the HitObject repeats.
diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs
index c817d84d5c..0dc3324559 100644
--- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs
+++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs
@@ -270,7 +270,7 @@ namespace osu.Game.Rulesets.UI.Scrolling
// Cant use AddOnce() since the delegate is re-constructed every invocation
private void computeInitialStateRecursive(DrawableHitObject hitObject) => hitObject.Schedule(() =>
{
- if (hitObject.HitObject is IHasEndTime e)
+ if (hitObject.HitObject is IHasDuration e)
{
switch (direction.Value)
{
diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs
index dd2f7a833e..b95b3842b3 100644
--- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs
@@ -72,7 +72,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
shadowComponents.Add(circle);
- if (hitObject is IHasEndTime)
+ if (hitObject is IHasDuration)
{
DragBar dragBarUnderlay;
Container extensionBar;
@@ -290,13 +290,13 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
repeatHitObject.RepeatCount = proposedCount;
break;
- case IHasEndTime endTimeHitObject:
+ case IHasDuration endTimeHitObject:
var snappedTime = Math.Max(hitObject.StartTime, beatSnapProvider.SnapTime(time));
if (endTimeHitObject.EndTime == snappedTime)
return;
- endTimeHitObject.EndTime = snappedTime;
+ endTimeHitObject.Duration = snappedTime - hitObject.StartTime;
break;
}
diff --git a/osu.Game/Screens/Ranking/ScorePanelList.cs b/osu.Game/Screens/Ranking/ScorePanelList.cs
index 18db3f2af4..1142297274 100644
--- a/osu.Game/Screens/Ranking/ScorePanelList.cs
+++ b/osu.Game/Screens/Ranking/ScorePanelList.cs
@@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Bindables;
@@ -107,6 +108,9 @@ namespace osu.Game.Screens.Ranking
// Find the panel corresponding to the new score.
expandedPanel = flow.SingleOrDefault(p => p.Score == score.NewValue);
+ // handle horizontal scroll only when not hovering the expanded panel.
+ scroll.HandleScroll = () => expandedPanel?.IsHovered != true;
+
if (expandedPanel == null)
return;
@@ -166,6 +170,11 @@ namespace osu.Game.Screens.Ranking
///
public float? InstantScrollTarget;
+ ///
+ /// Whether this container should handle scroll trigger events.
+ ///
+ public Func HandleScroll;
+
protected override void UpdateAfterChildren()
{
if (InstantScrollTarget != null)
@@ -177,9 +186,9 @@ namespace osu.Game.Screens.Ranking
base.UpdateAfterChildren();
}
- public override bool HandlePositionalInput => false;
+ public override bool HandlePositionalInput => HandleScroll();
- public override bool HandleNonPositionalInput => false;
+ public override bool HandleNonPositionalInput => HandleScroll();
}
}
}
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 3d2a4f3081..305e4e0a92 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -19,7 +19,7 @@
-
+
diff --git a/osu.iOS.props b/osu.iOS.props
index 8a7f75b515..016f2ba35d 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -75,7 +75,7 @@
-
+
diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings
index e3b64c03b9..b9fc3de734 100644
--- a/osu.sln.DotSettings
+++ b/osu.sln.DotSettings
@@ -1,4 +1,4 @@
-
+
True
True
True
@@ -905,14 +905,17 @@ private void load()
True
True
True
+ True
True
True
True
True
True
True
+ True
True
True
True
+ True
True
True