mirror of
https://github.com/ppy/osu.git
synced 2024-11-11 09:27:29 +08:00
Merge pull request #28528 from bdach/break-autogeneration
This commit is contained in:
commit
9eb6752033
@ -74,7 +74,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Mods
|
|||||||
StartTime = 5000,
|
StartTime = 5000,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Breaks = new List<BreakPeriod>
|
Breaks =
|
||||||
{
|
{
|
||||||
new BreakPeriod(2000, 4000),
|
new BreakPeriod(2000, 4000),
|
||||||
}
|
}
|
||||||
|
@ -133,7 +133,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
|
|||||||
Autoplay = false,
|
Autoplay = false,
|
||||||
Beatmap = new Beatmap
|
Beatmap = new Beatmap
|
||||||
{
|
{
|
||||||
Breaks = new List<BreakPeriod>
|
Breaks =
|
||||||
{
|
{
|
||||||
new BreakPeriod(500, 2000),
|
new BreakPeriod(500, 2000),
|
||||||
},
|
},
|
||||||
|
@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
|
|||||||
StartTime = 5000,
|
StartTime = 5000,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Breaks = new List<BreakPeriod>
|
Breaks =
|
||||||
{
|
{
|
||||||
new BreakPeriod(2000, 4000),
|
new BreakPeriod(2000, 4000),
|
||||||
}
|
}
|
||||||
|
@ -132,7 +132,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
|
|||||||
Autoplay = false,
|
Autoplay = false,
|
||||||
Beatmap = new Beatmap
|
Beatmap = new Beatmap
|
||||||
{
|
{
|
||||||
Breaks = new List<BreakPeriod>
|
Breaks =
|
||||||
{
|
{
|
||||||
new BreakPeriod(500, 2000),
|
new BreakPeriod(500, 2000),
|
||||||
},
|
},
|
||||||
|
@ -177,7 +177,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Mods
|
|||||||
Autoplay = false,
|
Autoplay = false,
|
||||||
Beatmap = new Beatmap
|
Beatmap = new Beatmap
|
||||||
{
|
{
|
||||||
Breaks = new List<BreakPeriod>
|
Breaks =
|
||||||
{
|
{
|
||||||
new BreakPeriod(100, 1600),
|
new BreakPeriod(100, 1600),
|
||||||
},
|
},
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
@ -29,7 +28,7 @@ namespace osu.Game.Tests.Editing.Checks
|
|||||||
{
|
{
|
||||||
var beatmap = new Beatmap<HitObject>
|
var beatmap = new Beatmap<HitObject>
|
||||||
{
|
{
|
||||||
Breaks = new List<BreakPeriod>
|
Breaks =
|
||||||
{
|
{
|
||||||
new BreakPeriod(0, 649)
|
new BreakPeriod(0, 649)
|
||||||
}
|
}
|
||||||
@ -52,7 +51,7 @@ namespace osu.Game.Tests.Editing.Checks
|
|||||||
new HitCircle { StartTime = 0 },
|
new HitCircle { StartTime = 0 },
|
||||||
new HitCircle { StartTime = 1_200 }
|
new HitCircle { StartTime = 1_200 }
|
||||||
},
|
},
|
||||||
Breaks = new List<BreakPeriod>
|
Breaks =
|
||||||
{
|
{
|
||||||
new BreakPeriod(100, 751)
|
new BreakPeriod(100, 751)
|
||||||
}
|
}
|
||||||
@ -75,7 +74,7 @@ namespace osu.Game.Tests.Editing.Checks
|
|||||||
new HitCircle { StartTime = 0 },
|
new HitCircle { StartTime = 0 },
|
||||||
new HitCircle { StartTime = 1_298 }
|
new HitCircle { StartTime = 1_298 }
|
||||||
},
|
},
|
||||||
Breaks = new List<BreakPeriod>
|
Breaks =
|
||||||
{
|
{
|
||||||
new BreakPeriod(200, 850)
|
new BreakPeriod(200, 850)
|
||||||
}
|
}
|
||||||
@ -98,7 +97,7 @@ namespace osu.Game.Tests.Editing.Checks
|
|||||||
new HitCircle { StartTime = 0 },
|
new HitCircle { StartTime = 0 },
|
||||||
new HitCircle { StartTime = 1200 }
|
new HitCircle { StartTime = 1200 }
|
||||||
},
|
},
|
||||||
Breaks = new List<BreakPeriod>
|
Breaks =
|
||||||
{
|
{
|
||||||
new BreakPeriod(1398, 2300)
|
new BreakPeriod(1398, 2300)
|
||||||
}
|
}
|
||||||
@ -121,7 +120,7 @@ namespace osu.Game.Tests.Editing.Checks
|
|||||||
new HitCircle { StartTime = 1100 },
|
new HitCircle { StartTime = 1100 },
|
||||||
new HitCircle { StartTime = 1500 }
|
new HitCircle { StartTime = 1500 }
|
||||||
},
|
},
|
||||||
Breaks = new List<BreakPeriod>
|
Breaks =
|
||||||
{
|
{
|
||||||
new BreakPeriod(0, 652)
|
new BreakPeriod(0, 652)
|
||||||
}
|
}
|
||||||
@ -145,7 +144,7 @@ namespace osu.Game.Tests.Editing.Checks
|
|||||||
new HitCircle { StartTime = 1_297 },
|
new HitCircle { StartTime = 1_297 },
|
||||||
new HitCircle { StartTime = 1_298 }
|
new HitCircle { StartTime = 1_298 }
|
||||||
},
|
},
|
||||||
Breaks = new List<BreakPeriod>
|
Breaks =
|
||||||
{
|
{
|
||||||
new BreakPeriod(200, 850)
|
new BreakPeriod(200, 850)
|
||||||
}
|
}
|
||||||
@ -168,7 +167,7 @@ namespace osu.Game.Tests.Editing.Checks
|
|||||||
new HitCircle { StartTime = 0 },
|
new HitCircle { StartTime = 0 },
|
||||||
new HitCircle { StartTime = 1_300 }
|
new HitCircle { StartTime = 1_300 }
|
||||||
},
|
},
|
||||||
Breaks = new List<BreakPeriod>
|
Breaks =
|
||||||
{
|
{
|
||||||
new BreakPeriod(200, 850)
|
new BreakPeriod(200, 850)
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ namespace osu.Game.Tests.Editing.Checks
|
|||||||
new HitCircle { StartTime = 0 },
|
new HitCircle { StartTime = 0 },
|
||||||
new HitCircle { StartTime = 40_000 }
|
new HitCircle { StartTime = 40_000 }
|
||||||
},
|
},
|
||||||
Breaks = new List<BreakPeriod>
|
Breaks =
|
||||||
{
|
{
|
||||||
new BreakPeriod(10_000, 21_000)
|
new BreakPeriod(10_000, 21_000)
|
||||||
}
|
}
|
||||||
|
300
osu.Game.Tests/Editing/TestSceneEditorBeatmapProcessor.cs
Normal file
300
osu.Game.Tests/Editing/TestSceneEditorBeatmapProcessor.cs
Normal file
@ -0,0 +1,300 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Beatmaps.Timing;
|
||||||
|
using osu.Game.Rulesets.Osu;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Screens.Edit;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Editing
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class TestSceneEditorBeatmapProcessor
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void TestEmptyBeatmap()
|
||||||
|
{
|
||||||
|
var controlPoints = new ControlPointInfo();
|
||||||
|
controlPoints.Add(0, new TimingControlPoint { BeatLength = 500 });
|
||||||
|
var beatmap = new Beatmap
|
||||||
|
{
|
||||||
|
ControlPointInfo = controlPoints,
|
||||||
|
};
|
||||||
|
|
||||||
|
var beatmapProcessor = new EditorBeatmapProcessor(beatmap, new OsuRuleset());
|
||||||
|
beatmapProcessor.PreProcess();
|
||||||
|
beatmapProcessor.PostProcess();
|
||||||
|
|
||||||
|
Assert.That(beatmap.Breaks, Is.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestSingleObjectBeatmap()
|
||||||
|
{
|
||||||
|
var controlPoints = new ControlPointInfo();
|
||||||
|
controlPoints.Add(0, new TimingControlPoint { BeatLength = 500 });
|
||||||
|
var beatmap = new Beatmap
|
||||||
|
{
|
||||||
|
ControlPointInfo = controlPoints,
|
||||||
|
HitObjects =
|
||||||
|
{
|
||||||
|
new HitCircle { StartTime = 1000 },
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var beatmapProcessor = new EditorBeatmapProcessor(beatmap, new OsuRuleset());
|
||||||
|
beatmapProcessor.PreProcess();
|
||||||
|
beatmapProcessor.PostProcess();
|
||||||
|
|
||||||
|
Assert.That(beatmap.Breaks, Is.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestTwoObjectsCloseTogether()
|
||||||
|
{
|
||||||
|
var controlPoints = new ControlPointInfo();
|
||||||
|
controlPoints.Add(0, new TimingControlPoint { BeatLength = 500 });
|
||||||
|
var beatmap = new Beatmap
|
||||||
|
{
|
||||||
|
ControlPointInfo = controlPoints,
|
||||||
|
HitObjects =
|
||||||
|
{
|
||||||
|
new HitCircle { StartTime = 1000 },
|
||||||
|
new HitCircle { StartTime = 2000 },
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var beatmapProcessor = new EditorBeatmapProcessor(beatmap, new OsuRuleset());
|
||||||
|
beatmapProcessor.PreProcess();
|
||||||
|
beatmapProcessor.PostProcess();
|
||||||
|
|
||||||
|
Assert.That(beatmap.Breaks, Is.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestTwoObjectsFarApart()
|
||||||
|
{
|
||||||
|
var controlPoints = new ControlPointInfo();
|
||||||
|
controlPoints.Add(0, new TimingControlPoint { BeatLength = 500 });
|
||||||
|
var beatmap = new Beatmap
|
||||||
|
{
|
||||||
|
ControlPointInfo = controlPoints,
|
||||||
|
HitObjects =
|
||||||
|
{
|
||||||
|
new HitCircle { StartTime = 1000 },
|
||||||
|
new HitCircle { StartTime = 5000 },
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var beatmapProcessor = new EditorBeatmapProcessor(beatmap, new OsuRuleset());
|
||||||
|
beatmapProcessor.PreProcess();
|
||||||
|
beatmapProcessor.PostProcess();
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(beatmap.Breaks, Has.Count.EqualTo(1));
|
||||||
|
Assert.That(beatmap.Breaks[0].StartTime, Is.EqualTo(1200));
|
||||||
|
Assert.That(beatmap.Breaks[0].EndTime, Is.EqualTo(4000));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestBreaksAreFused()
|
||||||
|
{
|
||||||
|
var controlPoints = new ControlPointInfo();
|
||||||
|
controlPoints.Add(0, new TimingControlPoint { BeatLength = 500 });
|
||||||
|
var beatmap = new Beatmap
|
||||||
|
{
|
||||||
|
ControlPointInfo = controlPoints,
|
||||||
|
HitObjects =
|
||||||
|
{
|
||||||
|
new HitCircle { StartTime = 1000 },
|
||||||
|
new HitCircle { StartTime = 9000 },
|
||||||
|
},
|
||||||
|
Breaks =
|
||||||
|
{
|
||||||
|
new BreakPeriod(1200, 4000),
|
||||||
|
new BreakPeriod(5200, 8000),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var beatmapProcessor = new EditorBeatmapProcessor(beatmap, new OsuRuleset());
|
||||||
|
beatmapProcessor.PreProcess();
|
||||||
|
beatmapProcessor.PostProcess();
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(beatmap.Breaks, Has.Count.EqualTo(1));
|
||||||
|
Assert.That(beatmap.Breaks[0].StartTime, Is.EqualTo(1200));
|
||||||
|
Assert.That(beatmap.Breaks[0].EndTime, Is.EqualTo(8000));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestBreaksAreSplit()
|
||||||
|
{
|
||||||
|
var controlPoints = new ControlPointInfo();
|
||||||
|
controlPoints.Add(0, new TimingControlPoint { BeatLength = 500 });
|
||||||
|
var beatmap = new Beatmap
|
||||||
|
{
|
||||||
|
ControlPointInfo = controlPoints,
|
||||||
|
HitObjects =
|
||||||
|
{
|
||||||
|
new HitCircle { StartTime = 1000 },
|
||||||
|
new HitCircle { StartTime = 5000 },
|
||||||
|
new HitCircle { StartTime = 9000 },
|
||||||
|
},
|
||||||
|
Breaks =
|
||||||
|
{
|
||||||
|
new BreakPeriod(1200, 8000),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var beatmapProcessor = new EditorBeatmapProcessor(beatmap, new OsuRuleset());
|
||||||
|
beatmapProcessor.PreProcess();
|
||||||
|
beatmapProcessor.PostProcess();
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(beatmap.Breaks, Has.Count.EqualTo(2));
|
||||||
|
Assert.That(beatmap.Breaks[0].StartTime, Is.EqualTo(1200));
|
||||||
|
Assert.That(beatmap.Breaks[0].EndTime, Is.EqualTo(4000));
|
||||||
|
Assert.That(beatmap.Breaks[1].StartTime, Is.EqualTo(5200));
|
||||||
|
Assert.That(beatmap.Breaks[1].EndTime, Is.EqualTo(8000));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestBreaksAreNudged()
|
||||||
|
{
|
||||||
|
var controlPoints = new ControlPointInfo();
|
||||||
|
controlPoints.Add(0, new TimingControlPoint { BeatLength = 500 });
|
||||||
|
var beatmap = new Beatmap
|
||||||
|
{
|
||||||
|
ControlPointInfo = controlPoints,
|
||||||
|
HitObjects =
|
||||||
|
{
|
||||||
|
new HitCircle { StartTime = 1100 },
|
||||||
|
new HitCircle { StartTime = 9000 },
|
||||||
|
},
|
||||||
|
Breaks =
|
||||||
|
{
|
||||||
|
new BreakPeriod(1200, 8000),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var beatmapProcessor = new EditorBeatmapProcessor(beatmap, new OsuRuleset());
|
||||||
|
beatmapProcessor.PreProcess();
|
||||||
|
beatmapProcessor.PostProcess();
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(beatmap.Breaks, Has.Count.EqualTo(1));
|
||||||
|
Assert.That(beatmap.Breaks[0].StartTime, Is.EqualTo(1300));
|
||||||
|
Assert.That(beatmap.Breaks[0].EndTime, Is.EqualTo(8000));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestManualBreaksAreNotFused()
|
||||||
|
{
|
||||||
|
var controlPoints = new ControlPointInfo();
|
||||||
|
controlPoints.Add(0, new TimingControlPoint { BeatLength = 500 });
|
||||||
|
var beatmap = new Beatmap
|
||||||
|
{
|
||||||
|
ControlPointInfo = controlPoints,
|
||||||
|
HitObjects =
|
||||||
|
{
|
||||||
|
new HitCircle { StartTime = 1000 },
|
||||||
|
new HitCircle { StartTime = 9000 },
|
||||||
|
},
|
||||||
|
Breaks =
|
||||||
|
{
|
||||||
|
new ManualBreakPeriod(1200, 4000),
|
||||||
|
new ManualBreakPeriod(5200, 8000),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var beatmapProcessor = new EditorBeatmapProcessor(beatmap, new OsuRuleset());
|
||||||
|
beatmapProcessor.PreProcess();
|
||||||
|
beatmapProcessor.PostProcess();
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(beatmap.Breaks, Has.Count.EqualTo(2));
|
||||||
|
Assert.That(beatmap.Breaks[0].StartTime, Is.EqualTo(1200));
|
||||||
|
Assert.That(beatmap.Breaks[0].EndTime, Is.EqualTo(4000));
|
||||||
|
Assert.That(beatmap.Breaks[1].StartTime, Is.EqualTo(5200));
|
||||||
|
Assert.That(beatmap.Breaks[1].EndTime, Is.EqualTo(8000));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestManualBreaksAreSplit()
|
||||||
|
{
|
||||||
|
var controlPoints = new ControlPointInfo();
|
||||||
|
controlPoints.Add(0, new TimingControlPoint { BeatLength = 500 });
|
||||||
|
var beatmap = new Beatmap
|
||||||
|
{
|
||||||
|
ControlPointInfo = controlPoints,
|
||||||
|
HitObjects =
|
||||||
|
{
|
||||||
|
new HitCircle { StartTime = 1000 },
|
||||||
|
new HitCircle { StartTime = 5000 },
|
||||||
|
new HitCircle { StartTime = 9000 },
|
||||||
|
},
|
||||||
|
Breaks =
|
||||||
|
{
|
||||||
|
new ManualBreakPeriod(1200, 8000),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var beatmapProcessor = new EditorBeatmapProcessor(beatmap, new OsuRuleset());
|
||||||
|
beatmapProcessor.PreProcess();
|
||||||
|
beatmapProcessor.PostProcess();
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(beatmap.Breaks, Has.Count.EqualTo(2));
|
||||||
|
Assert.That(beatmap.Breaks[0].StartTime, Is.EqualTo(1200));
|
||||||
|
Assert.That(beatmap.Breaks[0].EndTime, Is.EqualTo(4000));
|
||||||
|
Assert.That(beatmap.Breaks[1].StartTime, Is.EqualTo(5200));
|
||||||
|
Assert.That(beatmap.Breaks[1].EndTime, Is.EqualTo(8000));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestManualBreaksAreNotNudged()
|
||||||
|
{
|
||||||
|
var controlPoints = new ControlPointInfo();
|
||||||
|
controlPoints.Add(0, new TimingControlPoint { BeatLength = 500 });
|
||||||
|
var beatmap = new Beatmap
|
||||||
|
{
|
||||||
|
ControlPointInfo = controlPoints,
|
||||||
|
HitObjects =
|
||||||
|
{
|
||||||
|
new HitCircle { StartTime = 1000 },
|
||||||
|
new HitCircle { StartTime = 9000 },
|
||||||
|
},
|
||||||
|
Breaks =
|
||||||
|
{
|
||||||
|
new ManualBreakPeriod(1200, 8800),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var beatmapProcessor = new EditorBeatmapProcessor(beatmap, new OsuRuleset());
|
||||||
|
beatmapProcessor.PreProcess();
|
||||||
|
beatmapProcessor.PostProcess();
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(beatmap.Breaks, Has.Count.EqualTo(1));
|
||||||
|
Assert.That(beatmap.Breaks[0].StartTime, Is.EqualTo(1200));
|
||||||
|
Assert.That(beatmap.Breaks[0].EndTime, Is.EqualTo(8800));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,10 +4,12 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Tests.Beatmaps;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Editing
|
namespace osu.Game.Tests.Visual.Editing
|
||||||
{
|
{
|
||||||
@ -15,6 +17,8 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
{
|
{
|
||||||
protected override Ruleset CreateEditorRuleset() => new OsuRuleset();
|
protected override Ruleset CreateEditorRuleset() => new OsuRuleset();
|
||||||
|
|
||||||
|
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset, false);
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestSelectedObjects()
|
public void TestSelectedObjects()
|
||||||
{
|
{
|
||||||
|
@ -8,6 +8,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Game.IO.Serialization.Converters;
|
using osu.Game.IO.Serialization.Converters;
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps
|
namespace osu.Game.Beatmaps
|
||||||
@ -61,7 +62,7 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
public ControlPointInfo ControlPointInfo { get; set; } = new ControlPointInfo();
|
public ControlPointInfo ControlPointInfo { get; set; } = new ControlPointInfo();
|
||||||
|
|
||||||
public List<BreakPeriod> Breaks { get; set; } = new List<BreakPeriod>();
|
public BindableList<BreakPeriod> Breaks { get; set; } = new BindableList<BreakPeriod>();
|
||||||
|
|
||||||
public List<string> UnhandledEventLines { get; set; } = new List<string>();
|
public List<string> UnhandledEventLines { get; set; } = new List<string>();
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Beatmaps.Timing;
|
using osu.Game.Beatmaps.Timing;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
@ -40,7 +41,7 @@ namespace osu.Game.Beatmaps
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The breaks in this beatmap.
|
/// The breaks in this beatmap.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
List<BreakPeriod> Breaks { get; }
|
BindableList<BreakPeriod> Breaks { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// All lines from the [Events] section which aren't handled in the encoding process yet.
|
/// All lines from the [Events] section which aren't handled in the encoding process yet.
|
||||||
|
@ -1,26 +1,44 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps.Timing
|
namespace osu.Game.Beatmaps.Timing
|
||||||
{
|
{
|
||||||
public class BreakPeriod
|
public class BreakPeriod : IEquatable<BreakPeriod>
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The minimum gap between the start of the break and the previous object.
|
||||||
|
/// </summary>
|
||||||
|
public const double GAP_BEFORE_BREAK = 200;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The minimum gap between the end of the break and the next object.
|
||||||
|
/// Based on osu! preempt time at AR=10.
|
||||||
|
/// See also: https://github.com/ppy/osu/issues/14330#issuecomment-1002158551
|
||||||
|
/// </summary>
|
||||||
|
public const double GAP_AFTER_BREAK = 450;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The minimum duration required for a break to have any effect.
|
/// The minimum duration required for a break to have any effect.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const double MIN_BREAK_DURATION = 650;
|
public const double MIN_BREAK_DURATION = 650;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The minimum required duration of a gap between two objects such that a break can be placed between them.
|
||||||
|
/// </summary>
|
||||||
|
public const double MIN_GAP_DURATION = GAP_BEFORE_BREAK + MIN_BREAK_DURATION + GAP_AFTER_BREAK;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The break start time.
|
/// The break start time.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public double StartTime;
|
public double StartTime { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The break end time.
|
/// The break end time.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public double EndTime;
|
public double EndTime { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The break duration.
|
/// The break duration.
|
||||||
@ -49,5 +67,14 @@ namespace osu.Game.Beatmaps.Timing
|
|||||||
/// <param name="time">The time to check in milliseconds.</param>
|
/// <param name="time">The time to check in milliseconds.</param>
|
||||||
/// <returns>Whether the time falls within this <see cref="BreakPeriod"/>.</returns>
|
/// <returns>Whether the time falls within this <see cref="BreakPeriod"/>.</returns>
|
||||||
public bool Contains(double time) => time >= StartTime && time <= EndTime - BreakOverlay.BREAK_FADE_DURATION;
|
public bool Contains(double time) => time >= StartTime && time <= EndTime - BreakOverlay.BREAK_FADE_DURATION;
|
||||||
|
|
||||||
|
public bool Intersects(BreakPeriod other) => StartTime <= other.EndTime && EndTime >= other.StartTime;
|
||||||
|
|
||||||
|
public virtual bool Equals(BreakPeriod? other) =>
|
||||||
|
other != null
|
||||||
|
&& StartTime == other.StartTime
|
||||||
|
&& EndTime == other.EndTime;
|
||||||
|
|
||||||
|
public override int GetHashCode() => HashCode.Combine(StartTime, EndTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ using System.Linq;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Audio.Track;
|
using osu.Framework.Audio.Track;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
@ -329,7 +330,7 @@ namespace osu.Game.Rulesets.Difficulty
|
|||||||
set => baseBeatmap.Difficulty = value;
|
set => baseBeatmap.Difficulty = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<BreakPeriod> Breaks => baseBeatmap.Breaks;
|
public BindableList<BreakPeriod> Breaks => baseBeatmap.Breaks;
|
||||||
public List<string> UnhandledEventLines => baseBeatmap.UnhandledEventLines;
|
public List<string> UnhandledEventLines => baseBeatmap.UnhandledEventLines;
|
||||||
|
|
||||||
public double TotalBreakTime => baseBeatmap.TotalBreakTime;
|
public double TotalBreakTime => baseBeatmap.TotalBreakTime;
|
||||||
|
@ -13,13 +13,7 @@ namespace osu.Game.Rulesets.Edit.Checks
|
|||||||
{
|
{
|
||||||
// Breaks may be off by 1 ms.
|
// Breaks may be off by 1 ms.
|
||||||
private const int leniency_threshold = 1;
|
private const int leniency_threshold = 1;
|
||||||
private const double minimum_gap_before_break = 200;
|
|
||||||
|
|
||||||
// Break end time depends on the upcoming object's pre-empt time.
|
|
||||||
// As things stand, "pre-empt time" is only defined for osu! standard
|
|
||||||
// This is a generic value representing AR=10
|
|
||||||
// Relevant: https://github.com/ppy/osu/issues/14330#issuecomment-1002158551
|
|
||||||
private const double min_end_threshold = 450;
|
|
||||||
public CheckMetadata Metadata => new CheckMetadata(CheckCategory.Events, "Breaks not achievable using the editor");
|
public CheckMetadata Metadata => new CheckMetadata(CheckCategory.Events, "Breaks not achievable using the editor");
|
||||||
|
|
||||||
public IEnumerable<IssueTemplate> PossibleTemplates => new IssueTemplate[]
|
public IEnumerable<IssueTemplate> PossibleTemplates => new IssueTemplate[]
|
||||||
@ -45,8 +39,8 @@ namespace osu.Game.Rulesets.Edit.Checks
|
|||||||
if (previousObjectEndTimeIndex >= 0)
|
if (previousObjectEndTimeIndex >= 0)
|
||||||
{
|
{
|
||||||
double gapBeforeBreak = breakPeriod.StartTime - endTimes[previousObjectEndTimeIndex];
|
double gapBeforeBreak = breakPeriod.StartTime - endTimes[previousObjectEndTimeIndex];
|
||||||
if (gapBeforeBreak < minimum_gap_before_break - leniency_threshold)
|
if (gapBeforeBreak < BreakPeriod.GAP_BEFORE_BREAK - leniency_threshold)
|
||||||
yield return new IssueTemplateEarlyStart(this).Create(breakPeriod.StartTime, minimum_gap_before_break - gapBeforeBreak);
|
yield return new IssueTemplateEarlyStart(this).Create(breakPeriod.StartTime, BreakPeriod.GAP_BEFORE_BREAK - gapBeforeBreak);
|
||||||
}
|
}
|
||||||
|
|
||||||
int nextObjectStartTimeIndex = startTimes.BinarySearch(breakPeriod.EndTime);
|
int nextObjectStartTimeIndex = startTimes.BinarySearch(breakPeriod.EndTime);
|
||||||
@ -55,8 +49,8 @@ namespace osu.Game.Rulesets.Edit.Checks
|
|||||||
if (nextObjectStartTimeIndex < startTimes.Count)
|
if (nextObjectStartTimeIndex < startTimes.Count)
|
||||||
{
|
{
|
||||||
double gapAfterBreak = startTimes[nextObjectStartTimeIndex] - breakPeriod.EndTime;
|
double gapAfterBreak = startTimes[nextObjectStartTimeIndex] - breakPeriod.EndTime;
|
||||||
if (gapAfterBreak < min_end_threshold - leniency_threshold)
|
if (gapAfterBreak < BreakPeriod.GAP_AFTER_BREAK - leniency_threshold)
|
||||||
yield return new IssueTemplateLateEnd(this).Create(breakPeriod.StartTime, min_end_threshold - gapAfterBreak);
|
yield return new IssueTemplateLateEnd(this).Create(breakPeriod.StartTime, BreakPeriod.GAP_AFTER_BREAK - gapAfterBreak);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Game.Beatmaps.Timing;
|
using osu.Game.Beatmaps.Timing;
|
||||||
@ -14,35 +15,33 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class BreakPart : TimelinePart
|
public partial class BreakPart : TimelinePart
|
||||||
{
|
{
|
||||||
|
private readonly BindableList<BreakPeriod> breaks = new BindableList<BreakPeriod>();
|
||||||
|
|
||||||
protected override void LoadBeatmap(EditorBeatmap beatmap)
|
protected override void LoadBeatmap(EditorBeatmap beatmap)
|
||||||
{
|
{
|
||||||
base.LoadBeatmap(beatmap);
|
base.LoadBeatmap(beatmap);
|
||||||
|
|
||||||
|
breaks.UnbindAll();
|
||||||
|
breaks.BindTo(beatmap.Breaks);
|
||||||
|
breaks.BindCollectionChanged((_, _) =>
|
||||||
|
{
|
||||||
foreach (var breakPeriod in beatmap.Breaks)
|
foreach (var breakPeriod in beatmap.Breaks)
|
||||||
Add(new BreakVisualisation(breakPeriod));
|
Add(new BreakVisualisation(breakPeriod));
|
||||||
|
}, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private partial class BreakVisualisation : Circle
|
private partial class BreakVisualisation : Circle
|
||||||
{
|
{
|
||||||
private readonly BreakPeriod breakPeriod;
|
|
||||||
|
|
||||||
public BreakVisualisation(BreakPeriod breakPeriod)
|
public BreakVisualisation(BreakPeriod breakPeriod)
|
||||||
{
|
{
|
||||||
this.breakPeriod = breakPeriod;
|
|
||||||
|
|
||||||
RelativePositionAxes = Axes.X;
|
RelativePositionAxes = Axes.X;
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Update()
|
|
||||||
{
|
|
||||||
base.Update();
|
|
||||||
|
|
||||||
X = (float)breakPeriod.StartTime;
|
X = (float)breakPeriod.StartTime;
|
||||||
Width = (float)breakPeriod.Duration;
|
Width = (float)breakPeriod.Duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours) => Colour = colours.GreyCarmineLight;
|
private void load(OsuColour colours) => Colour = colours.Gray7;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ using System;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
@ -20,11 +21,11 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
{
|
{
|
||||||
public partial class TimelineBreak : CompositeDrawable
|
public partial class TimelineBreak : CompositeDrawable
|
||||||
{
|
{
|
||||||
public BreakPeriod Break { get; }
|
public Bindable<BreakPeriod> Break { get; } = new Bindable<BreakPeriod>();
|
||||||
|
|
||||||
public TimelineBreak(BreakPeriod b)
|
public TimelineBreak(BreakPeriod b)
|
||||||
{
|
{
|
||||||
Break = b;
|
Break.Value = b;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
@ -44,44 +45,50 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
Child = new Box
|
Child = new Box
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = colours.PurpleLight,
|
Colour = colours.Gray5,
|
||||||
Alpha = 0.4f,
|
Alpha = 0.7f,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
new DragHandle(Break, isStartHandle: true)
|
new DragHandle(isStartHandle: true)
|
||||||
{
|
{
|
||||||
|
Break = { BindTarget = Break },
|
||||||
Anchor = Anchor.TopLeft,
|
Anchor = Anchor.TopLeft,
|
||||||
Origin = Anchor.TopLeft,
|
Origin = Anchor.TopLeft,
|
||||||
Action = (time, breakPeriod) => breakPeriod.StartTime = time,
|
Action = (time, breakPeriod) => new ManualBreakPeriod(time, breakPeriod.EndTime),
|
||||||
},
|
},
|
||||||
new DragHandle(Break, isStartHandle: false)
|
new DragHandle(isStartHandle: false)
|
||||||
{
|
{
|
||||||
|
Break = { BindTarget = Break },
|
||||||
Anchor = Anchor.TopRight,
|
Anchor = Anchor.TopRight,
|
||||||
Origin = Anchor.TopRight,
|
Origin = Anchor.TopRight,
|
||||||
Action = (time, breakPeriod) => breakPeriod.EndTime = time,
|
Action = (time, breakPeriod) => new ManualBreakPeriod(breakPeriod.StartTime, time),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Update()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.Update();
|
base.LoadComplete();
|
||||||
|
|
||||||
X = (float)Break.StartTime;
|
Break.BindValueChanged(_ =>
|
||||||
Width = (float)Break.Duration;
|
{
|
||||||
|
X = (float)Break.Value.StartTime;
|
||||||
|
Width = (float)Break.Value.Duration;
|
||||||
|
}, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private partial class DragHandle : FillFlowContainer
|
private partial class DragHandle : FillFlowContainer
|
||||||
{
|
{
|
||||||
|
public Bindable<BreakPeriod> Break { get; } = new Bindable<BreakPeriod>();
|
||||||
|
|
||||||
public new Anchor Anchor
|
public new Anchor Anchor
|
||||||
{
|
{
|
||||||
get => base.Anchor;
|
get => base.Anchor;
|
||||||
init => base.Anchor = value;
|
init => base.Anchor = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Action<double, BreakPeriod>? Action { get; init; }
|
public Func<double, BreakPeriod, BreakPeriod>? Action { get; init; }
|
||||||
|
|
||||||
private readonly BreakPeriod breakPeriod;
|
|
||||||
private readonly bool isStartHandle;
|
private readonly bool isStartHandle;
|
||||||
|
|
||||||
private Container handle = null!;
|
private Container handle = null!;
|
||||||
@ -99,9 +106,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private OsuColour colours { get; set; } = null!;
|
private OsuColour colours { get; set; } = null!;
|
||||||
|
|
||||||
public DragHandle(BreakPeriod breakPeriod, bool isStartHandle)
|
public DragHandle(bool isStartHandle)
|
||||||
{
|
{
|
||||||
this.breakPeriod = breakPeriod;
|
|
||||||
this.isStartHandle = isStartHandle;
|
this.isStartHandle = isStartHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,13 +170,13 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
changeHandler?.BeginChange();
|
changeHandler?.BeginChange();
|
||||||
updateState();
|
updateState();
|
||||||
|
|
||||||
double min = beatmap.HitObjects.Last(ho => ho.GetEndTime() <= breakPeriod.StartTime).GetEndTime();
|
double min = beatmap.HitObjects.Last(ho => ho.GetEndTime() <= Break.Value.StartTime).GetEndTime();
|
||||||
double max = beatmap.HitObjects.First(ho => ho.StartTime >= breakPeriod.EndTime).StartTime;
|
double max = beatmap.HitObjects.First(ho => ho.StartTime >= Break.Value.EndTime).StartTime;
|
||||||
|
|
||||||
if (isStartHandle)
|
if (isStartHandle)
|
||||||
max = Math.Min(max, breakPeriod.EndTime - BreakPeriod.MIN_BREAK_DURATION);
|
max = Math.Min(max, Break.Value.EndTime - BreakPeriod.MIN_BREAK_DURATION);
|
||||||
else
|
else
|
||||||
min = Math.Max(min, breakPeriod.StartTime + BreakPeriod.MIN_BREAK_DURATION);
|
min = Math.Max(min, Break.Value.StartTime + BreakPeriod.MIN_BREAK_DURATION);
|
||||||
|
|
||||||
allowedDragRange = (min, max);
|
allowedDragRange = (min, max);
|
||||||
|
|
||||||
@ -183,11 +189,13 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
|
|
||||||
Debug.Assert(allowedDragRange != null);
|
Debug.Assert(allowedDragRange != null);
|
||||||
|
|
||||||
if (timeline.FindSnappedPositionAndTime(e.ScreenSpaceMousePosition).Time is double time
|
if (Action != null
|
||||||
|
&& timeline.FindSnappedPositionAndTime(e.ScreenSpaceMousePosition).Time is double time
|
||||||
&& time > allowedDragRange.Value.min
|
&& time > allowedDragRange.Value.min
|
||||||
&& time < allowedDragRange.Value.max)
|
&& time < allowedDragRange.Value.max)
|
||||||
{
|
{
|
||||||
Action?.Invoke(time, breakPeriod);
|
int index = beatmap.Breaks.IndexOf(Break.Value);
|
||||||
|
beatmap.Breaks[index] = Break.Value = Action.Invoke(time, Break.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateState();
|
updateState();
|
||||||
@ -204,7 +212,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
{
|
{
|
||||||
bool active = IsHovered || IsDragged;
|
bool active = IsHovered || IsDragged;
|
||||||
|
|
||||||
var colour = colours.PurpleLighter;
|
var colour = colours.Gray8;
|
||||||
if (active)
|
if (active)
|
||||||
colour = colour.Lighten(0.3f);
|
colour = colour.Lighten(0.3f);
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Specialized;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Caching;
|
using osu.Framework.Caching;
|
||||||
@ -27,8 +28,13 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
{
|
{
|
||||||
base.LoadBeatmap(beatmap);
|
base.LoadBeatmap(beatmap);
|
||||||
|
|
||||||
// TODO: this will have to be mutable soon enough
|
breaks.UnbindAll();
|
||||||
breaks.AddRange(beatmap.Breaks);
|
breaks.BindTo(beatmap.Breaks);
|
||||||
|
breaks.BindCollectionChanged((_, e) =>
|
||||||
|
{
|
||||||
|
if (e.Action != NotifyCollectionChangedAction.Replace)
|
||||||
|
breakCache.Invalidate();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
@ -56,14 +62,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
|
|
||||||
private void recreateBreaks()
|
private void recreateBreaks()
|
||||||
{
|
{
|
||||||
// Remove groups outside the visible range
|
Clear();
|
||||||
foreach (TimelineBreak drawableBreak in this)
|
|
||||||
{
|
|
||||||
if (!shouldBeVisible(drawableBreak.Break))
|
|
||||||
drawableBreak.Expire();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add remaining ones
|
|
||||||
for (int i = 0; i < breaks.Count; i++)
|
for (int i = 0; i < breaks.Count; i++)
|
||||||
{
|
{
|
||||||
var breakPeriod = breaks[i];
|
var breakPeriod = breaks[i];
|
||||||
@ -71,20 +71,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
if (!shouldBeVisible(breakPeriod))
|
if (!shouldBeVisible(breakPeriod))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
bool alreadyVisible = false;
|
|
||||||
|
|
||||||
foreach (var b in this)
|
|
||||||
{
|
|
||||||
if (ReferenceEquals(b.Break, breakPeriod))
|
|
||||||
{
|
|
||||||
alreadyVisible = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (alreadyVisible)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
Add(new TimelineBreak(breakPeriod));
|
Add(new TimelineBreak(breakPeriod));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -105,7 +105,7 @@ namespace osu.Game.Screens.Edit
|
|||||||
BeatmapSkin.BeatmapSkinChanged += SaveState;
|
BeatmapSkin.BeatmapSkinChanged += SaveState;
|
||||||
}
|
}
|
||||||
|
|
||||||
beatmapProcessor = playableBeatmap.BeatmapInfo.Ruleset.CreateInstance().CreateBeatmapProcessor(this);
|
beatmapProcessor = new EditorBeatmapProcessor(this, playableBeatmap.BeatmapInfo.Ruleset.CreateInstance());
|
||||||
|
|
||||||
foreach (var obj in HitObjects)
|
foreach (var obj in HitObjects)
|
||||||
trackStartTime(obj);
|
trackStartTime(obj);
|
||||||
@ -172,7 +172,7 @@ namespace osu.Game.Screens.Edit
|
|||||||
set => PlayableBeatmap.ControlPointInfo = value;
|
set => PlayableBeatmap.ControlPointInfo = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<BreakPeriod> Breaks => PlayableBeatmap.Breaks;
|
public BindableList<BreakPeriod> Breaks => PlayableBeatmap.Breaks;
|
||||||
|
|
||||||
public List<string> UnhandledEventLines => PlayableBeatmap.UnhandledEventLines;
|
public List<string> UnhandledEventLines => PlayableBeatmap.UnhandledEventLines;
|
||||||
|
|
||||||
@ -349,13 +349,13 @@ namespace osu.Game.Screens.Edit
|
|||||||
if (batchPendingUpdates.Count == 0 && batchPendingDeletes.Count == 0 && batchPendingInserts.Count == 0)
|
if (batchPendingUpdates.Count == 0 && batchPendingDeletes.Count == 0 && batchPendingInserts.Count == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
beatmapProcessor?.PreProcess();
|
beatmapProcessor.PreProcess();
|
||||||
|
|
||||||
foreach (var h in batchPendingDeletes) processHitObject(h);
|
foreach (var h in batchPendingDeletes) processHitObject(h);
|
||||||
foreach (var h in batchPendingInserts) processHitObject(h);
|
foreach (var h in batchPendingInserts) processHitObject(h);
|
||||||
foreach (var h in batchPendingUpdates) processHitObject(h);
|
foreach (var h in batchPendingUpdates) processHitObject(h);
|
||||||
|
|
||||||
beatmapProcessor?.PostProcess();
|
beatmapProcessor.PostProcess();
|
||||||
|
|
||||||
BeatmapReprocessed?.Invoke();
|
BeatmapReprocessed?.Invoke();
|
||||||
|
|
||||||
|
70
osu.Game/Screens/Edit/EditorBeatmapProcessor.cs
Normal file
70
osu.Game/Screens/Edit/EditorBeatmapProcessor.cs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.Timing;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Edit
|
||||||
|
{
|
||||||
|
public class EditorBeatmapProcessor : IBeatmapProcessor
|
||||||
|
{
|
||||||
|
public IBeatmap Beatmap { get; }
|
||||||
|
|
||||||
|
private readonly IBeatmapProcessor? rulesetBeatmapProcessor;
|
||||||
|
|
||||||
|
public EditorBeatmapProcessor(IBeatmap beatmap, Ruleset ruleset)
|
||||||
|
{
|
||||||
|
Beatmap = beatmap;
|
||||||
|
rulesetBeatmapProcessor = ruleset.CreateBeatmapProcessor(beatmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PreProcess()
|
||||||
|
{
|
||||||
|
rulesetBeatmapProcessor?.PreProcess();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PostProcess()
|
||||||
|
{
|
||||||
|
rulesetBeatmapProcessor?.PostProcess();
|
||||||
|
|
||||||
|
autoGenerateBreaks();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void autoGenerateBreaks()
|
||||||
|
{
|
||||||
|
Beatmap.Breaks.RemoveAll(b => b is not ManualBreakPeriod);
|
||||||
|
|
||||||
|
foreach (var manualBreak in Beatmap.Breaks.ToList())
|
||||||
|
{
|
||||||
|
if (Beatmap.HitObjects.Any(ho => ho.StartTime <= manualBreak.EndTime && ho.GetEndTime() >= manualBreak.StartTime))
|
||||||
|
Beatmap.Breaks.Remove(manualBreak);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 1; i < Beatmap.HitObjects.Count; ++i)
|
||||||
|
{
|
||||||
|
double previousObjectEndTime = Beatmap.HitObjects[i - 1].GetEndTime();
|
||||||
|
double nextObjectStartTime = Beatmap.HitObjects[i].StartTime;
|
||||||
|
|
||||||
|
if (nextObjectStartTime - previousObjectEndTime < BreakPeriod.MIN_GAP_DURATION)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
double breakStartTime = previousObjectEndTime + BreakPeriod.GAP_BEFORE_BREAK;
|
||||||
|
double breakEndTime = nextObjectStartTime - Math.Max(BreakPeriod.GAP_AFTER_BREAK, Beatmap.ControlPointInfo.TimingPointAt(nextObjectStartTime).BeatLength * 2);
|
||||||
|
|
||||||
|
if (breakEndTime - breakStartTime < BreakPeriod.MIN_BREAK_DURATION)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var breakPeriod = new BreakPeriod(breakStartTime, breakEndTime);
|
||||||
|
|
||||||
|
if (Beatmap.Breaks.Any(b => b.Intersects(breakPeriod)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Beatmap.Breaks.Add(breakPeriod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -45,6 +45,7 @@ namespace osu.Game.Screens.Edit
|
|||||||
editorBeatmap.BeginChange();
|
editorBeatmap.BeginChange();
|
||||||
processHitObjects(result, () => newBeatmap ??= readBeatmap(newState));
|
processHitObjects(result, () => newBeatmap ??= readBeatmap(newState));
|
||||||
processTimingPoints(() => newBeatmap ??= readBeatmap(newState));
|
processTimingPoints(() => newBeatmap ??= readBeatmap(newState));
|
||||||
|
processBreaks(() => newBeatmap ??= readBeatmap(newState));
|
||||||
processHitObjectLocalData(() => newBeatmap ??= readBeatmap(newState));
|
processHitObjectLocalData(() => newBeatmap ??= readBeatmap(newState));
|
||||||
editorBeatmap.EndChange();
|
editorBeatmap.EndChange();
|
||||||
}
|
}
|
||||||
@ -75,6 +76,27 @@ namespace osu.Game.Screens.Edit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void processBreaks(Func<IBeatmap> getNewBeatmap)
|
||||||
|
{
|
||||||
|
var newBreaks = getNewBeatmap().Breaks.ToArray();
|
||||||
|
|
||||||
|
foreach (var oldBreak in editorBeatmap.Breaks.ToArray())
|
||||||
|
{
|
||||||
|
if (newBreaks.Any(b => b.Equals(oldBreak)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
editorBeatmap.Breaks.Remove(oldBreak);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var newBreak in newBreaks)
|
||||||
|
{
|
||||||
|
if (editorBeatmap.Breaks.Any(b => b.Equals(newBreak)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
editorBeatmap.Breaks.Add(newBreak);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void processHitObjects(DiffResult result, Func<IBeatmap> getNewBeatmap)
|
private void processHitObjects(DiffResult result, Func<IBeatmap> getNewBeatmap)
|
||||||
{
|
{
|
||||||
findChangedIndices(result, LegacyDecoder<Beatmap>.Section.HitObjects, out var removedIndices, out var addedIndices);
|
findChangedIndices(result, LegacyDecoder<Beatmap>.Section.HitObjects, out var removedIndices, out var addedIndices);
|
||||||
|
15
osu.Game/Screens/Edit/ManualBreakPeriod.cs
Normal file
15
osu.Game/Screens/Edit/ManualBreakPeriod.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Game.Beatmaps.Timing;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Edit
|
||||||
|
{
|
||||||
|
public class ManualBreakPeriod : BreakPeriod
|
||||||
|
{
|
||||||
|
public ManualBreakPeriod(double startTime, double endTime)
|
||||||
|
: base(startTime, endTime)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -26,11 +26,13 @@ namespace osu.Game.Tests.Beatmaps
|
|||||||
|
|
||||||
BeatmapInfo = baseBeatmap.BeatmapInfo;
|
BeatmapInfo = baseBeatmap.BeatmapInfo;
|
||||||
ControlPointInfo = baseBeatmap.ControlPointInfo;
|
ControlPointInfo = baseBeatmap.ControlPointInfo;
|
||||||
Breaks = baseBeatmap.Breaks;
|
|
||||||
UnhandledEventLines = baseBeatmap.UnhandledEventLines;
|
UnhandledEventLines = baseBeatmap.UnhandledEventLines;
|
||||||
|
|
||||||
if (withHitObjects)
|
if (withHitObjects)
|
||||||
|
{
|
||||||
HitObjects = baseBeatmap.HitObjects;
|
HitObjects = baseBeatmap.HitObjects;
|
||||||
|
Breaks = baseBeatmap.Breaks;
|
||||||
|
}
|
||||||
|
|
||||||
BeatmapInfo.Ruleset = ruleset;
|
BeatmapInfo.Ruleset = ruleset;
|
||||||
BeatmapInfo.Length = 75000;
|
BeatmapInfo.Length = 75000;
|
||||||
|
Loading…
Reference in New Issue
Block a user